Skip to content

Adjust PyROS handling of separation objective evaluation errors #3646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions pyomo/contrib/pyros/separation_problem_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,27 +193,23 @@ def get_sep_objective_values(separation_data, ss_ineq_cons):
con_to_obj_map = separation_data.separation_model.second_stage_ineq_con_to_obj_map
violations = ComponentMap()

user_var_partitioning = separation_data.separation_model.user_var_partitioning
first_stage_variables = user_var_partitioning.first_stage_variables
second_stage_variables = user_var_partitioning.second_stage_variables

for ss_ineq_con in ss_ineq_cons:
obj = con_to_obj_map[ss_ineq_con]
try:
violations[ss_ineq_con] = value(obj.expr)
except ValueError:
for v in first_stage_variables:
config.progress_logger.info(v.name + " " + str(v.value))
for v in second_stage_variables:
config.progress_logger.info(v.name + " " + str(v.value))
raise ArithmeticError(
f"Evaluation of second-stage inequality constraint {ss_ineq_con.name} "
f"(separation objective {obj.name}) "
"led to a math domain error. "
"Does the constraint expression "
"contain log(x) or 1/x functions "
except (ValueError, ArithmeticError):
vars_in_expr_str = ",\n ".join(
f"{var.name}={var.value}" for var in identify_variables(obj.expr)
)
config.progress_logger.error(
"PyROS encountered an exception evaluating "
"expression of second-stage inequality constraint with name "
f"{ss_ineq_con.name!r} (separation objective {obj.name!r}) "
f"at variable values:\n {vars_in_expr_str}\n"
"Does the expression contain log(x) or 1/x functions "
"or others with tricky domains?"
)
raise

return violations

Expand Down
38 changes: 28 additions & 10 deletions pyomo/contrib/pyros/tests/test_grcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,9 +1242,8 @@ def test_pyros_nl_and_ampl_writer_tol(self):
)
def test_pyros_math_domain_error(self):
"""
Test PyROS on a two-stage problem, discrete
set type with a math domain error evaluating
second-stage inequality constraint expressions in separation.
Test PyROS behavior is as expected when there are errors
encountered while evaluating separation problem objectives.
"""
m = ConcreteModel()
m.q = Param(initialize=1, mutable=True)
Expand All @@ -1259,16 +1258,35 @@ def test_pyros_math_domain_error(self):
pyros_solver = SolverFactory("pyros")

with self.assertRaisesRegex(
expected_exception=ArithmeticError,
expected_regex=(
"Evaluation of second-stage inequality constraint.*math domain error.*"
),
msg="ValueError arising from math domain error not raised",
expected_exception=ValueError,
expected_regex="math domain error",
msg="Exception arising from math domain error not raised",
):
# should raise math domain error:
# (1) lower bounding constraint on x2 solved first
# in separation, q = 0 in worst case
# (2) now tries to evaluate log(q), but q = 0
# in separation. Solution has q = 0
# (2) upon solution of the first separation problem,
# evaluation of x2 - log(q) at q = 0
# results in exception
pyros_solver.solve(
model=m,
first_stage_variables=[m.x1],
second_stage_variables=[m.x2],
uncertain_params=[m.q],
uncertainty_set=box_set,
local_solver=local_solver,
global_solver=global_solver,
decision_rule_order=1,
tee=True,
)

# this should result in error stemming from division by zero
m.x2.setub(1 / m.q)
with self.assertRaisesRegex(
expected_exception=ZeroDivisionError,
expected_regex="float division by zero",
msg="Exception arising from math domain error not raised",
):
pyros_solver.solve(
model=m,
first_stage_variables=[m.x1],
Expand Down
Loading