gpkit: Auto-breakdowns

A feature I discussed with @mtwichan.

Anecdotally, a lot of GP(kit) constraints take the form y > x1 + x2 + x3 + ... + xn (e.g. mass rollup, cost rollup, energy rollup etc.). What’s more, those x1, x2 etc. are often themselves constrained by rollups.

I often want to see/understand/plot/fetch the values of the constituent parts that make up y, i.e. the values of x1...n.

To do this in a semi-automatic way, I currently access the (known) constraint’s varkeys, with something like:

mass_rollup = constraints[4]
masses = mass_rollup.right.varkeys
some_plotting_function(sol, masses)

However, given that the pattern is relatively prevalent and that it would be nice to be able to visualize any arbitrary rollup, I think it could be useful (and not too tricky) to have a native breakdown generator built into GPkit that takes a model (or a solution!) and a variable (in this case y) as an input and returns the rollup.

I think there are ~three~ four possible tiers of functionality, maybe implementation stepping stones.

Bronze

Returns a list of varkeys given an input varkey

# for the constraint y >= x1 + x2 + x3
m.breakdown_search("y") --> [x1, x2, x3]

Silver

Returns a dict of lists given an input varkey

# y >= x1 + x2 + x3
# x1 >= v1 + v2
# x2 >= v3
# x3 >= b*t**3 + a*t**2
m.breakdown_search("y") --> {x1: [v1, v2], x2: [v3], x3: x3}

Gold

Returns a nested dict of dicts for a SolutionArray given an input varkey. This is particularly appealing because I often want to dig into a solution without needing to re-generate the model object from which to derive the rollup (which breaks if the model has rollup has diverged from the rollup used to generate the solution).

# y >= x1 + x2 + x3
# x1 >= v1 + v2           # v2 has optimal value 5
# v1 >= u2 + u2           # u1 has optimal value 1, u2 has optimal value 2
# x2 >= v3 + v4           # v3 has optimal value 2, v4 has optimal value 1
# x3 >= b*t**3 + a*t**2   # x3 has optimal value 10
sol.breakdown("y") --> {x1: {v1: {u1: 1, u2: 2}, v2: 5},
                        x2: {v3: 2, v4: 1},
                        x3: 10}

Platinum

Returns a JSON string for creating an icicle plot (the D3 syntax is just one option – I’m biased because it’s the one I use)

# y >= x1 + x2 + x3
# x1 >= v1 + v2           # v2 has optimal value 5
# v1 >= u2 + u2           # u1 has optimal value 1, u2 has optimal value 2
# x2 >= v3 + v4           # v3 has optimal value 2, v4 has optimal value 1
# x3 >= b*t**3 + a*t**2   # x3 has optimal value 10
sol.breakdown("y", units="MUSD") -->
"""
{"name": "y",                                                                    
 "children": [                                                                   
  {"name": "x1",                                                                 
   "children": [                                                                 
    {"name": "v1":                                                               
     "children": [                                                               
      {"name": "u1",                                                             
       "size": 1,                                                                
       "label": "1 MUSD",                                                        
      },                                                                         
      {"name": "u2",                                                             
       "size": 2,                                                                
       "label": "0.002 MUSD",                                                    
      },                                                                         
    },                                                                           
    {"name": v2"                                                                 
     "size": 5,                                                                  
     "label": "5 MUSD",                                                          
    },                                                                           
   ]                                                                             
  },                                                                             
  {"name": x2",                                                                  
   "children": [                                                                 
    {"name": "v3",                                                               
     "size": 2,                                                                  
     "label": "2 MUSD",                                                          
    },                                                                           
    {"name": v4,                                                                 
     "size": 1,                                                                  
     "label": "1 MUSD",                                                          
    },                                                                           
   ],                                                                            
  },                                                                             
  {"name": "x3",                                                                 
   "size": 10,                                                                   
   "label": "0.01 MUSD",                                                         
  },                                                                             
 ]                                                                               
} # I'm sure I made some mistake but you get the point
"""

@bqpd what do you think about these various proposals?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 16 (15 by maintainers)

Most upvoted comments

yup, nesting would still be implicit! Breakdown is a great name.

related to #507, #522

@bqpd while I definitely don’t want to burden you, I was more really talking about model build times and performance! 😃

  1. My preference is to not create a different type of constraint set. As you know, I like that GPkit models can look pretty close to lists of pure engineering relationships with as little extra stuff (python wizardry) as necessary (I already have to tell people to ignore the Tight(), Loose() constructs). I think this is a big part of making large models seem less scary.

  2. I would lean towards the former but are those mutually exclusive? The latter would be helpful even if it only allowed one breakdown.

@1ozturkbe Yes good point. This could be an argument for only allowing breakdowns of c=1, exp=1? Alternatively, something that checks whether the variable has already been… “broken down”.