Coverage for picos/constraints/con_extremum.py: 98.25%
57 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) 2020 Maximilian Stahlberg
3#
4# This file is part of PICOS.
5#
6# PICOS is free software: you can redistribute it and/or modify it under the
7# terms of the GNU General Public License as published by the Free Software
8# Foundation, either version 3 of the License, or (at your option) any later
9# version.
10#
11# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY
12# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program. If not, see <http://www.gnu.org/licenses/>.
17# ------------------------------------------------------------------------------
19"""Implementation of :class:`ExtremumConstraint`."""
21import operator
22from collections import namedtuple
24from .. import glyphs
25from ..apidoc import api_end, api_start
26from .constraint import Constraint, ConstraintConversion
28_API_START = api_start(globals())
29# -------------------------------
32class ExtremumConstraint(Constraint):
33 """Bound on a maximum (minimum) over convex (concave) functions."""
35 class Conversion(ConstraintConversion):
36 """Bound on a maximum or minimum of functions conversion."""
38 @classmethod
39 def predict(cls, subtype, options):
40 """Implement :meth:`~.constraint.ConstraintConversion.predict`."""
41 if subtype.relation == Constraint.LE:
42 relation = operator.__le__
43 else:
44 relation = operator.__ge__
46 for lhs_type in subtype.lhs_types:
47 yield ("con", lhs_type.predict(relation, subtype.rhs_type), 1)
49 @classmethod
50 def convert(cls, con, options):
51 """Implement :meth:`~.constraint.ConstraintConversion.convert`."""
52 from ..modeling import Problem
54 P = Problem()
56 if con.relation == Constraint.LE:
57 for x in con.extremum.expressions:
58 P.add_constraint(x <= con.rhs)
59 else:
60 for x in con.extremum.expressions:
61 P.add_constraint(x >= con.rhs)
63 return P
65 def __init__(self, extremum, relation, rhs):
66 """Construct a :class:`ExtremumConstraint`.
68 :param ~picos.expressions.Extremum extremum:
69 Left hand side expression.
70 :param str relation:
71 Constraint relation symbol.
72 :param ~picos.expressions.AffineExpression rhs:
73 Right hand side expression.
74 """
75 from ..expressions import (AffineExpression, Extremum, MaximumConvex,
76 MinimumConcave)
78 assert isinstance(extremum, Extremum)
79 assert isinstance(rhs, AffineExpression)
80 assert relation in self.LE + self.GE
81 if relation == self.LE:
82 assert isinstance(extremum, MaximumConvex)
83 else:
84 assert isinstance(extremum, MinimumConcave)
85 assert len(rhs) == 1
87 self.extremum = extremum
88 self.relation = relation
89 self.rhs = rhs
91 super(ExtremumConstraint, self).__init__(extremum._typeStr)
93 Subtype = namedtuple("Subtype", ("lhs_types", "relation", "rhs_type"))
95 def _subtype(self):
96 return self.Subtype(
97 lhs_types=self.extremum.subtype.types,
98 relation=self.relation,
99 rhs_type=self.rhs.type)
101 @classmethod
102 def _cost(cls, subtype):
103 return len(subtype.lhs_types)
105 def _expression_names(self):
106 yield "extremum"
107 yield "rhs"
109 def _str(self):
110 if self.relation == self.LE:
111 return glyphs.le(self.extremum.string, self.rhs.string)
112 else:
113 return glyphs.ge(self.extremum.string, self.rhs.string)
115 def _get_slack(self):
116 if self.relation == self.LE:
117 return self.rhs.safe_value - self.extremum.safe_value
118 else:
119 return self.extremum.safe_value - self.rhs.safe_value
122# --------------------------------------
123__all__ = api_end(_API_START, globals())