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

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

from __future__ import absolute_import 

from __future__ import division 

from builtins import range 

from builtins import object 

import numpy as np 

from gpkit import Variable, Monomial, Posynomial 

import random 

import math 

from copy import copy 

 

from .robust_gp_tools import RobustGPTools 

 

 

class TwoTermApproximation(object): 

""" 

replaces a large posynomial by a data-deprived large posynomial and a set of two term posynomials 

""" 

 

p = None 

number_of_monomials = None 

list_of_permutations = [] 

 

def __init__(self, p, setting): 

self.p = p 

self.number_of_monomials = len(self.p.exps) 

self.list_of_permutations = [] 

 

if not setting.get('boyd'): 

if setting.get('smartTwoTermChoose'): 

bad_relations, sizes = self.bad_relations(self.p) 

list_of_couples, new_list_to_permute = TwoTermApproximation. \ 

choose_convenient_couples(bad_relations, sizes, self.number_of_monomials) 

else: 

list_of_couples = [] 

new_list_to_permute = list(range(0, self.number_of_monomials)) 

 

first_elements = [] 

for couple in list_of_couples: 

first_elements += couple 

 

length_of_permutation = len(new_list_to_permute) 

max_num_of_perms = \ 

TwoTermApproximation.total_number_of_permutations(length_of_permutation) 

 

counter = 0 

number_of_permutations = min(setting.get('allowedNumOfPerms'), max_num_of_perms) 

while counter < number_of_permutations: 

temp = copy(new_list_to_permute) 

random.shuffle(temp) 

if TwoTermApproximation.check_if_permutation_exists(self.list_of_permutations, first_elements + temp): 

continue 

else: 

self.list_of_permutations.append(first_elements + temp) 

counter += 1 

 

@staticmethod 

def two_term_equivalent_posynomial(p, m, permutation, boyd): 

""" 

returns a two term posynomial equivalent to the original large posynomial 

:param p: the posynomial 

:param m: the index of the posynomial 

:param boyd: whether or not a boyd two term approximation is preferred 

:param permutation: the permutation to be used for two term approximation 

:return: the no data constraints and the data constraints 

""" 

monomials = p.chop() 

number_of_monomials = len(monomials) 

if number_of_monomials <= 2: 

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

 

data_constraints, no_data_constraints = [], [] 

 

if boyd: 

z_1 = Variable("z^1_(%s)" % m) 

data_constraints += [monomials[0] + z_1 <= 1] 

for i in range(number_of_monomials - 3): 

if i > 0: 

z_1 = Variable("z^%s_(%s)" % (i + 1, m)) 

z_2 = Variable("z^%s_(%s)" % (i + 2, m)) 

data_constraints += [monomials[i+1]/z_1 + z_2 / z_1 <= 1] 

z_2 = Variable("z^%s_(%s)" % (number_of_monomials - 2, m)) 

data_constraints += [ 

(monomials[number_of_monomials - 2] 

+ monomials[number_of_monomials - 1]) / z_2 <= 1] 

return [], data_constraints 

 

length_of_permutation = len(permutation) 

number_of_iterations = int(np.floor(length_of_permutation / 2.0)) 

 

zs = [] 

 

for j in range(number_of_iterations): 

z = Variable("z^%s_%s" % (j, m)) 

zs.append(z) 

data_constraints += [monomials[2*j] + monomials[2*j + 1] <= z] 

 

if length_of_permutation % 2 == 1: 

z = Variable("z^%s_%s" % (number_of_iterations, m)) 

zs.append(z) 

data_constraints += [monomials[permutation[length_of_permutation - 1]] <= z] 

 

no_data_constraints.append([sum(zs) <= 1]) 

 

return no_data_constraints, data_constraints 

 

@staticmethod 

def check_if_permutation_exists(permutations, permutation): 

""" 

Checks if a permutation already exists in a list of permutations 

:param permutations: the list of permutations 

:param permutation: the permutation to be checked 

:return: True or false 

""" 

if permutation in permutations: 

return True 

else: 

return False 

 

@staticmethod 

def n_choose_r(n, r): 

