Skip to content

Commit bb9fcde

Browse files
authored
Merge branch 'main' into main
2 parents 0bf3edd + 7deb912 commit bb9fcde

File tree

4 files changed

+78
-10
lines changed

4 files changed

+78
-10
lines changed

pyomo/contrib/sensitivity_toolbox/pynumero.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# Use attempt_import here due to unguarded NumPy import in this file
1818
nlp = attempt_import('pyomo.contrib.pynumero.interfaces.pyomo_nlp')[0]
1919
from pyomo.common.collections import ComponentSet, ComponentMap
20+
import pyomo.core.expr.calculus.derivatives as derivatives
2021

2122

2223
def _coo_reorder_cols(mat, remap):
@@ -111,28 +112,56 @@ def get_dsdp_dfdp(model, theta):
111112
return dsdp, dfdp, row_map, column_map
112113

113114

114-
def get_dydp(y_list, dsdp, row_map):
115+
def get_dydp(y_list, dsdp, row_map, column_map=None):
115116
"""Reduce the sensitivity matrix from get_dsdp_dfdp to only
116117
a specified set of state variables of interest.
117118
118119
Parameters
119120
----------
120121
y_list: list
121-
A list of state variables of interest (a subset of s)
122+
A list of state variables or named expressions
122123
dsdp: csc_matrix
123124
A sensitivity matrix calculated by get_dsdp_dfdp
124125
row_map: ComponentMap
125126
A row map from get_dsdp_dfdp
127+
column_map: ComponentMap
128+
A column map from get_dsdp_dfdp, only needed if y_list
129+
contains expressions
126130
127131
Returns
128132
-------
129133
csc_matrix, ComponentMap
130134
dy/dp and a new row map with only y variables
131135
132136
"""
137+
j = 0
133138
new_row_map = ComponentMap()
139+
expr_row_map = ComponentMap()
140+
rows = [None] * len(y_list)
134141
for i, v in enumerate(y_list):
135142
new_row_map[v] = i
136-
rows = [row_map[v] for v in y_list]
137-
dydp = dsdp[rows, :]
143+
if v not in row_map:
144+
expr_row_map[v] = j
145+
j += 1
146+
else:
147+
rows[i] = dsdp[row_map[v], :]
148+
149+
if j > 0:
150+
if column_map is None:
151+
raise ValueError(
152+
"A column_map must be provided if y_list contains named expressions."
153+
)
154+
wrt_list = [s for s in row_map]
155+
dedx = [None] * j
156+
for v, i in expr_row_map.items():
157+
dedx[i] = scipy.sparse.csc_matrix(
158+
derivatives.differentiate(v, wrt_list=wrt_list)
159+
)
160+
dedx = scipy.sparse.vstack(dedx)
161+
ns = len(row_map) - len(column_map)
162+
dedp = dedx[:, :ns] @ dsdp[:ns, :] + dedx[:, ns:]
163+
for v, i in expr_row_map.items():
164+
rows[new_row_map[v]] = dedp[i, :]
165+
166+
dydp = scipy.sparse.vstack(rows)
138167
return dydp, new_row_map

pyomo/contrib/sensitivity_toolbox/tests/test_pynumero.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import pyomo.environ as pyo
1818

1919
# Use attempt_import here due to unguarded NumPy import in this file
20-
nlp = attempt_import('pyomo.contrib.pynumero.interfaces.pyomo_nlp')[0]
20+
nlp = attempt_import("pyomo.contrib.pynumero.interfaces.pyomo_nlp")[0]
2121
import pyomo.contrib.sensitivity_toolbox.pynumero as pnsens
2222
from pyomo.contrib.pynumero.asl import AmplInterface
2323

