|
20 | 20 | import cvxpy.settings as s |
21 | 21 | from cvxpy.error import SolverError |
22 | 22 | from cvxpy.reductions.solution import Solution, failure_solution |
| 23 | +from cvxpy.reductions.solvers import utilities |
23 | 24 | from cvxpy.reductions.solvers.conic_solvers.conic_solver import ( |
24 | 25 | ConicSolver, |
25 | 26 | dims_to_solver_dict, |
@@ -104,14 +105,26 @@ def invert(self, results, inverse_data): |
104 | 105 | if status in s.SOLUTION_PRESENT: |
105 | 106 | opt_val = results["info"].objective_function_value + inverse_data[s.OFFSET] |
106 | 107 | primal_vars = { |
| 108 | + # inverse_data[HIGHS.VAR_ID]: ... |
| 109 | + # I don't understand how the line below works, the other conif solvers have |
| 110 | + # something similar to the commented line above for the "key". |
107 | 111 | HIGHS.VAR_ID: intf.DEFAULT_INTF.const_to_matrix( |
108 | 112 | np.array(results["solution"].col_value) |
109 | 113 | ) |
110 | 114 | } |
111 | 115 | # add duals if not a MIP. |
112 | 116 | dual_vars = None |
113 | | - if not inverse_data["is_mip"]: |
114 | | - dual_vars = {HIGHS.DUAL_VAR_ID: -np.array(results["solution"].row_dual)} |
| 117 | + if not inverse_data['is_mip']: |
| 118 | + # The dual values are retrieved in the order that the |
| 119 | + # constraints were added in solve_via_data() below. We |
| 120 | + # must be careful to map them to inverse_data[EQ_CONSTR] |
| 121 | + # followed by inverse_data[NEQ_CONSTR] accordingly. |
| 122 | + y = -np.array(results["solution"].row_dual) |
| 123 | + dual_vars = utilities.get_dual_values( |
| 124 | + y, |
| 125 | + utilities.extract_dual_value, |
| 126 | + inverse_data[HIGHS.EQ_CONSTR] + inverse_data[HIGHS.NEQ_CONSTR]) |
| 127 | + |
115 | 128 | attr[s.NUM_ITERS] = ( |
116 | 129 | results["info"].ipm_iteration_count |
117 | 130 | + results["info"].crossover_iteration_count |
|
0 commit comments