Skip to content

Commit

Permalink
Improve errors raised by calculate_variable_from_constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
jsiirola committed Jul 19, 2023
1 parent e1ae3d1 commit 73f872b
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 8 deletions.
10 changes: 10 additions & 0 deletions pyomo/common/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ class InvalidValueError(PyomoException, ValueError):
pass


class IterationLimitError(PyomoException, RuntimeError):
"""A subclass of :py:class:`RuntimeError`, raised by an iterative method
when the iteration limit is reached.
TODO: currently solvers currently do not raise this exception, but
probably should (at least when non-normal termination conditions are
mapped to exceptions)
"""

class IntervalException(PyomoException, ValueError):
"""
Exception class used for errors in interval arithmetic.
Expand Down
9 changes: 6 additions & 3 deletions pyomo/util/calc_var_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# This software is distributed under the 3-clause BSD License.
# ___________________________________________________________________________

from pyomo.common.errors import IterationLimitError
from pyomo.core.expr.numvalue import native_numeric_types, value, is_fixed
from pyomo.core.expr.calculus.derivatives import differentiate
from pyomo.core.base.constraint import Constraint, _ConstraintData
Expand Down Expand Up @@ -212,7 +213,7 @@ def calculate_variable_from_constraint(
fp0 = value(expr_deriv)

if abs(value(fp0)) < 1e-12:
raise RuntimeError(
raise ValueError(
f"Initial value for variable '{variable}' results in a derivative "
f"value for constraint '{constraint}' that is very close to zero.\n"
"\tPlease provide a different initial guess."
Expand All @@ -223,7 +224,7 @@ def calculate_variable_from_constraint(
while abs(fk) > eps and iter_left:
iter_left -= 1
if not iter_left:
raise RuntimeError(
raise IterationLimitError(
f"Iteration limit (%s) reached solving for variable '{variable}' "
f"using constraint '{constraint}'; remaining residual = %s"
% (iterlim, value(expr))
Expand Down Expand Up @@ -251,6 +252,8 @@ def calculate_variable_from_constraint(
fpk = value(expr_deriv)

if abs(fpk) < 1e-12:
# TODO: should this raise a ValueError or a new
# DerivativeError (subclassing ArithmeticError)?
raise RuntimeError(
"Newton's method encountered a derivative of constraint "
f"'{constraint}' with respect to variable '{variable}' that was too "
Expand Down Expand Up @@ -288,7 +291,7 @@ def calculate_variable_from_constraint(
residual = value(expr, exception=False)
if residual is None or type(residual) is complex:
residual = "{function evaluation error}"
raise RuntimeError(
raise IterationLimitError(
f"Linesearch iteration limit reached solving for "
f"variable '{variable}' using constraint '{constraint}'; "
f"remaining residual = {residual}."
Expand Down
11 changes: 6 additions & 5 deletions pyomo/util/tests/test_calc_var_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import pyomo.common.unittest as unittest

from pyomo.common.errors import IterationLimitError
from pyomo.common.log import LoggingIntercept
from pyomo.environ import (
ConcreteModel,
Expand Down Expand Up @@ -152,7 +153,7 @@ def test_nonlinear(self):
for mode in all_diff_modes:
m.x.set_value(1.25) # set the initial value
with self.assertRaisesRegex(
RuntimeError, r'Iteration limit \(10\) reached'
IterationLimitError, r'Iteration limit \(10\) reached'
):
calculate_variable_from_constraint(
m.x, m.d, iterlim=10, linesearch=False, diff_mode=mode
Expand All @@ -162,7 +163,7 @@ def test_nonlinear(self):
for mode in all_diff_modes:
m.x.set_value(1.25) # set the initial value
with self.assertRaisesRegex(
RuntimeError, "Linesearch iteration limit reached"
IterationLimitError, "Linesearch iteration limit reached"
):
calculate_variable_from_constraint(
m.x, m.d, iterlim=10, linesearch=True, diff_mode=mode
Expand All @@ -172,7 +173,7 @@ def test_nonlinear(self):
for mode in all_diff_modes:
m.x = 0
with self.assertRaisesRegex(
RuntimeError,
ValueError,
"Initial value for variable 'x' results in a "
"derivative value for constraint 'c' that is very close to zero.",
):
Expand All @@ -185,7 +186,7 @@ def test_nonlinear(self):
# numeric differentiation should not be used to check if a
# derivative is always zero
with self.assertRaisesRegex(
RuntimeError,
ValueError,
"Initial value for variable 'y' results in a "
"derivative value for constraint 'c' that is very close to zero.",
):
Expand Down Expand Up @@ -314,7 +315,7 @@ def f(m):
m.c = Constraint(expr=m.x**0.5 == -1e-8)
m.x = 1e-8 # 197.932807183
with self.assertRaisesRegex(
RuntimeError,
IterationLimitError,
"Linesearch iteration limit reached solving for variable 'x' using "
"constraint 'c'; remaining residual = {function evaluation error}",
):
Expand Down

0 comments on commit 73f872b

Please sign in to comment.