[docs]
class Constraint(object):
"""
A :class:`Constraint` encodes either an equality or an inequality between two :class:`Expression` objects.
A :class:`Constraint` must be understood either as
`self.expression` = 0 or `self.expression` :math:`\\leqslant` 0
depending on the value of `self.equality_or_inequality`.
Attributes:
name (str): A name set through the set_name method. None is no name is given.
expression (Expression): The :class:`Expression` that is compared to 0.
equality_or_inequality (str): "equality" or "inequality". Encodes the type of constraint.
_value (float): numerical value of `self.expression` obtained after solving the PEP via SDP solver.
Set to None before the call to the method `PEP.solve` from the :class:`PEP`.
_dual_variable_value (float): the associated dual variable from the numerical solution to the corresponding PEP.
Set to None before the call to `PEP.solve` from the :class:`PEP`
counter (int): counts the number of :class:`Constraint` objects.
A :class:`Constraint` results from a comparison between two :class:`Expression` objects.
Example:
>>> from PEPit import Expression
>>> expr1 = Expression()
>>> expr2 = Expression()
>>> inequality1 = expr1 <= expr2
>>> inequality2 = expr1 >= expr2
>>> equality = expr1 == expr2
"""
# Class counter.
# It counts the number of generated constraints
counter = 0
def __init__(self,
expression,
equality_or_inequality,
):
"""
:class:`Constraint` objects can also be instantiated via the following arguments.
Args:
expression (Expression): an object of class Expression
equality_or_inequality (str): either 'equality' or 'inequality'.
Instantiating the :class:`Constraint` objects of the first example can be done by
Example:
>>> from PEPit import Expression
>>> expr1 = Expression()
>>> expr2 = Expression()
>>> inequality1 = Constraint(expression=expr1-expr2, equality_or_inequality="inequality")
>>> inequality2 = Constraint(expression=expr2-expr1, equality_or_inequality="inequality")
>>> equality = Constraint(expression=expr1-expr2, equality_or_inequality="equality")
Raises:
AssertionError: if provided `equality_or_inequality` argument is neither "equality" nor "inequality".
"""
# Initialize name of the constraint
self.name = None
# Update the counter
self.counter = Constraint.counter
Constraint.counter += 1
# Store the underlying expression
self.expression = expression
# Verify that 'equality_or_inequality' is well-defined and store its value
assert equality_or_inequality in {'equality', 'inequality'}
self.equality_or_inequality = equality_or_inequality
# The value of the underlying expression must be stored in self._value.
self._value = None
# Moreover, the associated dual variable value must be stored in self._dual_variable_value.
self._dual_variable_value = None
[docs]
def set_name(self, name):
"""
Assign a name to self for easier identification purpose.
Args:
name (str): a name to be given to self.
"""
self.name = name
[docs]
def get_name(self):
"""
Returns (str): the attribute name.
"""
return self.name
[docs]
def eval(self):
"""
Compute, store and return the value of the underlying :class:`Expression` of this :class:`Constraint`.
Returns:
self._value (np.array): The value of the underlying :class:`Expression` of this :class:`Constraint`
after the corresponding PEP was solved numerically.
Raises:
ValueError("The PEP must be solved to evaluate Constraints!") if the PEP has not been solved yet.
"""
# If the attribute value is not None, then simply return it.
# Otherwise, compute it and return it.
if self._value is None:
try:
self._value = self.expression.eval()
except ValueError("The PEP must be solved to evaluate Expressions!"):
raise ValueError("The PEP must be solved to evaluate Constraints!")
return self._value
[docs]
def eval_dual(self):
"""
Compute, store and return the value of the dual variable of this :class:`Constraint`.
Returns:
self._dual_variable_value (float): The value of the dual variable of this :class:`Constraint`
after the corresponding PEP was solved numerically.
Raises:
ValueError("The PEP must be solved to evaluate Constraints dual variables!")
if the PEP has not been solved yet.
"""
# If the attribute _dual_variable_value is not None, then simply return it.
# Otherwise, raise a ValueError.
if self._dual_variable_value is None:
# The PEP would have filled the attribute after solving the problem.
raise ValueError("The PEP must be solved to evaluate Constraints dual variables!")
return self._dual_variable_value