""" 

Combination formula 

:param n: the number of possibilities 

:param r: the numbers to choose from 

:return: the number of possible combinations 

""" 

f = math.factorial 

return f(n) / f(r) / f(n - r) 

 

@staticmethod 

def total_number_of_permutations(length_of_permutation): 

""" 

Finds the total number of possible "different" permutations 

:param length_of_permutation: the number of elements 

:return: the total number of permutations 

""" 

if length_of_permutation % 2 == 1: 

length_of_permutation += 1 

 

n = length_of_permutation 

prod = 1 

while n >= 4: 

prod *= TwoTermApproximation.n_choose_r(n, 2) 

n -= 2 

return prod / math.factorial(length_of_permutation / 2) 

 

@staticmethod 

def bad_relations(p): 

""" 

Investigates the relations between the monomials in a posynomial 

:param p: the posynomial 

:return: the dictionary of relations, and some other assisting dictionary 

""" 

number_of_monomials = len(p.exps) 

inverse_relations = {} 

sizes = {} 

for i in range(number_of_monomials): 

direct_vars_only_monomial_ith_exps = RobustGPTools.\ 

only_uncertain_vars_monomial(p.exps[i]) 

ith_monomial_exps = direct_vars_only_monomial_ith_exps 

m_uncertain_vars = [var for var in list(ith_monomial_exps.keys()) 

if RobustGPTools.is_directly_uncertain(var)] 

for j in range(0, number_of_monomials): 

direct_vars_only_monomial_jth_exps = RobustGPTools.\ 

only_uncertain_vars_monomial(p.exps[j]) 

jth_monomial_exps = direct_vars_only_monomial_jth_exps 

for var in m_uncertain_vars: 

if ith_monomial_exps.get(var.key, 0) * jth_monomial_exps.get(var.key, 0) < 0: 

if i in inverse_relations: 

if j in inverse_relations[i]: 

inverse_relations[i][j] += 1 

else: 

inverse_relations[i][j] = 1 

sizes[i] += 1 

else: 

inverse_relations[i] = {j: 1} 

sizes[i] = 1 

return inverse_relations, sizes 

 

@staticmethod 

def choose_convenient_couples(relations, sizes, number_of_monomials): 

""" 

Chooses which couples goes together for a two term approximation of a posynomial 

:param relations: the dictionary of relations 

:param sizes: some assisting dictionary 

:param number_of_monomials: the total number of monomials 

:return:the list of couples and the remaining monomials that need to be dealt with 

""" 

list_of_couples = [] 

to_permute = list(range(0, number_of_monomials)) 

while len(relations) > 0: 

vals_sizes = list(sizes.values()) 

keys_sizes = list(sizes.keys()) 

minimum_value_key = keys_sizes[vals_sizes.index(min(vals_sizes))] 

couple = [minimum_value_key] 

 

del to_permute[to_permute.index(minimum_value_key)] 

 

vals_relations_of_min = list(relations[minimum_value_key].values()) 

keys_relations_of_min = list(relations[minimum_value_key].keys()) 

maximum_of_min_value_key = keys_relations_of_min[vals_relations_of_min.index(max(vals_relations_of_min))] 

couple.append(maximum_of_min_value_key) 

 

del to_permute[to_permute.index(maximum_of_min_value_key)] 

 

del relations[minimum_value_key] 

del relations[maximum_of_min_value_key] 

del sizes[minimum_value_key] 

del sizes[maximum_of_min_value_key] 

 

for key in list(relations.keys()): 

if minimum_value_key in relations[key]: 

del relations[key][minimum_value_key] 

sizes[key] -= 1 

 

if maximum_of_min_value_key in relations[key]: 

del relations[key][maximum_of_min_value_key] 

sizes[key] -= 1 

 

if sizes[key] == 0: 

del sizes[key] 

del relations[key] 

 

list_of_couples.append(couple) 

 

return list_of_couples, to_permute 

 

def __repr__(self): 

return "TwoTermApproximation(" + self.p.__repr__() + ")" 

 

def __str__(self): 

return "TwoTermApproximation(" + self.p.__str__() + ")" 

 

if __name__ == '__main__': 

pass