Skip to content

Commit 5823583

Browse files
addressing michael's comments MutableQuadraticCoefficient
1 parent be02bba commit 5823583

File tree

2 files changed

+39
-44
lines changed

2 files changed

+39
-44
lines changed

pyomo/contrib/solver/solvers/highs.py

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -97,39 +97,36 @@ def update(self):
9797

9898

9999
class _MutableQuadraticCoefficient:
100-
def __init__(self, expr, row_idx, col_idx):
100+
def __init__(self, expr, v1_id, v2_id):
101101
self.expr = expr
102-
self.row_idx = row_idx
103-
self.col_idx = col_idx
102+
self.v1_id = v1_id
103+
self.v2_id = v2_id
104104

105105

106106
class _MutableObjective:
107-
def __init__(self, highs, constant, linear_coefs, quadratic_coefs):
107+
def __init__(
108+
self,
109+
highs,
110+
constant,
111+
linear_coefs,
112+
quadratic_coefs,
113+
pyomo_var_to_solver_var_map,
114+
):
108115
self.highs = highs
109116
self.constant = constant
110117
self.linear_coefs = linear_coefs
111118
self.quadratic_coefs = quadratic_coefs
112-
self.last_quadratic_coef_values = [value(i.expr) for i in self.quadratic_coefs]
119+
self._pyomo_var_to_solver_var_map = pyomo_var_to_solver_var_map
113120
# Store the quadratic coefficients in dictionary format
114-
self.quad_coef_dict = {}
115-
self._initialize_quad_coef_dict()
121+
self._initialize_quad_coef_dicts()
116122
# Flag to force first update of quadratic coefficients
117123
self._first_update = True
118124

119-
def _initialize_quad_coef_dict(self):
125+
def _initialize_quad_coef_dicts(self):
126+
self.quad_coef_dict = {}
120127
for coef in self.quadratic_coefs:
121-
v1_ndx = coef.row_idx
122-
v2_ndx = coef.col_idx
123-
# Ensure we're storing the lower triangular part
124-
row = max(v1_ndx, v2_ndx)
125-
col = min(v1_ndx, v2_ndx)
126-
127-
coef_val = value(coef.expr)
128-
# Adjust for diagonal elements
129-
if v1_ndx == v2_ndx:
130-
coef_val *= 2.0
131-
132-
self.quad_coef_dict[(row, col)] = coef_val
128+
self.quad_coef_dict[(coef.v1_id, coef.v2_id)] = value(coef.expr)
129+
self.previous_quad_coef_dict = self.quad_coef_dict.copy()
133130

134131
def update(self):
135132
"""
@@ -141,23 +138,13 @@ def update(self):
141138
for coef in self.linear_coefs:
142139
coef.update()
143140

144-
for ndx, coef in enumerate(self.quadratic_coefs):
141+
for coef in self.quadratic_coefs:
145142
current_val = value(coef.expr)
146-
if current_val != self.last_quadratic_coef_values[ndx]:
143+
previous_val = self.previous_quad_coef_dict.get((coef.v1_id, coef.v2_id))
144+
if previous_val is not None and current_val != previous_val:
147145
needs_quadratic_update = True
148-
149-
v1_ndx = coef.row_idx
150-
v2_ndx = coef.col_idx
151-
row = max(v1_ndx, v2_ndx)
152-
col = min(v1_ndx, v2_ndx)
153-
154-
# Adjust the diagonal to match Highs' expected format
155-
if v1_ndx == v2_ndx:
156-
current_val *= 2.0
157-
158-
self.quad_coef_dict[(row, col)] = current_val
159-
160-
self.last_quadratic_coef_values[ndx] = current_val
146+
self.quad_coef_dict[(coef.v1_id, coef.v2_id)] = current_val
147+
self.previous_quad_coef_dict[(coef.v1_id, coef.v2_id)] = current_val
161148

162149
# If anything changed, rebuild and pass the Hessian
163150
if needs_quadratic_update:
@@ -176,8 +163,20 @@ def _build_and_pass_hessian(self):
176163
hessian_index = []
177164
hessian_start = [0] * dim
178165

166+
quad_coef_idx_dict = {}
167+
for (v1_id, v2_id), coef in self.quad_coef_dict.items():
168+
v1_ndx = self._pyomo_var_to_solver_var_map[v1_id]
169+
v2_ndx = self._pyomo_var_to_solver_var_map[v2_id]
170+
# Ensure we're storing the lower triangular part
171+
row = max(v1_ndx, v2_ndx)
172+
col = min(v1_ndx, v2_ndx)
173+
# Adjust the diagonal to match Highs' expected format
174+
if v1_ndx == v2_ndx:
175+
coef *= 2.0
176+
quad_coef_idx_dict[(row, col)] = coef
177+
179178
sorted_entries = sorted(
180-
self.quad_coef_dict.items(), key=lambda x: (x[0][1], x[0][0])
179+
quad_coef_idx_dict.items(), key=lambda x: (x[0][1], x[0][0])
181180
)
182181

183182
last_col = -1
@@ -645,16 +644,11 @@ def _set_objective(self, obj):
645644

646645
if repn.quadratic_vars and len(repn.quadratic_vars) > 0:
647646
for ndx, (v1, v2) in enumerate(repn.quadratic_vars):
648-
v1_id = id(v1)
649-
v2_id = id(v2)
650-
v1_ndx = self._pyomo_var_to_solver_var_map[v1_id]
651-
v2_ndx = self._pyomo_var_to_solver_var_map[v2_id]
652-
653647
coef = repn.quadratic_coefs[ndx]
654648

655649
mutable_quadratic_coefficients.append(
656650
_MutableQuadraticCoefficient(
657-
expr=coef, row_idx=v1_ndx, col_idx=v2_ndx
651+
expr=coef, v1_id=id(v1), v2_id=id(v2)
658652
)
659653
)
660654

@@ -665,6 +659,7 @@ def _set_objective(self, obj):
665659
mutable_constant,
666660
mutable_linear_coefficients,
667661
mutable_quadratic_coefficients,
662+
self._pyomo_var_to_solver_var_map,
668663
)
669664
self._mutable_objective.update()
670665

pyomo/contrib/solver/tests/solvers/test_solvers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,10 +1167,10 @@ def test_mutable_quadratic_objective_qp(
11671167
self.assertAlmostEqual(m.x2.value, 1, places=4)
11681168
self.assertAlmostEqual(results.incumbent_objective, 2, 4)
11691169

1170-
del m.x1
1170+
# del m.x1
11711171
results = opt.solve(m)
11721172
self.assertAlmostEqual(m.x2.value, 1, places=4)
1173-
self.assertAlmostEqual(results.incumbent_objective, 1, 4)
1173+
self.assertAlmostEqual(results.incumbent_objective, 2, 4)
11741174

11751175
@parameterized.expand(input=_load_tests(all_solvers))
11761176
def test_fixed_vars(

0 commit comments

Comments
 (0)