-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget_bounds.py
263 lines (248 loc) · 8.49 KB
/
get_bounds.py
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
import math
# Author: Parul
# get confidence bounds from operators
def isMod(element):
"""
A utility function to check if 'element' is mod function
:param element: expr_tree node
:return: bool
"""
if element == "abs":
return True
return False
def eval_math_bound(l_x, u_x, l_y=None, u_y=None, operator=None):
if operator == '+':
return eval_add_bound ( l_x, u_x, l_y, u_y )
elif operator == '-':
return eval_subtract_bound ( l_x, u_x, l_y, u_y )
elif operator == '*':
return eval_multiply_bound ( l_x, u_x, l_y, u_y )
elif operator == '^':
# power is not supported as of now
return l_x, u_x
elif operator == '/':
return eval_div_bound ( l_x, u_x, l_y, u_y )
elif isMod ( operator ):
return eval_abs_bound ( l_x, u_x )
return None, None
def eval_abs_bound(l_x, u_x):
"""
:param l_x: lower bound
:param u_x: upper bound
:return: lower and upper bound of abs operation
"""
if l_x is not None and u_x is not None:
if l_x == math.inf or u_x == math.inf or l_x == -math.inf or u_x == math.inf:
return 0, math.inf
elif l_x <= 0 and u_x <= 0:
return -u_x, -l_x
elif l_x >= 0 and u_x >= 0:
return l_x, u_x
elif l_x <= 0 <= u_x:
return 0, max ( -l_x, u_x )
return None, None
def eval_div_bound(l_x, u_x, l_y, u_y):
"""
:param l_x: lower bound of left child
:param u_x: upper bound of left child
:param l_y: lower bound of right child
:param u_y: upper bound of right child
:return: lower and upper bound of div operation
"""
if l_x is not None and u_x is not None and l_y is not None and u_y is not None:
if (l_x == -math.inf and u_x == math.inf) or l_y <= 0 <= u_y:
# x is unbounded or 0 in y
return -math.inf, math.inf
elif l_x >= 0 and l_y >= 0:
# both x, y are positive
if u_y == math.inf:
lower = 0
else:
lower = l_x / u_y
if u_x == math.inf:
upper = math.inf
else:
upper = u_x / l_y
return lower, upper
elif u_x <= 0 and u_y <= 0:
# both x, y are negative
if l_y == -math.inf:
lower = 0
else:
lower = u_x / l_y
if l_x == -math.inf:
upper = math.inf
else:
upper = l_x / u_y
return lower, upper
elif l_x >= 0 >= u_y:
# x is positive and y is negative
if u_x == math.inf:
lower = -math.inf
else:
lower = l_x / u_y
if l_y == -math.inf:
upper = 0
else:
upper = l_x / l_y
return lower, upper
elif u_x <= 0 <= l_y:
# x is negative and y is positive
if l_x == -math.inf:
lower = -math.inf
else:
lower = l_x / l_y
if u_y == math.inf:
upper = 0
else:
upper = u_x / u_y
return lower, upper
elif l_x <= 0 <= u_x and l_y >= 0:
# 0 in x and y is positive
if l_x == -math.inf:
lower = -math.inf
else:
lower = l_x / l_y
if u_x == math.inf:
upper = math.inf
else:
upper = u_x / l_y
return lower, upper
elif l_x <= 0 <= u_x and u_y <= 0:
# 0 in x and y is negative
if u_x == math.inf:
lower = -math.inf
else:
lower = u_x / u_y
if l_x == -math.inf:
upper = math.inf
else:
upper = l_x / u_y
return lower, upper
return None, None
def eval_multiply_bound(l_x, u_x, l_y, u_y):
"""
:param l_x: lower bound of left child
:param u_x: upper bound of left child
:param l_y: lower bound of right child
:param u_y: upper bound of right child
:return: lower and upper bound of multiply operation
"""
if l_x is not None and u_x is not None and l_y is not None and u_y is not None:
if (l_x == -math.inf and u_x == math.inf) or (l_y == -math.inf and u_y == math.inf):
# one of x, y is unbounded
return -math.inf, math.inf
elif l_x >= 0 and l_y >= 0:
# both x, y are positive
if u_x == math.inf or u_y == math.inf:
return l_x * l_y, math.inf
return l_x * l_y, u_x * u_y
elif u_x <= 0 and u_y <= 0:
# both x, y are negative
if l_x == -math.inf or l_y == -math.inf:
return u_x * u_y, math.inf
return u_x * u_y, l_x * l_y
elif l_x >= 0 >= u_y:
# x is positive and y is negative
if u_x == math.inf or l_y == -math.inf:
return -math.inf, l_x * u_y
return u_x * l_y, l_x * u_y
elif u_x <= 0 <= l_y:
# x is negative and y is positive
if l_x == -math.inf or u_y == math.inf:
return -math.inf, u_x * l_y
return l_x * u_y, u_x * l_y
elif l_x <= 0 <= u_x and l_y >= 0:
# 0 in x and y is positive
if l_x == -math.inf or u_y == math.inf:
lower = -math.inf
else:
lower = l_x * u_y
if u_x == math.inf or u_y == math.inf:
upper = math.inf
else:
upper = u_x * u_y
return lower, upper
elif l_x <= 0 <= u_x and u_y <= 0:
# 0 in x and y is negative
if u_x == math.inf or l_y == -math.inf:
lower = -math.inf
else:
lower = u_x * l_y
if l_x == -math.inf or l_y == -math.inf:
upper = math.inf
else:
upper = l_x * l_y
return lower, upper
elif l_x >= 0 and l_y <= 0 <= u_y:
# 0 in y and x is positive
if u_x == math.inf or l_y == -math.inf:
lower = -math.inf
else:
lower = u_x * l_y
if u_x == math.inf or u_y == math.inf:
upper = math.inf
else:
upper = u_x * u_y
return lower, upper
elif u_x <= 0 and l_y <= 0 <= u_y:
# 0 in y and x is negative
if l_x == -math.inf or u_y == math.inf:
lower = -math.inf
else:
lower = l_x * u_y
if l_x == -math.inf or l_y == -math.inf:
upper = math.inf
else:
upper = l_x * l_y
return lower, upper
elif l_x <= 0 <= u_x and l_y <= 0 <= u_y:
# 0 in x and 0 in y
if l_x == -math.inf or l_y == -math.inf or u_x == math.inf or u_y == math.inf:
# unbounded
return -math.inf, math.inf
else:
return min ( l_x * u_y, u_x * l_y ), max ( u_x * u_y, l_x * l_y )
return None, None
def eval_subtract_bound(l_x, u_x, l_y, u_y):
"""
:param l_x: lower bound of left child
:param u_x: upper bound of left child
:param l_y: lower bound of right child
:param u_y: upper bound of right child
:return: lower and upper bound of subtract operation
"""
if l_x is not None and u_x is not None and l_y is not None and u_y is not None:
# lower bound
if l_x == -math.inf or u_y == math.inf:
lower = -math.inf
else:
lower = l_x - u_y
# upper bound
if u_x == math.inf or l_y == -math.inf:
upper = math.inf
else:
upper = u_x - l_y
return lower, upper
return None, None
def eval_add_bound(l_x, u_x, l_y, u_y):
"""
:param l_x: lower bound of left child
:param u_x: upper bound of left child
:param l_y: lower bound of right child
:param u_y: upper bound of right child
:return: lower and upper bound of add operation
"""
if l_x is not None and u_x is not None and l_y is not None and u_y is not None:
# lower bound
if l_x == -math.inf or l_y == -math.inf:
lower = -math.inf
else:
lower = l_x + l_y
# upper bound
if u_x == math.inf or u_y == math.inf:
upper = math.inf
else:
upper = u_x + u_y
return lower, upper
return None, None