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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

from __future__ import division 

from builtins import range 

from builtins import object 

import numpy as np 

import scipy.optimize as op 

import os 

from gpkit import Variable, Monomial, Posynomial 

 

 

class LinearizeTwoTermPosynomials(object): 

""" 

Linearizes two term posynomials 

""" 

 

def __init__(self, p): 

self.p = p 

 

@staticmethod 

def tangent_point_func(k, x, eps): 

""" 

the function used to calculate the tangent points 

:param k: the point of tangency 

:param x: the old intersection point 

:param eps: the error 

:return: the equation of the tangent line 

""" 

# warnings.simplefilter("ignore"): # this is making things slower 

return np.log(1 + np.exp(x)) - eps - np.log(1 + np.exp(k)) - np.exp(k) * (x - k) / (1 + np.exp(k)) 

 

@staticmethod 

def intersection_point_func(x, a, b, eps): 

""" 

the function used to calculate the intersection points 

:param x: the break point to be solved for 

:param a: the slope of the tangent line 

:param b: the intercept of the tangent line 

:param eps: the linearization error 

:return: the break point equation 

""" 

return a * x + b - np.log(1 + np.exp(x)) + eps 

 

@staticmethod 

def iterate_two_term_posynomial_linearization_coeff(r, eps): 

""" 

Finds the appropriate slopes, intercepts, tangency points, and intersection points for a given linearization 

error and number of piecewise linear sections 

:param r: the number of piecewise linear sections 

:param eps: linearization error 

:return: the slopes, intercepts, tangency points, and intersection points 

""" 

if r < 2: 

raise Exception('The number of piece-wise sections should two or larger') 

 

a, b = [], [] 

 

x_intersection = [] 

x_tangent = [] 

x_intersection.append(np.log(np.exp(eps) - 1)) 

 

i = 1 

while i < r - 1: 

x_old = x_intersection[i - 1] 

try: 

tangent_point = op.newton(LinearizeTwoTermPosynomials.tangent_point_func, x_old + 1, args=(x_old, eps)) 

slope = np.exp(tangent_point) / (1 + np.exp(tangent_point)) 

intercept = -np.exp(tangent_point) * tangent_point / (1 + np.exp(tangent_point)) + np.log( 

1 + np.exp(tangent_point)) 

intersection_point = op.newton(LinearizeTwoTermPosynomials.intersection_point_func, 

tangent_point + 1, args=(slope, intercept, eps)) 

except RuntimeError: 

return i, a, b, x_tangent, x_intersection 

 

x_tangent.append(tangent_point) 

a.append(slope) 

b.append(intercept) 

x_intersection.append(intersection_point) 

 

i += 1 

return r, a, b, x_tangent, x_intersection 

 

@staticmethod 

def compute_two_term_posynomial_linearization_coeff(r, tol): 

""" 

Calculates the slopes, intercepts, tangency points, intersection points, and linearization error for a given 

number of piecewise-linear sections 

 

:param r: the number of piecewise-linear sections 

:param tol: tolerance of the linearization parameters 

:return: slopes, intercepts, tangency points, intersection points, and linearization error 

""" 

a = None 

b = None 

x_tangent = None 

x_intersection = None 

 

eps = None 

eps_min = 0 

eps_max = np.log(2) 

delta = 100 

delta_old = 200 

 

while delta > tol and delta != delta_old: 

delta_old = delta 

eps = (eps_max + eps_min) / 2 

x_final_theoretical = -np.log(np.exp(eps) - 1) 

number_of_actual_r, a, b, x_tangent, x_intersection = \ 

LinearizeTwoTermPosynomials.iterate_two_term_posynomial_linearization_coeff(r, eps) 

 

x_final_actual = x_intersection[-1] 

 

if x_final_actual > x_final_theoretical or number_of_actual_r < r: 

eps_max = eps 

else: 

eps_min = eps 

delta = np.abs(x_final_actual - x_final_theoretical) 

return a, b, x_tangent, x_intersection, eps 

 

@staticmethod 

def two_term_posynomial_linearization_coeff(r): 

""" 

Reads the slopes, intercepts, tangency points, intersection points, and linearization error for a given number 

of piecewise-linear sections from a text file 

 

:param r: the number of piecewise-linear sections 

:return: slopes, intercepts, tangency points, intersection points, and linearization error 

""" 

if r < 2: 

raise Exception('The number of piece-wise sections should two or larger') 

 

if r < 100: 

linearization_data_file = open(os.path.dirname(__file__) + "/data/linearization_data.txt", "r") 

for _ in range(r-2): 

linearization_data_file.readline() 

line = linearization_data_file.readline() 

data = line.split(": ") 

slopes = data[0].split(", ")[0:-1] 

slopes = [float(item) for item in slopes] 

intercepts = data[1].split(", ")[0:-1] 

intercepts = [float(item) for item in intercepts] 

x_tangent = data[2].split(", ")[0:-1] 

x_tangent = [float(item) for item in x_tangent] 

x_intersection = data[3].split(", ")[0:-1] 

x_intersection = [float(item) for item in x_intersection] 

eps = float(data[4]) 

linearization_data_file.close() 

 

return slopes, intercepts, x_tangent, x_intersection, eps 

else: 

return LinearizeTwoTermPosynomials.compute_two_term_posynomial_linearization_coeff(r, 2*np.finfo(float).eps) 

 

def linearize_two_term_posynomial(self, m, r): 

""" 

Approximates a two term posynomial constraint by upper and lower piecewise-linear constraints 

 

:param m: the index of the constraint 

:param r: the number of piecewise-linear sections 

:return: the deprived of data upper and lower constraints and the common data containing constraints 

""" 

if r < 2: 

raise Exception('The number of piece-wise sections should be two or larger') 

 

if len(self.p.exps) > 2: 

raise Exception('The posynomial is larger than a two term posynomial') 

 

if len(self.p.exps) < 2: 

return [], [], [self.p <= 1] 

 

a, b, _, _, eps = LinearizeTwoTermPosynomials.two_term_posynomial_linearization_coeff(r) 

 

data_constraints = [] 

 

w = Variable('w_%s' % m) 

no_data_constraints_upper = [w * np.exp(eps) <= 1] 

no_data_constraints_lower = [w <= 1] 

 

first_monomial, second_monomial = self.p.chop() 

data_constraints += [first_monomial <= w] 

 

for i in range(r - 2): 

data_constraints += [first_monomial ** a[r - 3 - i] * 

second_monomial ** a[i] * np.exp(b[i]) <= w] 

 

data_constraints += [second_monomial <= w] 

return no_data_constraints_upper, no_data_constraints_lower, data_constraints 

 

if __name__ == '__main__': 

pass