Skip to content
Open
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
22 changes: 20 additions & 2 deletions PEPit/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Constraint(object):

Attributes:
name (str): A name set through the set_name method. None is no name is given.
activated (bool): A boolean flag used to activate/deactivate the Constraint in the PEP.
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.
Expand Down Expand Up @@ -56,13 +57,16 @@ def __init__(self,
AssertionError: if provided `equality_or_inequality` argument is neither "equality" nor "inequality".

"""
# Initialize name of the constraint
self.name = None
# Initialize the activated attribute to True
self.activated = True

# Update the counter
self.counter = Constraint.counter
Constraint.counter += 1

# Initialize name of the constraint
self.name = "Constraint {}".format(self.counter)

# Store the underlying expression
self.expression = expression

Expand Down Expand Up @@ -92,6 +96,20 @@ def get_name(self):
"""
return self.name

def activate(self):
"""
Activate the use of the Constraint.

"""
self.activated = True

def deactivate(self):
"""
Deactivate the use of the Constraint.

"""
self.activated = False

def eval(self):
"""
Compute, store and return the value of the underlying :class:`Expression` of this :class:`Constraint`.
Expand Down
183 changes: 115 additions & 68 deletions PEPit/pep.py

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions PEPit/psd_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class PSDMatrix(object):

Attributes:
name (str): A name set through the set_name method. None is no name is given.
activated (bool): A boolean flag used to activate/deactivate the LMI constraint in the PEP.
matrix_of_expressions (Iterable of Iterable of Expression): a square matrix of :class:`Expression` objects.
shape (tuple of ints): the shape of the underlying matrix of :class:`Expression` objects.
_value (2D ndarray of floats): numerical values of :class:`Expression` objects
Expand Down Expand Up @@ -61,6 +62,9 @@ def __init__(self,
TypeError: if provided matrix does not contain only Expressions and / or scalar values.

"""
# Initialize the activated attribute to True
self.activated = True

# Initialize name of the psd matrix
self.name = name

Expand Down Expand Up @@ -95,6 +99,20 @@ def get_name(self):
"""
return self.name

def activate(self):
"""
Activate the use of the LMI constraint.

"""
self.activated = True

def deactivate(self):
"""
Deactivate the use of the LMI constraint.

"""
self.activated = False

@staticmethod
def _store(matrix_of_expressions):
"""
Expand Down
3 changes: 1 addition & 2 deletions PEPit/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,12 @@ def send_constraint_to_solver(self, constraint):
"""
raise NotImplementedError("This method must be overwritten in children classes")

def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix):
def send_lmi_constraint_to_solver(self, psd_matrix):
"""
Transfer a PEPit :class:`PSDMatrix` (LMI constraint) to the solver
and add it the tracking lists.

Args:
psd_counter (int): a counter useful for the verbose mode.
psd_matrix (PSDMatrix): a matrix of expressions that is constrained to be PSD.

"""
Expand Down
9 changes: 2 additions & 7 deletions PEPit/wrappers/cvxpy_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,17 @@ def send_constraint_to_solver(self, constraint):
# Raise an exception otherwise
raise ValueError('The attribute \'equality_or_inequality\' of a constraint object'
' must either be \'equality\' or \'inequality\'.'
'Got {}'.format(constraint.equality_or_inequality))
'{} got {}'.format(constraint.get_name(), constraint.equality_or_inequality))

# Add the corresponding CVXPY constraint to the list of constraints to be sent to CVXPY
self._list_of_solver_constraints.append(cvxpy_constraint)

def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix):
def send_lmi_constraint_to_solver(self, psd_matrix):
"""
Transform a PEPit :class:`PSDMatrix` into a CVXPY symmetric PSD matrix
and add the 2 formats of the constraints into the tracking lists.

Args:
psd_counter (int): a counter useful for the verbose mode.
psd_matrix (PSDMatrix): a matrix of expressions that is constrained to be PSD.

"""
Expand All @@ -170,10 +169,6 @@ def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix):
for j in range(psd_matrix.shape[1]):
cvxpy_constraints_list.append(M[i, j] == self._expression_to_solver(psd_matrix[i, j]))

# Print a message if verbose mode activated
if self.verbose > 0:
print('\t\t Size of PSD matrix {}: {}x{}'.format(psd_counter + 1, *psd_matrix.shape))

# Add the corresponding CVXPY constraints to the list of constraints to be sent to CVXPY
self._list_of_solver_constraints += cvxpy_constraints_list

Expand Down
9 changes: 2 additions & 7 deletions PEPit/wrappers/mosek_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,13 @@ def send_constraint_to_solver(self, constraint, track=True):
# Raise an exception otherwise
raise ValueError('The attribute \'equality_or_inequality\' of a constraint object'
' must either be \'equality\' or \'inequality\'.'
'Got {}'.format(constraint.equality_or_inequality))
'{} got {}'.format(constraint.get_name(), constraint.equality_or_inequality))

def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix):
def send_lmi_constraint_to_solver(self, psd_matrix):
"""
Transfer a PEPit :class:`PSDMatrix` (LMI constraint) to MOSEK and add it the tracking lists.

Args:
psd_counter (int): a counter useful for the verbose mode.
psd_matrix (PSDMatrix): a matrix of expressions that is constrained to be PSD.

"""
Expand Down Expand Up @@ -204,10 +203,6 @@ def send_lmi_constraint_to_solver(self, psd_counter, psd_matrix):
self.task.putaijlist(np.full(a_i.shape, nb_cons), a_i, a_val)
self.task.putconbound(nb_cons, mosek.boundkey.fx, -alpha_val, -alpha_val)

# Print a message if verbose mode activated
if self.verbose > 0:
print('\t\t Size of PSD matrix {}: {}x{}'.format(psd_counter + 1, *psd_matrix.shape))

def _recover_dual_values(self):
"""
Recover all dual variables from solver.
Expand Down
9 changes: 8 additions & 1 deletion tests/test_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_counter(self):

def test_name(self):

self.assertIsNone(self.initial_condition.get_name())
self.assertEqual(self.initial_condition.get_name(), "Constraint 0")
self.assertIsNone(self.performance_metric.get_name())

self.initial_condition.set_name("init")
Expand All @@ -93,6 +93,13 @@ def test_equality_inequality(self):
self.assertIsInstance(self.problem.list_of_constraints[i].equality_or_inequality, str)
self.assertIn(self.problem.list_of_constraints[i].equality_or_inequality, {'equality', 'inequality'})

def test_activated(self):
self.assertIs(self.initial_condition.activated, True)
self.initial_condition.deactivate()
self.assertIs(self.initial_condition.activated, False)
self.initial_condition.activate()
self.assertIs(self.initial_condition.activated, True)

def test_eval(self):

for i in range(len(self.func.list_of_class_constraints)):
Expand Down
34 changes: 31 additions & 3 deletions tests/test_pep.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,40 @@ def test_lmi_constraints_in_real_problem(self):
def test_consistency(self):

# Solve twice the same problem in a row and verify the two lists of constraints have same length.
self.assertEqual(len(self.problem._list_of_prepared_constraints), 0)
self.assertEqual(len(self.problem._list_of_constraints_sent_to_wrapper), 0)

# Run solve
_ = self.problem.solve(verbose=self.verbose)
l1 = self.problem._list_of_constraints_sent_to_wrapper
lp1 = self.problem._list_of_prepared_constraints
ls1 = self.problem._list_of_constraints_sent_to_wrapper

# Rerun solve
_ = self.problem.solve(verbose=self.verbose)
l2 = self.problem._list_of_constraints_sent_to_wrapper
lp2 = self.problem._list_of_prepared_constraints
ls2 = self.problem._list_of_constraints_sent_to_wrapper

self.assertEqual(len(l1), len(l2))
# Deactivate one constraint, then rerun solve
self.problem.list_of_constraints[0].deactivate()
_ = self.problem.solve(verbose=self.verbose)
lp3 = self.problem._list_of_prepared_constraints
ls3 = self.problem._list_of_constraints_sent_to_wrapper

# Reactivate the constraint, then rerun solve
self.problem.list_of_constraints[0].activate()
_ = self.problem.solve(verbose=self.verbose)
lp4 = self.problem._list_of_prepared_constraints
ls4 = self.problem._list_of_constraints_sent_to_wrapper

# Compare lengths
self.assertEqual(len(lp1), len(lp2))
self.assertEqual(len(lp1), len(lp3))
self.assertEqual(len(lp1), len(lp4))

self.assertEqual(len(lp1), len(ls1))
self.assertEqual(len(lp2), len(ls2))
self.assertEqual(len(lp3), len(ls3)+1)
self.assertEqual(len(lp4), len(ls4))

def test_dimension_reduction(self):

Expand Down
7 changes: 7 additions & 0 deletions tests/test_psd_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ def test_getitem(self):
self.assertIs(self.psd1[0, 0], self.expr1)
self.assertIs(self.psd2[0, 1], self.expr3)

def test_activated(self):
self.assertIs(self.psd1.activated, True)
self.psd1.deactivate()
self.assertIs(self.psd1.activated, False)
self.psd1.activate()
self.assertIs(self.psd1.activated, True)

def test_eval(self):

# The PEP has not been solved yet, so no value is accessible.
Expand Down
1 change: 1 addition & 0 deletions tests/test_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def test_dual_sign_in_equality_constraints(self):
1 == (self.x0 - self.xs) ** 2,
]:
self.problem.list_of_constraints = [constraint]
self.problem._prepare_constraints(verbose=self.verbose)
self.problem.solve(verbose=self.verbose, wrapper=self.wrapper)
elements_of_proof.append(constraint.eval_dual() * constraint.expression)

Expand Down
Loading