Coverage for picos/constraints/con_simplex.py: 69.62%

79 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-26 07:46 +0000

1# ------------------------------------------------------------------------------ 

2# Copyright (C) 2012-2017 Guillaume Sagnol 

3# Copyright (C) 2018-2019 Maximilian Stahlberg 

4# 

5# This file is part of PICOS. 

6# 

7# PICOS is free software: you can redistribute it and/or modify it under the 

8# terms of the GNU General Public License as published by the Free Software 

9# Foundation, either version 3 of the License, or (at your option) any later 

10# version. 

11# 

12# PICOS is distributed in the hope that it will be useful, but WITHOUT ANY 

13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

14# A PARTICULAR PURPOSE. See the GNU General Public License for more details. 

15# 

16# You should have received a copy of the GNU General Public License along with 

17# this program. If not, see <http://www.gnu.org/licenses/>. 

18# ------------------------------------------------------------------------------ 

19 

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

21 

22from collections import namedtuple 

23 

24import cvxopt 

25 

26from .. import glyphs 

27from ..apidoc import api_end, api_start 

28from .constraint import Constraint, ConstraintConversion 

29 

30_API_START = api_start(globals()) 

31# ------------------------------- 

32 

33 

34class SimplexConstraint(Constraint): 

35 """(Symmetrized, truncated) simplex membership constraint.""" 

36 

37 class AffineConversion(ConstraintConversion): 

38 """Simplex membership to affine constraint conversion.""" 

39 

40 @classmethod 

41 def predict(cls, subtype, options): 

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

43 from ..expressions import RealVariable as RV 

44 from . import AffineConstraint as AC 

45 

46 n = subtype.argdim 

47 truncated = subtype.truncated 

48 symmetrized = subtype.symmetrized 

49 

50 if not truncated and not symmetrized: 

51 yield ("con", AC.make_type(dim=(n + 1), eq=False), 1) 

52 elif truncated and not symmetrized: 

53 yield ("con", AC.make_type(dim=(2*n + 1), eq=False), 1) 

54 elif not truncated and symmetrized: 

55 yield ("var", RV.make_var_type(dim=n, bnd=0), 1) 

56 yield ("con", AC.make_type(dim=(n + 1), eq=False), 2) 

57 yield ("con", AC.make_type(dim=1, eq=False), 1) 

58 else: 

59 yield ("var", RV.make_var_type(dim=n, bnd=0), 1) 

60 yield ("con", AC.make_type(dim=(n + 1), eq=False), 2) 

61 yield ("con", AC.make_type(dim=1, eq=False), 1) 

62 yield ("con", AC.make_type(dim=n, eq=False), 1) 

63 

64 @classmethod 

65 def convert(cls, con, options): 

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

67 from ..expressions import RealVariable 

68 from ..modeling import Problem 

69 

70 simplex = con.simplex 

71 truncated = simplex.truncated 

72 symmetrized = simplex.symmetrized 

73 

74 x = con.element.vec 

75 n = len(x) 

76 r = simplex.radius 

77 

78 P = Problem() 

79 

80 if not truncated and not symmetrized: 

81 aff = -x // (1 | x) 

82 rhs = cvxopt.sparse([0]*n) 

83 P.add_constraint(aff <= rhs // r) 

84 elif truncated and not symmetrized: 

85 aff = x // -x // (1 | x) 

86 rhs = cvxopt.sparse([1]*n + [0]*n) 

87 P.add_constraint(aff <= rhs // r) 

88 elif not truncated and symmetrized: 

89 v = RealVariable("__v", n) 

90 P.add_constraint(x <= v) 

91 P.add_constraint(-x <= v) 

92 P.add_constraint((1 | v) <= r) 

93 else: # truncated and symmetrized 

94 v = RealVariable("__v", n) 

95 P.add_constraint(x <= v) 

96 P.add_constraint(-x <= v) 

97 P.add_constraint((1 | v) <= r) 

98 P.add_constraint(v <= 1) 

99 

100 return P 

101 

102 def __init__(self, simplex, element): 

103 """Construct a :class:`SimplexConstraint`. 

104 

105 :param ~picos.expressions.AffineExpression element: 

106 Expression in the simplex. 

107 """ 

108 from ..expressions import AffineExpression, Simplex 

109 

110 assert isinstance(simplex, Simplex) 

111 assert isinstance(element, AffineExpression) 

112 

113 self.simplex = simplex 

114 self.element = element 

115 

116 super(SimplexConstraint, self).__init__(simplex._typeStr) 

117 

118 Subtype = namedtuple("Subtype", ("argdim", "truncated", "symmetrized")) 

119 

120 def _subtype(self): 

121 return self.Subtype( 

122 len(self.element), 

123 self.simplex.truncated, 

124 self.simplex.symmetrized) 

125 

126 @classmethod 

127 def _cost(cls, subtype): 

128 return subtype.argdim 

129 

130 def _expression_names(self): 

131 yield "simplex" 

132 yield "element" 

133 

134 def _str(self): 

135 return glyphs.element(self.element.string, self.simplex.string) 

136 

137 def _get_slack(self): 

138 # TODO: Compute simplex constraint slack. 

139 raise NotImplementedError 

140 

141 # FIXME: This old code can't be right for all cases. 

142 # return cvxopt.matrix([1 - norm(self.element, 'inf').safe_value, 

143 # self.simplex.radius - norm(self.element, 1).safe_value]) 

144 

145 

146# -------------------------------------- 

147__all__ = api_end(_API_START, globals())