from interval import interval
DISTURBANCE_INTERVAL = interval[0, 0] # interval[-0.01, 0.01]
# Start helper functions
[docs]
def convert_1D_list_to_intervals(list_1D):
new_list_1D = []
for element in list_1D:
new_list_1D.append(interval([element, element]))
return new_list_1D
[docs]
def convert_2D_list_to_intervals(list_2D):
new_list_2D = []
for vector in list_2D:
new_vector = []
for element in vector:
new_vector.append(interval([element, element]))
new_list_2D.append(new_vector)
return new_list_2D
[docs]
def replace_safe_state_intervals_with_saved_value_intervals(x_interval, x_saved_interval, unsafeSensors_oneHot):
for i, unsafe in enumerate(unsafeSensors_oneHot):
if not unsafe:
x_interval[i] = x_saved_interval[i]
return x_interval
# Helper functions for operations on lists of intervals
def __mul__(list_of_intervals, constant):
return [Interval*constant for Interval in list_of_intervals]
def __add__(list_of_intervals, another_list_of_intervals):
assert len(list_of_intervals)==len(another_list_of_intervals), "You can't add 2 lists (of intervals) with different number of elements"
new_list = []
for i in range(len(list_of_intervals)):
new_list.append(list_of_intervals[i]+another_list_of_intervals[i])
return new_list
def __sum__(list_of_list_of_intervals):
summed_list = list_of_list_of_intervals[0]
for list_of_intervals in list_of_list_of_intervals[1:]:
for i, interval in enumerate(list_of_intervals):
summed_list[i] += interval
return summed_list
def __add_to_every_element__(list_of_intervals, another_interval):
return [Interval+another_interval for Interval in list_of_intervals]
# Helper functions for numerical integration
[docs]
def RungeKutta_Integration(dynamics, x_interval, u_interval, step_size):
x = x_interval
u = u_interval
t = None
k_1 = dynamics(t, x, u)
k_2 = dynamics(t, __add__(x, __mul__(k_1, step_size/2.0)) , u)
k_3 = dynamics(t, __add__(x, __mul__(k_2, step_size/2.0)) , u)
k_4 = dynamics(t, __add__(x, __mul__(k_3, step_size)) , u)
x_next = __add__(x, __sum__( [__mul__(k_1, step_size/6.0), __mul__(k_2, 2.0*step_size/6.0), __mul__(k_3, 2.0*step_size/6.0), __mul__(k_4, step_size/6.0)] ))
return __add_to_every_element__(x_next, DISTURBANCE_INTERVAL)
[docs]
def Newton_Integration(dynamics, x_interval, u_interval, step_size):
x = x_interval
u = u_interval
t = None
x_dot = dynamics(t, x, u)
x_next = __add__(x, __mul__(x_dot, step_size))
return __add_to_every_element__(x_next, DISTURBANCE_INTERVAL)
# Helper functions for midpoint and intersection
[docs]
def isIntersecting(Int1, Int2):
if len(Int1 & Int2)==0:
return False
else:
return True
[docs]
def midpoint(Int):
lower_bound = Int[0][0]
upper_bound = Int[0][1]
return (lower_bound+upper_bound)/2.0
# reachability
[docs]
def reach(dynamics, x0_interval, Us_saved, Xs_saved, unsafeSensors_oneHot, step_size):
if Xs_saved:
assert len(Us_saved) == len(Xs_saved)-1, "There should be one less u vector than saved x vectors! ("
total_steps = len(Us_saved)
x_interval = x0_interval
if Xs_saved:
Xs_saved_interval = convert_2D_list_to_intervals(Xs_saved)
Us_saved_interval = convert_2D_list_to_intervals(Us_saved)
for i in range(total_steps):
if Xs_saved:
x_interval = replace_safe_state_intervals_with_saved_value_intervals(x_interval, Xs_saved_interval[i], unsafeSensors_oneHot)
u_interval = Us_saved_interval[i]
# x_interval = Newton_Integration(dynamics, x_interval, u_interval, step_size)
x_interval = RungeKutta_Integration(dynamics, x_interval, u_interval, step_size)
#print(x_interval)
return x_interval
if __name__ == '__main__':
import sys
sys.path.append('../../../')
from cpsim.models.nonlinear.continuous_stirred_tank_reactor import cstr_imath as cstr
# This code won't run if this file is imported.
# print(interval([1,1]) == interval([1])) # True
x0_interval = [interval[1,1], interval[3,3]]
#print(x0_interval)
Us_saved = [ [0], [0.1], [1]]
Xs_saved = [ [4,5], [6,7], [8,9], [10,11]]
unsafeSensors_oneHot = [0,1]
step_size = 0.1
out = reach(cstr, x0_interval, Us_saved, Xs_saved, unsafeSensors_oneHot, step_size)
print(f'{out=}')
print(f'{midpoint(interval[5,9])=}')