Coverage for picos/constraints/con_absolute.py: 100.00%
49 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) 2018-2019 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:`AbsoluteValueConstraint`."""
21from collections import namedtuple
23from .. import glyphs
24from ..apidoc import api_end, api_start
25from .constraint import Constraint, ConstraintConversion
27_API_START = api_start(globals())
28# -------------------------------
31class AbsoluteValueConstraint(Constraint):
32 """Upper bound on an absolute value."""
34 # TODO: Improve performance: Add two scalar constraints.
35 class AffineConversion(ConstraintConversion):
36 """Upper bound on an absolute value to affine inequality conversion."""
38 @classmethod
39 def predict(cls, subtype, options):
40 """Implement :meth:`~.constraint.ConstraintConversion.predict`."""
41 from . import AffineConstraint
43 yield ("con", AffineConstraint.make_type(dim=2, eq=False), 1)
45 @classmethod
46 def convert(cls, con, options):
47 """Implement :meth:`~.constraint.ConstraintConversion.convert`."""
48 from ..modeling import Problem
50 P = Problem()
51 P.add_constraint((con.signedScalar // -con.signedScalar)
52 <= (con.upperBound // con.upperBound))
54 return P
56 @classmethod
57 def dual(cls, auxVarPrimals, auxConDuals, options):
58 """Implement :meth:`~.constraint.ConstraintConversion.dual`."""
59 assert len(auxConDuals) == 1
61 if auxConDuals[0] is None:
62 return None
63 else:
64 return auxConDuals[0][1] - auxConDuals[0][0]
66 def __init__(self, signedScalar, upperBound):
67 """Construct an :class:`AbsoluteValueConstraint`.
69 :param ~picos.expressions.AffineExpression signedScalar:
70 A scalar expression.
71 :param ~picos.expressions.AffineExpression upperBound:
72 Upper bound on the expression.
73 """
74 from ..expressions import AffineExpression
76 assert isinstance(signedScalar, AffineExpression)
77 assert isinstance(upperBound, AffineExpression)
78 assert len(signedScalar) == 1
79 assert len(upperBound) == 1
81 self.signedScalar = signedScalar
82 self.upperBound = upperBound
84 super(AbsoluteValueConstraint, self).__init__("Absolute Value")
86 Subtype = namedtuple("Subtype", ())
88 def _subtype(self):
89 return self.Subtype()
91 @classmethod
92 def _cost(cls, subtype):
93 return 2
95 def _expression_names(self):
96 yield "signedScalar"
97 yield "upperBound"
99 def _str(self):
100 return glyphs.le(
101 glyphs.abs(self.signedScalar.string), self.upperBound.string)
103 def _get_size(self):
104 return (1, 1)
106 def _get_slack(self):
107 return self.upperBound.safe_value - abs(self.signedScalar.safe_value)
110# --------------------------------------
111__all__ = api_end(_API_START, globals())