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

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

18 

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

20 

21import operator 

22from collections import namedtuple 

23 

24from .. import glyphs 

25from ..apidoc import api_end, api_start 

26from .constraint import Constraint, ConstraintConversion 

27 

28_API_START = api_start(globals()) 

29# ------------------------------- 

30 

31 

32class ExtremumConstraint(Constraint): 

33 """Bound on a maximum (minimum) over convex (concave) functions.""" 

34 

35 class Conversion(ConstraintConversion): 

36 """Bound on a maximum or minimum of functions conversion.""" 

37 

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__ 

45 

46 for lhs_type in subtype.lhs_types: 

47 yield ("con", lhs_type.predict(relation, subtype.rhs_type), 1) 

48 

49 @classmethod 

50 def convert(cls, con, options): 

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

52 from ..modeling import Problem 

53 

54 P = Problem() 

55 

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) 

62 

63 return P 

64 

65 def __init__(self, extremum, relation, rhs): 

66 """Construct a :class:`ExtremumConstraint`. 

67 

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) 

77 

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 

86 

87 self.extremum = extremum 

88 self.relation = relation 

89 self.rhs = rhs 

90 

91 super(ExtremumConstraint, self).__init__(extremum._typeStr) 

92 

93 Subtype = namedtuple("Subtype", ("lhs_types", "relation", "rhs_type")) 

94 

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) 

100 

101 @classmethod 

102 def _cost(cls, subtype): 

103 return len(subtype.lhs_types) 

104 

105 def _expression_names(self): 

106 yield "extremum" 

107 yield "rhs" 

108 

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) 

114 

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 

120 

121 

122# -------------------------------------- 

123__all__ = api_end(_API_START, globals())