@@ -177,21 +177,29 @@ class SolveInputs:
177177 problem_outline : ProblemOutline
178178 """Target model metadata"""
179179
180- raw_parameters : list [Any ]
180+ raw_parameters : list [Json ] = dataclasses . field ( repr = False )
181181 """All parameters in raw format"""
182182
183- raw_dimensions : list [Any ] | None
183+ raw_dimensions : list [Json ] | None = dataclasses . field ( repr = False )
184184 """All dimensions in raw format"""
185185
186- def parameter (self , label : Label ) -> pd .Series :
187- """Returns the parameter for a given label as a pandas Series"""
186+ def parameter (self , label : Label , coerce : bool = True ) -> pd .DataFrame :
187+ """Returns the parameter for a given label as a pandas DataFrame
188+
189+ The returned dataframe has a `value` column with the parameter's values
190+ (0 values may be omitted).
191+
192+ Args:
193+ label: Parameter label to retrieve
194+ coerce: Round integral parameters
195+ """
188196 for param in self .raw_parameters :
189197 if param ["label" ] == label :
190- entries = param ["entries" ]
191198 outline = self .problem_outline .parameters [label ]
192- return pd .Series (
193- data = (e ["value" ] for e in entries ),
194- index = _entry_index (entries , outline .bindings ),
199+ return _entries_dataframe (
200+ param ["entries" ],
201+ outline .bindings ,
202+ round_values = coerce and outline .is_integral ,
195203 )
196204 raise Exception (f"Unknown parameter: { label } " )
197205
@@ -210,10 +218,10 @@ class SolveOutputs:
210218 problem_outline : ProblemOutline
211219 """Solved model metadata"""
212220
213- raw_variables : list [Any ]
221+ raw_variables : list [Json ] = dataclasses . field ( repr = False )
214222 """All variables in raw format"""
215223
216- raw_constraints : list [Any ]
224+ raw_constraints : list [Json ] = dataclasses . field ( repr = False )
217225 """All constraints in raw format"""
218226
219227 def variable (self , label : Label , coerce : bool = True ) -> pd .DataFrame :
@@ -230,10 +238,9 @@ def variable(self, label: Label, coerce: bool = True) -> pd.DataFrame:
230238 for res in self .raw_variables :
231239 if res ["label" ] == label :
232240 outline = self .problem_outline .variables [label ]
233- return _output_dataframe (
241+ return _entries_dataframe (
234242 res ["entries" ],
235243 outline .bindings ,
236- value_name = "value" ,
237244 dual_value_name = "reduced_cost" ,
238245 round_values = coerce and outline .is_integral ,
239246 )
@@ -248,7 +255,7 @@ def constraint(self, label: Label) -> pd.DataFrame:
248255 """
249256 for res in self .raw_constraints :
250257 if res ["label" ] == label :
251- return _output_dataframe (
258+ return _entries_dataframe (
252259 res ["entries" ],
253260 self .problem_outline .constraints [label ].bindings ,
254261 value_name = "slack" ,
@@ -257,27 +264,34 @@ def constraint(self, label: Label) -> pd.DataFrame:
257264 raise Exception (f"Unknown constraint { label } " )
258265
259266
260- def _output_dataframe (
267+ def _entries_dataframe (
261268 entries : Sequence [Json ],
262269 bindings : Sequence [SourceBinding ],
263270 * ,
264- value_name : str ,
265- dual_value_name : str ,
271+ value_name : str = "value" ,
272+ dual_value_name : str | None = None ,
266273 round_values : bool = False ,
267274) -> pd .DataFrame :
268- df = pd . DataFrame (
269- data = (
275+ if dual_value_name :
276+ data = (
270277 (decode_extended_float (e ["value" ]), e .get ("dualValue" ))
271278 for e in entries
272- ),
273- columns = [value_name , dual_value_name ],
279+ )
280+ columns = [value_name , dual_value_name ]
281+ else :
282+ data = (decode_extended_float (e ["value" ]) for e in entries )
283+ columns = [value_name ]
284+ df = pd .DataFrame (
285+ data = data ,
286+ columns = columns ,
274287 index = _entry_index (entries , bindings ),
275288 )
276- if df [dual_value_name ].isnull ().all ():
289+ if dual_value_name and df [dual_value_name ].isnull ().all ():
277290 df .drop (dual_value_name , axis = 1 , inplace = True )
278291 if round_values :
279292 df [value_name ] = df [value_name ].round (0 ).astype (np .int64 )
280293 df .fillna (0 , inplace = True )
294+ df .sort_index (inplace = True )
281295 return df
282296
283297
@@ -302,6 +316,9 @@ class Solution:
302316 problem_summary : ProblemSummary
303317 """Problem summary statistics"""
304318
319+ inputs : SolveInputs = dataclasses .field (repr = False )
320+ """Problem inputs"""
321+
305322 outputs : SolveOutputs | None = dataclasses .field (default = None , repr = False )
306323 """Solution data, present iff the solution is feasible"""
307324
@@ -313,6 +330,7 @@ def feasible(self) -> bool:
313330
314331def solution_from_json (
315332 outline : ProblemOutline ,
333+ inputs : SolveInputs ,
316334 response_json : Any ,
317335 problem_summary : ProblemSummary | None = None ,
318336) -> Solution :
@@ -341,6 +359,7 @@ def solution_from_json(
341359 outcome = outcome ,
342360 problem_summary = problem_summary
343361 or problem_summary_from_json (response_json ["summaries" ]["problem" ]),
362+ inputs = inputs ,
344363 outputs = outputs ,
345364 )
346365
@@ -452,9 +471,7 @@ class SolveStrategy:
452471 @classmethod
453472 def equally_weighted_sum (cls , sense : ObjectiveSense | None = None ) -> Self :
454473 """Returns a strategy optimizing the sum of all objectives"""
455- return cls (
456- target = collections .defaultdict (lambda : 1 ), sense = sense
457- )
474+ return cls (target = collections .defaultdict (lambda : 1 ), sense = sense )
458475
459476
460477def solve_strategy_to_json (
0 commit comments