1919 UnboundedOutcome ,
2020)
2121from .outlines import Label , ObjectiveSense , ProblemOutline , SourceBinding
22+ from .tensors import KeyItem
2223
2324
2425@dataclasses .dataclass (frozen = True )
@@ -177,37 +178,58 @@ class SolveInputs:
177178 problem_outline : ProblemOutline
178179 """Target model metadata"""
179180
180- raw_parameters : list [Json ] = dataclasses .field (repr = False )
181+ raw_parameters : Sequence [Json ] = dataclasses .field (repr = False )
181182 """All parameters in raw format"""
182183
183- raw_dimensions : list [Json ] | None = dataclasses .field (repr = False )
184+ raw_dimensions : Sequence [Json ] | None = dataclasses .field (repr = False )
184185 """All dimensions in raw format"""
185186
186- def parameter (self , label : Label , coerce : bool = True ) -> pd .DataFrame :
187- """Returns the parameter for a given label as a pandas DataFrame
187+ def parameter (
188+ self ,
189+ label : Label ,
190+ coerce : bool = True ,
191+ index : pd .Index | Sequence [KeyItem ] | None = None ,
192+ ) -> pd .DataFrame :
193+ """Returns the parameter for a given label as a pandas dataframe
188194
189195 The returned dataframe has a `value` column with the parameter's values
190196 (0 values may be omitted).
191197
192198 Args:
193199 label: Parameter label to retrieve
194200 coerce: Round integral parameters
201+ index: Returned dataframe index
195202 """
196203 for param in self .raw_parameters :
197204 if param ["label" ] == label :
198205 outline = self .problem_outline .parameters [label ]
199- return _entries_dataframe (
200- param [ "entries" ] ,
206+ return _tensor_json_dataframe (
207+ param ,
201208 outline .bindings ,
209+ index = index ,
202210 round_values = coerce and outline .is_integral ,
203211 )
204212 raise Exception (f"Unknown parameter: { label } " )
205213
206214 def dimension (self , label : Label ) -> pd .Index :
207215 """Returns the dimension for a given label as a pandas Index"""
208- for dim in self .raw_dimensions or []:
209- if dim ["label" ] == label :
210- return pd .Index (dim ["items" ])
216+ if self .raw_dimensions is not None :
217+ for dim in self .raw_dimensions :
218+ if dim ["label" ] == label :
219+ return pd .Index (dim ["items" ], name = label )
220+ else :
221+ items = set ()
222+ has_binding = False
223+ for param in self .raw_parameters :
224+ outline = self .problem_outline .parameters [param ["label" ]]
225+ for i , binding in enumerate (outline .bindings ):
226+ if binding .dimension_label != label :
227+ continue
228+ has_binding = True
229+ for entry in param ["entries" ]:
230+ items .add (entry ["key" ][i ])
231+ if has_binding :
232+ return pd .Index (items , name = label ).sort_values ()
211233 raise Exception (f"Unknown dimension: { label } " )
212234
213235
@@ -218,13 +240,18 @@ class SolveOutputs:
218240 problem_outline : ProblemOutline
219241 """Solved model metadata"""
220242
221- raw_variables : list [Json ] = dataclasses .field (repr = False )
243+ raw_variables : Sequence [Json ] = dataclasses .field (repr = False )
222244 """All variables in raw format"""
223245
224- raw_constraints : list [Json ] = dataclasses .field (repr = False )
246+ raw_constraints : Sequence [Json ] = dataclasses .field (repr = False )
225247 """All constraints in raw format"""
226248
227- def variable (self , label : Label , coerce : bool = True ) -> pd .DataFrame :
249+ def variable (
250+ self ,
251+ label : Label ,
252+ coerce : bool = True ,
253+ index : pd .Index | Sequence [KeyItem ] | None = None ,
254+ ) -> pd .DataFrame :
228255 """Returns variable results for a given label
229256
230257 The returned dataframe always has a `value` column with the variable's
@@ -234,14 +261,16 @@ def variable(self, label: Label, coerce: bool = True) -> pd.DataFrame:
234261 Args:
235262 label: Variable label to retrieve
236263 coerce: Round integral variables
264+ index: Returned dataframe index
237265 """
238266 for res in self .raw_variables :
239267 if res ["label" ] == label :
240268 outline = self .problem_outline .variables [label ]
241- return _entries_dataframe (
242- res [ "entries" ] ,
269+ return _tensor_json_dataframe (
270+ res ,
243271 outline .bindings ,
244272 dual_value_name = "reduced_cost" ,
273+ index = index ,
245274 round_values = coerce and outline .is_integral ,
246275 )
247276 raise Exception (f"Unknown variable { label } " )
@@ -255,29 +284,35 @@ def constraint(self, label: Label) -> pd.DataFrame:
255284 """
256285 for res in self .raw_constraints :
257286 if res ["label" ] == label :
258- return _entries_dataframe (
259- res [ "entries" ] ,
287+ return _tensor_json_dataframe (
288+ res ,
260289 self .problem_outline .constraints [label ].bindings ,
261290 value_name = "slack" ,
262291 dual_value_name = "shadow_price" ,
263292 )
264293 raise Exception (f"Unknown constraint { label } " )
265294
266295
267- def _entries_dataframe (
268- entries : Sequence [ Json ] ,
296+ def _tensor_json_dataframe (
297+ tensor_json : Json ,
269298 bindings : Sequence [SourceBinding ],
270299 * ,
271300 value_name : str = "value" ,
272301 dual_value_name : str | None = None ,
302+ index : pd .Index | Sequence [KeyItem ] | None = None ,
273303 round_values : bool = False ,
274304) -> pd .DataFrame :
305+ entries = tensor_json ["entries" ]
306+ default_values = {
307+ value_name : decode_extended_float (tensor_json .get ("defaultValue" , 0 )),
308+ }
275309 if dual_value_name :
276310 data = (
277311 (decode_extended_float (e ["value" ]), e .get ("dualValue" ))
278312 for e in entries
279313 )
280314 columns = [value_name , dual_value_name ]
315+ default_values [dual_value_name ] = 0
281316 else :
282317 data = (decode_extended_float (e ["value" ]) for e in entries )
283318 columns = [value_name ]
@@ -287,11 +322,11 @@ def _entries_dataframe(
287322 index = _entry_index (entries , bindings ),
288323 )
289324 if dual_value_name and df [dual_value_name ].isnull ().all ():
290- df .drop (dual_value_name , axis = 1 , inplace = True )
325+ df = df .drop (dual_value_name , axis = 1 )
326+ df = df .sort_index () if index is None else df .reindex (cast (Any , index ))
327+ df = df .fillna (default_values )
291328 if round_values :
292329 df [value_name ] = df [value_name ].round (0 ).astype (np .int64 )
293- df .fillna (0 , inplace = True )
294- df .sort_index (inplace = True )
295330 return df
296331
297332
@@ -463,7 +498,7 @@ class SolveStrategy:
463498 sense : ObjectiveSense | None = None
464499 """Optimization sense"""
465500
466- epsilon_constraints : list [EpsilonConstraint ] = dataclasses .field (
501+ epsilon_constraints : Sequence [EpsilonConstraint ] = dataclasses .field (
467502 default_factory = lambda : []
468503 )
469504 """All epsilon-constraints to apply"""
0 commit comments