Coverage for picos/constraints/con_simplex.py: 69.62%
79 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-03-26 07:46 +0000
1# ------------------------------------------------------------------------------
2# Copyright (C) 2012-2017 Guillaume Sagnol
3# Copyright (C) 2018-2019 Maximilian Stahlberg
4#
5# This file is part of PICOS.
6#
7# PICOS is free software: you can redistribute it and/or modify it under the
8# terms of the GNU General Public License as published by the Free Software
9# Foundation, either version 3 of the License, or (at your option) any later
10# version.
11#
12# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program. If not, see <http://www.gnu.org/licenses/>.
18# ------------------------------------------------------------------------------
20"""Implementation of :class:`SimplexConstraint`."""
22from collections import namedtuple
24import cvxopt
26from .. import glyphs
27from ..apidoc import api_end, api_start
28from .constraint import Constraint, ConstraintConversion
30_API_START = api_start(globals())
31# -------------------------------
34class SimplexConstraint(Constraint):
35 """(Symmetrized, truncated) simplex membership constraint."""
37 class AffineConversion(ConstraintConversion):
38 """Simplex membership to affine constraint conversion."""
40 @classmethod
41 def predict(cls, subtype, options):
42 """Implement :meth:`~.constraint.ConstraintConversion.predict`."""
43 from ..expressions import RealVariable as RV
44 from . import AffineConstraint as AC
46 n = subtype.argdim
47 truncated = subtype.truncated
48 symmetrized = subtype.symmetrized
50 if not truncated and not symmetrized:
51 yield ("con", AC.make_type(dim=(n + 1), eq=False), 1)
52 elif truncated and not symmetrized:
53 yield ("con", AC.make_type(dim=(2*n + 1), eq=False), 1)
54 elif not truncated and symmetrized:
55 yield ("var", RV.make_var_type(dim=n, bnd=0), 1)
56 yield ("con", AC.make_type(dim=(n + 1), eq=False), 2)
57 yield ("con", AC.make_type(dim=1, eq=False), 1)
58 else:
59 yield ("var", RV.make_var_type(dim=n, bnd=0), 1)
60 yield ("con", AC.make_type(dim=(n + 1), eq=False), 2)
61 yield ("con", AC.make_type(dim=1, eq=False), 1)
62 yield ("con", AC.make_type(dim=n, eq=False), 1)
64 @classmethod
65 def convert(cls, con, options):
66 """Implement :meth:`~.constraint.ConstraintConversion.convert`."""
67 from ..expressions import RealVariable
68 from ..modeling import Problem
70 simplex = con.simplex
71 truncated = simplex.truncated
72 symmetrized = simplex.symmetrized
74 x = con.element.vec
75 n = len(x)
76 r = simplex.radius
78 P = Problem()
80 if not truncated and not symmetrized:
81 aff = -x // (1 | x)
82 rhs = cvxopt.sparse([0]*n)
83 P.add_constraint(aff <= rhs // r)
84 elif truncated and not symmetrized:
85 aff = x // -x // (1 | x)
86 rhs = cvxopt.sparse([1]*n + [0]*n)
87 P.add_constraint(aff <= rhs // r)
88 elif not truncated and symmetrized:
89 v = RealVariable("__v", n)
90 P.add_constraint(x <= v)
91 P.add_constraint(-x <= v)
92 P.add_constraint((1 | v) <= r)
93 else: # truncated and symmetrized
94 v = RealVariable("__v", n)
95 P.add_constraint(x <= v)
96 P.add_constraint(-x <= v)
97 P.add_constraint((1 | v) <= r)
98 P.add_constraint(v <= 1)
100 return P
102 def __init__(self, simplex, element):
103 """Construct a :class:`SimplexConstraint`.
105 :param ~picos.expressions.AffineExpression element:
106 Expression in the simplex.
107 """
108 from ..expressions import AffineExpression, Simplex
110 assert isinstance(simplex, Simplex)
111 assert isinstance(element, AffineExpression)
113 self.simplex = simplex
114 self.element = element
116 super(SimplexConstraint, self).__init__(simplex._typeStr)
118 Subtype = namedtuple("Subtype", ("argdim", "truncated", "symmetrized"))
120 def _subtype(self):
121 return self.Subtype(
122 len(self.element),
123 self.simplex.truncated,
124 self.simplex.symmetrized)
126 @classmethod
127 def _cost(cls, subtype):
128 return subtype.argdim
130 def _expression_names(self):
131 yield "simplex"
132 yield "element"
134 def _str(self):
135 return glyphs.element(self.element.string, self.simplex.string)
137 def _get_slack(self):
138 # TODO: Compute simplex constraint slack.
139 raise NotImplementedError
141 # FIXME: This old code can't be right for all cases.
142 # return cvxopt.matrix([1 - norm(self.element, 'inf').safe_value,
143 # self.simplex.radius - norm(self.element, 1).safe_value])
146# --------------------------------------
147__all__ = api_end(_API_START, globals())