Source code for picos.constraints.con_simplex

# ------------------------------------------------------------------------------
# Copyright (C) 2012-2017 Guillaume Sagnol
# Copyright (C) 2018-2019 Maximilian Stahlberg
#
# This file is part of PICOS.
#
# PICOS is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------

"""Implementation of :class:`SimplexConstraint`."""

from collections import namedtuple

import cvxopt

from .. import glyphs
from ..apidoc import api_end, api_start
from .constraint import Constraint, ConstraintConversion

_API_START = api_start(globals())
# -------------------------------


[docs]class SimplexConstraint(Constraint): """(Symmetrized, truncated) simplex membership constraint."""
[docs] class AffineConversion(ConstraintConversion): """Simplex membership to affine constraint conversion."""
[docs] @classmethod def predict(cls, subtype, options): """Implement :meth:`~.constraint.ConstraintConversion.predict`.""" from ..expressions import RealVariable as RV from . import AffineConstraint as AC n = subtype.argdim truncated = subtype.truncated symmetrized = subtype.symmetrized if not truncated and not symmetrized: yield ("con", AC.make_type(dim=(n + 1), eq=False), 1) elif truncated and not symmetrized: yield ("con", AC.make_type(dim=(2*n + 1), eq=False), 1) elif not truncated and symmetrized: yield ("var", RV.make_var_type(dim=n, bnd=0), 1) yield ("con", AC.make_type(dim=(n + 1), eq=False), 2) yield ("con", AC.make_type(dim=1, eq=False), 1) else: yield ("var", RV.make_var_type(dim=n, bnd=0), 1) yield ("con", AC.make_type(dim=(n + 1), eq=False), 2) yield ("con", AC.make_type(dim=1, eq=False), 1) yield ("con", AC.make_type(dim=n, eq=False), 1)
[docs] @classmethod def convert(cls, con, options): """Implement :meth:`~.constraint.ConstraintConversion.convert`.""" from ..expressions import RealVariable from ..modeling import Problem simplex = con.simplex truncated = simplex.truncated symmetrized = simplex.symmetrized x = con.element.vec n = len(x) r = simplex.radius P = Problem() if not truncated and not symmetrized: aff = -x // (1 | x) rhs = cvxopt.sparse([0]*n) P.add_constraint(aff <= rhs // r) elif truncated and not symmetrized: aff = x // -x // (1 | x) rhs = cvxopt.sparse([1]*n + [0]*n) P.add_constraint(aff <= rhs // r) elif not truncated and symmetrized: v = RealVariable("__v", n) P.add_constraint(x <= v) P.add_constraint(-x <= v) P.add_constraint((1 | v) <= r) else: # truncated and symmetrized v = RealVariable("__v", n) P.add_constraint(x <= v) P.add_constraint(-x <= v) P.add_constraint((1 | v) <= r) P.add_constraint(v <= 1) return P
[docs] def __init__(self, simplex, element): """Construct a :class:`SimplexConstraint`. :param ~picos.expressions.AffineExpression element: Expression in the simplex. """ from ..expressions import AffineExpression, Simplex assert isinstance(simplex, Simplex) assert isinstance(element, AffineExpression) self.simplex = simplex self.element = element super(SimplexConstraint, self).__init__(simplex._typeStr)
Subtype = namedtuple("Subtype", ("argdim", "truncated", "symmetrized")) def _subtype(self): return self.Subtype( len(self.element), self.simplex.truncated, self.simplex.symmetrized) @classmethod def _cost(cls, subtype): return subtype.argdim def _expression_names(self): yield "simplex" yield "element" def _str(self): return glyphs.element(self.element.string, self.simplex.string) def _get_slack(self): # TODO: Compute simplex constraint slack. raise NotImplementedError
# FIXME: This old code can't be right for all cases. # return cvxopt.matrix([1 - norm(self.element, 'inf').safe_value, # self.simplex.radius - norm(self.element, 1).safe_value]) # -------------------------------------- __all__ = api_end(_API_START, globals())