Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Models for assessing primal feasibility""" 

2from .set import ConstraintSet 

3from ..nomials import Variable, VectorVariable, parse_subs, NomialArray 

4from ..keydict import KeyDict 

5from .. import NamedVariables, SignomialsEnabled 

6 

7 

8class ConstraintsRelaxedEqually(ConstraintSet): 

9 """Relax constraints the same amount, as in Eqn. 10 of [Boyd2007]. 

10 

11 Arguments 

12 --------- 

13 constraints : iterable 

14 Constraints which will be relaxed (made easier). 

15 

16 

17 Attributes 

18 ---------- 

19 relaxvar : Variable 

20 The variable controlling the relaxation. A solved value of 1 means no 

21 relaxation. Higher values indicate the amount by which all constraints 

22 have been made easier: e.g., a value of 1.5 means all constraints were 

23 50 percent easier in the final solution than in the original problem. 

24 

25 [Boyd2007] : "A tutorial on geometric programming", Optim Eng 8:67-122 

26 

27 """ 

28 

29 def __init__(self, original_constraints): 

30 if not isinstance(original_constraints, ConstraintSet): 

31 original_constraints = ConstraintSet(original_constraints) 

32 self.original_constraints = original_constraints 

33 original_substitutions = original_constraints.substitutions 

34 

35 with NamedVariables("Relax"): 

36 self.relaxvar = Variable("C") 

37 with SignomialsEnabled(): 

38 relaxed_constraints = [c.relaxed(self.relaxvar) 

39 for c in original_constraints.flat()] 

40 

41 ConstraintSet.__init__(self, { 

42 "minimum relaxation": self.relaxvar >= 1, 

43 "relaxed constraints": relaxed_constraints}, original_substitutions) 

44 

45 

46class ConstraintsRelaxed(ConstraintSet): 

47 """Relax constraints, as in Eqn. 11 of [Boyd2007]. 

48 

49 Arguments 

50 --------- 

51 constraints : iterable 

52 Constraints which will be relaxed (made easier). 

53 

54 Attributes 

55 ---------- 

56 relaxvars : Variable 

57 The variables controlling the relaxation. A solved value of 1 means no 

58 relaxation was necessary or optimal for a particular constraint. 

59 Higher values indicate the amount by which that constraint has been 

60 made easier: e.g., a value of 1.5 means it was made 50 percent easier 

61 in the final solution than in the original problem. 

62 

63 [Boyd2007] : "A tutorial on geometric programming", Optim Eng 8:67-122 

64 

65 """ 

66 

67 def __init__(self, original_constraints): 

68 if not isinstance(original_constraints, ConstraintSet): 

69 original_constraints = ConstraintSet(original_constraints) 

70 self.original_constraints = original_constraints 

71 original_substitutions = original_constraints.substitutions 

72 with NamedVariables("Relax"): 

73 self.relaxvars = VectorVariable(len(original_constraints), "C") 

74 

75 with SignomialsEnabled(): 

76 relaxed_constraints = [ 

77 c.relaxed(self.relaxvars[i]) 

78 for i, c in enumerate(original_constraints.flat())] 

79 

80 ConstraintSet.__init__(self, { 

81 "minimum relaxation": self.relaxvars >= 1, 

82 "relaxed constraints": relaxed_constraints}, original_substitutions) 

83 

84 

85class ConstantsRelaxed(ConstraintSet): 

86 """Relax constants in a constraintset. 

87 

88 Arguments 

89 --------- 

90 constraints : iterable 

91 Constraints which will be relaxed (made easier). 

92 

93 include_only : set (optional) 

94 variable names must be in this set to be relaxed 

95 

96 exclude : set (optional) 

97 variable names in this set will never be relaxed 

98 

99 

100 Attributes 

101 ---------- 

102 relaxvars : Variable 

103 The variables controlling the relaxation. A solved value of 1 means no 

104 relaxation was necessary or optimal for a particular constant. 

105 Higher values indicate the amount by which that constant has been 

106 made easier: e.g., a value of 1.5 means it was made 50 percent easier 

107 in the final solution than in the original problem. Of course, this 

108 can also be determined by looking at the constant's new value directly. 

109 """ 

110 # pylint:disable=too-many-locals 

111 def __init__(self, constraints, *, include_only=None, exclude=None): 

112 exclude = frozenset(exclude) if exclude else frozenset() 

113 include_only = frozenset(include_only) if include_only else frozenset() 

114 with NamedVariables("Relax") as (self.lineage, _): 

115 pass # gives this model the correct lineage. 

116 

117 if not isinstance(constraints, ConstraintSet): 

118 constraints = ConstraintSet(constraints) 

119 substitutions = KeyDict(constraints.substitutions) 

120 constants, _, linked = parse_subs(constraints.varkeys, substitutions) 

121 if linked: 

122 kdc = KeyDict(constants) 

123 constrained_varkeys = constraints.constrained_varkeys() 

124 constants.update({k: f(kdc) for k, f in linked.items() 

125 if k in constrained_varkeys}) 

126 

127 self._derelax_map = {} 

128 relaxvars, self.freedvars, relaxation_constraints = [], [], {} 

129 for const, val in sorted(constants.items(), key=lambda i: i[0].eqstr): 

130 if val == 0: 

131 substitutions[const] = 0 

132 continue 

133 if include_only and const.name not in include_only: 

134 continue 

135 if const.name in exclude: 

136 continue 

137 # set up the lineage 

138 const.descr.pop("gradients", None) # nothing wants an old gradient 

139 newconstd = const.descr.copy() 

140 newconstd.pop("veckey", None) # only const wants an old veckey 

141 # everything but const wants a new lineage, to distinguish them 

142 newconstd["lineage"] = (newconstd.pop("lineage", ()) 

143 + (self.lineage[-1],)) 

144 # make the relaxation variable, free to get as large as it needs 

145 relaxedd = newconstd.copy() 

146 relaxedd["unitrepr"] = "-" # and unitless, importantly 

147 relaxvar = Variable(**relaxedd) 

148 relaxvars.append(relaxvar) 

149 # the newly freed const can acquire a new value 

150 del substitutions[const] 

151 freed = Variable(**const.descr) 

152 self.freedvars.append(freed) 

153 # becuase the make the newconst will take its old value 

154 newconstd["lineage"] += (("OriginalValues", 0),) 

155 newconst = Variable(**newconstd) 

156 substitutions[newconst] = val 

157 self._derelax_map[newconst.key] = const 

158 # add constraints so the newly freed's wiggle room 

159 # is proportional to the value relaxvar, and it can't antirelax 

160 relaxation_constraints[str(const)] = [relaxvar >= 1, 

161 newconst/relaxvar <= freed, 

162 freed <= newconst*relaxvar] 

163 ConstraintSet.__init__(self, { 

164 "original constraints": constraints, 

165 "relaxation constraints": relaxation_constraints}) 

166 self.relaxvars = NomialArray(relaxvars) # so they can be .prod()'d 

167 self.substitutions = substitutions 

168 self.constants = constants 

169 

170 def process_result(self, result): 

171 "Transfers the constant sensitivities back to the original constants" 

172 ConstraintSet.process_result(self, result) 

173 constant_senss = result["sensitivities"]["variables"] 

174 for new_constant, former_constant in self._derelax_map.items(): 

175 constant_senss[former_constant] = constant_senss[new_constant] 

176 del constant_senss[new_constant]