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

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# ------------------------------------------------------------------------------ 

18 

19"""Implementation of :class:`AbsoluteValueConstraint`.""" 

20 

21from collections import namedtuple 

22 

23from .. import glyphs 

24from ..apidoc import api_end, api_start 

25from .constraint import Constraint, ConstraintConversion 

26 

27_API_START = api_start(globals()) 

28# ------------------------------- 

29 

30 

31class AbsoluteValueConstraint(Constraint): 

32 """Upper bound on an absolute value.""" 

33 

34 # TODO: Improve performance: Add two scalar constraints. 

35 class AffineConversion(ConstraintConversion): 

36 """Upper bound on an absolute value to affine inequality conversion.""" 

37 

38 @classmethod 

39 def predict(cls, subtype, options): 

40 """Implement :meth:`~.constraint.ConstraintConversion.predict`.""" 

41 from . import AffineConstraint 

42 

43 yield ("con", AffineConstraint.make_type(dim=2, eq=False), 1) 

44 

45 @classmethod 

46 def convert(cls, con, options): 

47 """Implement :meth:`~.constraint.ConstraintConversion.convert`.""" 

48 from ..modeling import Problem 

49 

50 P = Problem() 

51 P.add_constraint((con.signedScalar // -con.signedScalar) 

52 <= (con.upperBound // con.upperBound)) 

53 

54 return P 

55 

56 @classmethod 

57 def dual(cls, auxVarPrimals, auxConDuals, options): 

58 """Implement :meth:`~.constraint.ConstraintConversion.dual`.""" 

59 assert len(auxConDuals) == 1 

60 

61 if auxConDuals[0] is None: 

62 return None 

63 else: 

64 return auxConDuals[0][1] - auxConDuals[0][0] 

65 

66 def __init__(self, signedScalar, upperBound): 

67 """Construct an :class:`AbsoluteValueConstraint`. 

68 

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 

75 

76 assert isinstance(signedScalar, AffineExpression) 

77 assert isinstance(upperBound, AffineExpression) 

78 assert len(signedScalar) == 1 

79 assert len(upperBound) == 1 

80 

81 self.signedScalar = signedScalar 

82 self.upperBound = upperBound 

83 

84 super(AbsoluteValueConstraint, self).__init__("Absolute Value") 

85 

86 Subtype = namedtuple("Subtype", ()) 

87 

88 def _subtype(self): 

89 return self.Subtype() 

90 

91 @classmethod 

92 def _cost(cls, subtype): 

93 return 2 

94 

95 def _expression_names(self): 

96 yield "signedScalar" 

97 yield "upperBound" 

98 

99 def _str(self): 

100 return glyphs.le( 

101 glyphs.abs(self.signedScalar.string), self.upperBound.string) 

102 

103 def _get_size(self): 

104 return (1, 1) 

105 

106 def _get_slack(self): 

107 return self.upperBound.safe_value - abs(self.signedScalar.safe_value) 

108 

109 

110# -------------------------------------- 

111__all__ = api_end(_API_START, globals())