Skip to content

Commit 7ab4028

Browse files
committed
Merge branch 'main' into dispatcher-simplification
2 parents 8730e17 + cd9d6ce commit 7ab4028

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1153
-1004
lines changed

doc/OnlineDocs/src/expr/managing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def clone_expression(expr):
181181
# x[0] + 5*x[1]
182182
print(str(ce))
183183
# x[0] + 5*x[1]
184-
print(e.arg(0) is not ce.arg(0))
184+
print(e.arg(0) is ce.arg(0))
185185
# True
186186
print(e.arg(1) is not ce.arg(1))
187187
# True

pyomo/contrib/appsi/cmodel/src/expression.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,7 +1548,10 @@ appsi_operator_from_pyomo_expr(py::handle expr, py::handle var_map,
15481548
break;
15491549
}
15501550
case param: {
1551-
res = param_map[expr_types.id(expr)].cast<std::shared_ptr<Node>>();
1551+
if (expr.attr("parent_component")().attr("mutable").cast<bool>())
1552+
res = param_map[expr_types.id(expr)].cast<std::shared_ptr<Node>>();
1553+
else
1554+
res = std::make_shared<Constant>(expr.attr("value").cast<double>());
15521555
break;
15531556
}
15541557
case product: {
@@ -1789,7 +1792,8 @@ int build_expression_tree(py::handle pyomo_expr,
17891792

17901793
if (expr_types.expr_type_map[py::type::of(pyomo_expr)].cast<ExprType>() ==
17911794
named_expr)
1792-
pyomo_expr = pyomo_expr.attr("expr");
1795+
return build_expression_tree(pyomo_expr.attr("expr"), appsi_expr, var_map,
1796+
param_map, expr_types);
17931797

17941798
if (appsi_expr->is_leaf()) {
17951799
;

pyomo/contrib/appsi/solvers/ipopt.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ def __init__(self, only_child_vars=False):
147147
self._primal_sol = ComponentMap()
148148
self._reduced_costs = ComponentMap()
149149
self._last_results_object: Optional[Results] = None
150+
self._version_timeout = 2
150151

151152
def available(self):
152153
if self.config.executable.path() is None:
@@ -158,7 +159,7 @@ def available(self):
158159
def version(self):
159160
results = subprocess.run(
160161
[str(self.config.executable), '--version'],
161-
timeout=1,
162+
timeout=self._version_timeout,
162163
stdout=subprocess.PIPE,
163164
stderr=subprocess.STDOUT,
164165
universal_newlines=True,

pyomo/contrib/appsi/solvers/tests/test_persistent_solvers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,27 @@ def test_bounds_with_params(
918918
res = opt.solve(m)
919919
self.assertAlmostEqual(m.y.value, 3)
920920

921+
@parameterized.expand(input=_load_tests(all_solvers, only_child_vars_options))
922+
def test_bounds_with_immutable_params(
923+
self, name: str, opt_class: Type[PersistentSolver], only_child_vars
924+
):
925+
# this test is for issue #2574
926+
opt: PersistentSolver = opt_class(only_child_vars=only_child_vars)
927+
if not opt.available():
928+
raise unittest.SkipTest
929+
m = pe.ConcreteModel()
930+
m.p = pe.Param(mutable=False, initialize=1)
931+
m.q = pe.Param([1, 2], mutable=False, initialize=10)
932+
m.y = pe.Var()
933+
m.y.setlb(m.p)
934+
m.y.setub(m.q[1])
935+
m.obj = pe.Objective(expr=m.y)
936+
res = opt.solve(m)
937+
self.assertAlmostEqual(m.y.value, 1)
938+
m.y.setlb(m.q[2])
939+
res = opt.solve(m)
940+
self.assertAlmostEqual(m.y.value, 10)
941+
921942
@parameterized.expand(input=_load_tests(all_solvers, only_child_vars_options))
922943
def test_solution_loader(
923944
self, name: str, opt_class: Type[PersistentSolver], only_child_vars

pyomo/contrib/appsi/tests/test_fbbt.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,16 @@ def test_named_exprs(self):
151151
for x in m.x.values():
152152
self.assertAlmostEqual(x.lb, 0)
153153
self.assertAlmostEqual(x.ub, 0)
154+
155+
def test_named_exprs_nest(self):
156+
# test for issue #3184
157+
m = pe.ConcreteModel()
158+
m.x = pe.Var()
159+
m.e = pe.Expression(expr=m.x + 1)
160+
m.f = pe.Expression(expr=m.e)
161+
m.c = pe.Constraint(expr=(0, m.f, 0))
162+
it = appsi.fbbt.IntervalTightener()
163+
it.perform_fbbt(m)
164+
for x in m.x.values():
165+
self.assertAlmostEqual(x.lb, -1)
166+
self.assertAlmostEqual(x.ub, -1)

pyomo/contrib/incidence_analysis/scc_solver.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
IncidenceGraphInterface,
1919
_generate_variables_in_constraints,
2020
)
21+
from pyomo.contrib.incidence_analysis.config import IncidenceMethod
2122

2223

2324
_log = logging.getLogger(__name__)
2425

2526

2627
def generate_strongly_connected_components(
27-
constraints, variables=None, include_fixed=False
28+
constraints, variables=None, include_fixed=False, igraph=None
2829
):
2930
"""Yield in order ``_BlockData`` that each contain the variables and
3031
constraints of a single diagonal block in a block lower triangularization
@@ -41,9 +42,12 @@ def generate_strongly_connected_components(
4142
variables: List of Pyomo variable data objects
4243
Variables that may participate in strongly connected components.
4344
If not provided, all variables in the constraints will be used.
44-
include_fixed: Bool
45+
include_fixed: Bool, optional
4546
Indicates whether fixed variables will be included when
4647
identifying variables in constraints.
48+
igraph: IncidenceGraphInterface, optional
49+
Incidence graph containing (at least) the provided constraints
50+
and variables.
4751
4852
Yields
4953
------
@@ -55,11 +59,17 @@ def generate_strongly_connected_components(
5559
"""
5660
if variables is None:
5761
variables = list(
58-
_generate_variables_in_constraints(constraints, include_fixed=include_fixed)
62+
_generate_variables_in_constraints(
63+
constraints,
64+
include_fixed=include_fixed,
65+
method=IncidenceMethod.ampl_repn,
66+
)
5967
)
6068

6169
assert len(variables) == len(constraints)
62-
igraph = IncidenceGraphInterface()
70+
if igraph is None:
71+
igraph = IncidenceGraphInterface()
72+
6373
var_blocks, con_blocks = igraph.block_triangularize(
6474
variables=variables, constraints=constraints
6575
)
@@ -73,7 +83,7 @@ def generate_strongly_connected_components(
7383

7484

7585
def solve_strongly_connected_components(
76-
block, solver=None, solve_kwds=None, calc_var_kwds=None
86+
block, *, solver=None, solve_kwds=None, use_calc_var=True, calc_var_kwds=None
7787
):
7888
"""Solve a square system of variables and equality constraints by
7989
solving strongly connected components individually.
@@ -98,6 +108,9 @@ def solve_strongly_connected_components(
98108
a solve method.
99109
solve_kwds: Dictionary
100110
Keyword arguments for the solver's solve method
111+
use_calc_var: Bool
112+
Whether to use ``calculate_variable_from_constraint`` for one-by-one
113+
square system solves
101114
calc_var_kwds: Dictionary
102115
Keyword arguments for calculate_variable_from_constraint
103116
@@ -112,23 +125,28 @@ def solve_strongly_connected_components(
112125
calc_var_kwds = {}
113126

114127
igraph = IncidenceGraphInterface(
115-
block, active=True, include_fixed=False, include_inequality=False
128+
block,
129+
active=True,
130+
include_fixed=False,
131+
include_inequality=False,
132+
method=IncidenceMethod.ampl_repn,
116133
)
117134
constraints = igraph.constraints
118135
variables = igraph.variables
119136

120137
res_list = []
121138
log_blocks = _log.isEnabledFor(logging.DEBUG)
122-
for scc, inputs in generate_strongly_connected_components(constraints, variables):
123-
with TemporarySubsystemManager(to_fix=inputs):
139+
for scc, inputs in generate_strongly_connected_components(
140+
constraints, variables, igraph=igraph
141+
):
142+
with TemporarySubsystemManager(to_fix=inputs, remove_bounds_on_fix=True):
124143
N = len(scc.vars)
125-
if N == 1:
144+
if N == 1 and use_calc_var:
126145
if log_blocks:
127146
_log.debug(f"Solving 1x1 block: {scc.cons[0].name}.")
128147
results = calculate_variable_from_constraint(
129148
scc.vars[0], scc.cons[0], **calc_var_kwds
130149
)
131-
res_list.append(results)
132150
else:
133151
if solver is None:
134152
var_names = [var.name for var in scc.vars.values()][:10]
@@ -142,5 +160,5 @@ def solve_strongly_connected_components(
142160
if log_blocks:
143161
_log.debug(f"Solving {N}x{N} block.")
144162
results = solver.solve(scc, **solve_kwds)
145-
res_list.append(results)
163+
res_list.append(results)
146164
return res_list

0 commit comments

Comments
 (0)