@@ -101,11 +101,31 @@ def test_dydp_pyomo(self):
101101
)
102102
m.c1 = pyo.Constraint(expr=m.x1 == 2 * m.p1**2)
103103
m.c2 = pyo.Constraint(expr=m.x2 == m.p2)
104+
105+
m.e1 = pyo.Expression(expr=2 * m.p1**2)
106+
m.e2 = pyo.Expression(expr=m.p2)
107+
m.e3 = pyo.Expression(expr=m.x1 * m.p1 + m.x2 * m.x2 * m.p2 + m.p1 * m.p2)
108+
104109
theta = [m.p1, m.p2]
105110
dsdp, dfdp, rmap, cmap = pnsens.get_dsdp_dfdp(m, theta)
106111

107-
dydp, rmap = pnsens.get_dydp([m.x2], dsdp, rmap)
112+
dydp, rmap = pnsens.get_dydp([m.e1, m.x1, m.e2, m.x2, m.e3], dsdp, rmap, cmap)
108113

114+
assert dydp.shape == (5, 2)
115+
np.testing.assert_almost_equal(dydp[rmap[m.x1], cmap[m.p1]], 40.0)
116+
np.testing.assert_almost_equal(dydp[rmap[m.e1], cmap[m.p1]], 40.0)
117+
np.testing.assert_almost_equal(dydp[rmap[m.x1], cmap[m.p2]], 0.0)
118+
np.testing.assert_almost_equal(dydp[rmap[m.e1], cmap[m.p2]], 0.0)
109119
np.testing.assert_almost_equal(dydp[rmap[m.x2], cmap[m.p1]], 0.0)
120+
np.testing.assert_almost_equal(dydp[rmap[m.e2], cmap[m.p1]], 0.0)
110121
np.testing.assert_almost_equal(dydp[rmap[m.x2], cmap[m.p2]], 1.0)
111-
assert dydp.shape == (1, 2)
122+
np.testing.assert_almost_equal(dydp[rmap[m.e2], cmap[m.p2]], 1.0)
123+
np.testing.assert_almost_equal(dydp[rmap[m.e3], cmap[m.p1]], 605.0)
124+
np.testing.assert_almost_equal(dydp[rmap[m.e3], cmap[m.p2]], 85.0)
125+
126+
# make sure the rows are in the order of y_list
127+
assert rmap[m.e1] == 0
128+
assert rmap[m.x1] == 1
129+
assert rmap[m.e2] == 2
130+
assert rmap[m.x2] == 3
131+
assert rmap[m.e3] == 4

pyomo/solvers/plugins/solvers/SCIPAMPL.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ def _postsolve(self):
255255

256256
results.solver.time = log_dict['solving_time']
257257
results.solver.gap = log_dict['gap']
258+
results.solver.node_count = log_dict['solving_nodes']
258259
results.solver.primal_bound = log_dict['primal_bound']
259260
results.solver.dual_bound = log_dict['dual_bound']
260261

pyomo/solvers/tests/mip/test_scip_log_data.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,29 @@ def test_scip_some_more():
198198
list_extra_data_expected = [
199199
(), # problem_lp_unbounded(),
200200
(), # problem_lp_infeasible(),
201-
('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_lp_optimal(),
201+
(
202+
'Time',
203+
'Gap',
204+
'Node count',
205+
'Primal bound',
206+
'Dual bound',
207+
), # problem_lp_optimal(),
202208
(), # problem_milp_unbounded(),
203209
(), # problem_milp_infeasible(),
204-
('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_milp_optimal(),
205-
('Time', 'Gap', 'Primal bound', 'Dual bound'), # problem_milp_feasible()
210+
(
211+
'Time',
212+
'Gap',
213+
'Node count',
214+
'Primal bound',
215+
'Dual bound',
216+
), # problem_milp_optimal(),
217+
(
218+
'Time',
219+
'Gap',
220+
'Node count',
221+
'Primal bound',
222+
'Dual bound',
223+
), # problem_milp_feasible()
206224
]
207225

208226
# **************************************************************************

0 commit comments

Comments
 (0)