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"Implements Bounded" 

2from collections import defaultdict 

3import numpy as np 

4from .. import Variable 

5from .set import ConstraintSet 

6 

7 

8def varkey_bounds(varkeys, lower, upper): 

9 """Returns constraints list bounding all varkeys. 

10 

11 Arguments 

12 --------- 

13 varkeys : iterable 

14 list of varkeys to create bounds for 

15 

16 lower : float 

17 lower bound for all varkeys 

18 

19 upper : float 

20 upper bound for all varkeys 

21 """ 

22 constraints = [] 

23 for varkey in varkeys: 

24 variable = Variable(**varkey.descr) 

25 if variable.units: 

26 variable.hmap.units = None 

27 variable.units = None 

28 constraint = [] 

29 if lower: 

30 constraint.append(lower <= variable) 

31 if upper: 

32 constraint.append(variable <= upper) 

33 constraints.append(constraint) 

34 return constraints 

35 

36 

37class Bounded(ConstraintSet): 

38 """Bounds contained variables so as to ensure dual feasibility. 

39 

40 Arguments 

41 --------- 

42 constraints : iterable 

43 constraints whose varkeys will be bounded 

44 

45 verbosity : int (default 1) 

46 how detailed of a warning to print 

47 0: nothing 

48 1: print warnings 

49 

50 eps : float (default 1e-30) 

51 default lower bound is eps, upper bound is 1/eps 

52 

53 lower : float (default None) 

54 lower bound for all varkeys, replaces eps 

55 

56 upper : float (default None) 

57 upper bound for all varkeys, replaces 1/eps 

58 """ 

59 sens_threshold = 1e-7 

60 logtol_threshold = 3 

61 

62 def __init__(self, constraints, *, verbosity=1, 

63 eps=1e-30, lower=None, upper=None): 

64 if not isinstance(constraints, ConstraintSet): 

65 constraints = ConstraintSet(constraints) 

66 self.verbosity = verbosity 

67 self.lowerbound = lower if (lower or upper) else eps 

68 self.upperbound = upper if (lower or upper) else 1/eps 

69 constrained_varkeys = constraints.constrained_varkeys() 

70 self.bound_varkeys = frozenset(vk for vk in constrained_varkeys 

71 if vk not in constraints.substitutions) 

72 bounding_constraints = varkey_bounds(self.bound_varkeys, 

73 self.lowerbound, self.upperbound) 

74 super().__init__({"original constraints": constraints, 

75 "variable bounds": bounding_constraints}) 

76 

77 def process_result(self, result): 

78 "Add boundedness to the model's solution" 

79 ConstraintSet.process_result(self, result) 

80 if "boundedness" not in result: 

81 result["boundedness"] = {} 

82 result["boundedness"].update( 

83 self.check_boundaries(result, verbosity=self.verbosity)) 

84 

85 def check_boundaries(self, result, *, verbosity=0): 

86 "Creates (and potentially prints) a dictionary of unbounded variables." 

87 out = defaultdict(set) 

88 for i, varkey in enumerate(self.bound_varkeys): 

89 value = result["variables"][varkey] 

90 c_senss = [result["sensitivities"]["constraints"].get(c, 0) 

91 for c in self["variable bounds"][i]] 

92 if self.lowerbound: 

93 if c_senss[0] >= self.sens_threshold: 

94 out["sensitive to lower bound"].add(varkey) 

95 if np.log(value/self.lowerbound) <= self.logtol_threshold: 

96 out["value near lower bound"].add(varkey) 

97 if self.upperbound: 

98 if c_senss[-1] >= self.sens_threshold: 

99 out["sensitive to upper bound"].add(varkey) 

100 if np.log(self.upperbound/value) <= self.logtol_threshold: 

101 out["value near upper bound"].add(varkey) 

102 if verbosity > 0 and out: 

103 print("") 

104 print("Solves with these variables bounded:") 

105 for key, value in sorted(out.items()): 

106 print("% 25s: %s" % (key, ", ".join([str(v) for v in value]))) 

107 print("") 

108 return out