Skip to content

Commit 10c0e03

Browse files
committed
Test PiecewiseNonlinearAdditiveNoiseDynamics
1 parent 0016acf commit 10c0e03

File tree

3 files changed

+146
-11
lines changed

3 files changed

+146
-11
lines changed

src/dynamics/PiecewiseNonlinearAdditiveNoiseDynamics.jl

+39-11
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,23 @@ export PiecewiseNonlinearAdditiveNoiseDynamics, NonlinearDynamicsRegion
55
66
A struct representing a non-linear dynamics, valid over a region.
77
"""
8-
struct NonlinearDynamicsRegion{F<:Function, S<:LazySet}
8+
struct NonlinearDynamicsRegion{F<:Function,S<:LazySet}
99
f::F
1010
region::S
1111
end
1212

13-
function (dyn::NonlinearDynamicsRegion)(X::Hyperrectangle{Float64}, U::Hyperrectangle{Float64})
13+
function (dyn::NonlinearDynamicsRegion)(
14+
X::Hyperrectangle{Float64},
15+
U::Hyperrectangle{Float64},
16+
)
1417
# Use the Taylor model to over-approximate the reachable set
1518
active_region = intersection(X, dyn.region)
1619
if isempty(active_region)
17-
throw(ArgumentError("The input region X does not intersect with the valid region of the dynamics"))
20+
throw(
21+
ArgumentError(
22+
"The input region X does not intersect with the valid region of the dynamics",
23+
),
24+
)
1825
end
1926
active_region_box = box_approximation(active_region)
2027

@@ -28,7 +35,10 @@ function (dyn::NonlinearDynamicsRegion)(X::Hyperrectangle{Float64}, U::Hyperrect
2835
# set_variables(Float64, "z"; order=order, numvars=LazySets.dim(X) + LazySets.dim(U))
2936

3037
order = 1
31-
z = [TaylorModelN(i, order, IntervalBox(z0), dom) for i = 1:LazySets.dim(X)+LazySets.dim(U)]
38+
z = [
39+
TaylorModelN(i, order, IntervalBox(z0), dom) for
40+
i = 1:LazySets.dim(X)+LazySets.dim(U)
41+
]
3242
x, u = (z-z0)[1:LazySets.dim(X)], z[LazySets.dim(X)+1:end]
3343

3444
# Perform the Taylor expansion
@@ -57,7 +67,11 @@ function (dyn::NonlinearDynamicsRegion)(X::Hyperrectangle{Float64}, U::Hyperrect
5767
Dupper = sup.(D)
5868

5969
Y1 = Alow * Translation(active_region, -x0) + Blow * Translation(U, -u0) + Clow + Dlower
60-
Y2 = Aupper * Translation(active_region, -x0) + Bupper * Translation(U, -u0) + Cupper + Dupper
70+
Y2 =
71+
Aupper * Translation(active_region, -x0) +
72+
Bupper * Translation(U, -u0) +
73+
Cupper +
74+
Dupper
6175

6276
Yconv = ConvexHull(Y1, Y2)
6377

@@ -69,12 +83,19 @@ function (dyn::NonlinearDynamicsRegion)(X::Hyperrectangle{Float64}, U::Singleton
6983
return dyn(X, element(U))
7084
end
7185

72-
function (dyn::NonlinearDynamicsRegion)(X::Hyperrectangle{Float64}, u::AbstractVector{Float64})
86+
function (dyn::NonlinearDynamicsRegion)(
87+
X::Hyperrectangle{Float64},
88+
u::AbstractVector{Float64},
89+
)
7390
# Use the Taylor model to over-approximate the reachable set
7491

7592
active_region = intersection(X, dyn.region)
7693
if isempty(active_region)
77-
throw(ArgumentError("The input region X does not intersect with the valid region of the dynamics"))
94+
throw(
95+
ArgumentError(
96+
"The input region X does not intersect with the valid region of the dynamics",
97+
),
98+
)
7899
end
79100
active_region_box = box_approximation(active_region)
80101

@@ -123,9 +144,16 @@ function (dyn::NonlinearDynamicsRegion)(X::Singleton{Float64}, U::Singleton{Floa
123144
return Singleton(y)
124145
end
125146

126-
function (dyn::NonlinearDynamicsRegion)(x::AbstractVector{Float64}, u::AbstractVector{Float64})
147+
function (dyn::NonlinearDynamicsRegion)(
148+
x::AbstractVector{Float64},
149+
u::AbstractVector{Float64},
150+
)
127151
if x dyn.region
128-
throw(ArgumentError("The input x does not belong to the valid region of the dynamics"))
152+
throw(
153+
ArgumentError(
154+
"The input x does not belong to the valid region of the dynamics",
155+
),
156+
)
129157
end
130158

131159
return dyn.f(x, u)
@@ -220,7 +248,7 @@ function nominal(
220248
dyn::PiecewiseNonlinearAdditiveNoiseDynamics,
221249
x::AbstractVector{Float64},
222250
u,
223-
)
251+
)
224252
for region in dyn.regions
225253
if x region.region
226254
return region(x, u)
@@ -243,4 +271,4 @@ function prepare_nominal(dyn::PiecewiseNonlinearAdditiveNoiseDynamics, input_abs
243271
set_variables(Float64, "z"; order = 2, numvars = n)
244272

245273
return nothing
246-
end
274+
end

test/dynamics/dynamics.jl

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ test_files = [
66
"uncertain_pwa.jl",
77
"gaussian_process.jl",
88
"stochastic_switched.jl",
9+
"piecewise_nonlinear.jl",
910
]
1011
for f in test_files
1112
@testset "dynamics/$f" include(f)

test/dynamics/piecewise_nonlinear.jl

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using Revise, Test
2+
using IntervalMDPAbstractions, LazySets
3+
4+
τ = 0.1
5+
6+
region1 = Hyperrectangle(low = [-1.0, -1.0], high = [0.0, 1.0])
7+
f(x, u) = [x[1] + x[2] * τ, x[2] + (-x[1] + (1 - x[1])^2 * x[2]) * τ]
8+
dyn_reg1 = NonlinearDynamicsRegion(f, region1)
9+
10+
region2 = Hyperrectangle(low = [0.0, -1.0], high = [1.0, 1.0])
11+
g(x, u) = [x[1] + x[2] * τ, x[2] + (-x[2] + (1 - x[2])^2 * x[1]) * τ]
12+
dyn_reg2 = NonlinearDynamicsRegion(g, region2)
13+
14+
w_stddev = [0.1, 0.1]
15+
w = AdditiveDiagonalGaussianNoise(w_stddev)
16+
17+
dyn = PiecewiseNonlinearAdditiveNoiseDynamics([dyn_reg1, dyn_reg2], 2, 0, w)
18+
19+
prepare_nominal(dyn, InputDiscrete([0.0]))
20+
21+
X = Hyperrectangle(low = [-1.0, 0.0], high = [1.0, 1.0])
22+
U = Singleton([0.0])
23+
24+
Y = concretize(nominal(dyn, X, U))
25+
26+
# First piecewise region
27+
X1 = Hyperrectangle(low = [-1.0, 0.0], high = [0.0, 1.0])
28+
29+
# 1st-order Taylor expansion at [center(X1)] = [-0.5; 0.5]:
30+
# z = x - center(X1)
31+
# y₁ = z₁ + 0.1 * z₂ - 0.45
32+
# y₂ = -0.25 * z₁ + 1.2245 * z₂ + 0.6625 + [-0.075, 0.1]
33+
X1centered = Translation(X1, -[-0.5, 0.5])
34+
AXD1 = AffineMap([1.0 0.1; -0.25 1.225], X1centered, [-0.45, 0.6625])
35+
Y1_expected = MinkowskiSum(AXD1, Hyperrectangle(low = [0.0, -0.075], high = [0.0, 0.1]))
36+
37+
# First piecewise region
38+
X2 = Hyperrectangle(low = [0.0, 0.0], high = [1.0, 1.0])
39+
40+
# 1st-order Taylor expansion at [center(X2)] = [0.5; 0.5]:
41+
# z = x - center(X2)
42+
# y₁ = z₁ + 0.1 * z₂ + 0.55
43+
# y₂ = 0.025 * z₁ + 0.85 * z₂ + 0.4625 + [-0.025, 0.05]
44+
X2centered = Translation(X2, -[0.5, 0.5])
45+
AXD2 = AffineMap([1.0 0.1; 0.025 0.85], X2centered, [0.55, 0.4625])
46+
Y2_expected = MinkowskiSum(AXD2, Hyperrectangle(low = [0.0, -0.025], high = [0.0, 0.05]))
47+
48+
Y_expected = concretize(ConvexHull(Y1_expected, Y2_expected))
49+
@test isequivalent(Y, Y_expected)
50+
51+
52+
#### Region-based actions
53+
region2 = Hyperrectangle(low = [0.0, -1.0], high = [1.0, 1.0])
54+
h(x, u) = [x[1] + x[2] * τ, x[2] + (-x[2] + (1 - x[2])^2 * x[1] + u[1]) * τ]
55+
dyn_reg2 = NonlinearDynamicsRegion(h, region2)
56+
57+
w_stddev = [0.1, 0.1]
58+
w = AdditiveDiagonalGaussianNoise(w_stddev)
59+
60+
dyn = PiecewiseNonlinearAdditiveNoiseDynamics([dyn_reg1, dyn_reg2], 2, 1, w)
61+
62+
input_abs = InputGridSplit(Hyperrectangle(low = [-1.0], high = [1.0]), (3,))
63+
prepare_nominal(dyn, input_abs)
64+
65+
X = Hyperrectangle(low = [-1.0, 0.0], high = [1.0, 1.0])
66+
U = Hyperrectangle(low = [-1.0], high = [-1 / 3])
67+
68+
Y = concretize(nominal(dyn, X, U))
69+
70+
# First piecewise region
71+
X1 = Hyperrectangle(low = [-1.0, 0.0], high = [0.0, 1.0])
72+
73+
# 1st-order Taylor expansion at [center(X1)] = [-0.5; 0.5]:
74+
# z = x - center(X1)
75+
# y₁ = z₁ + 0.1 * z₂
76+
# y₂ = -0.1 * z₁ + 1.1 * z₂ + 0.6625 + [-0.075, 0.1]
77+
X1centered = Translation(X1, -[-0.5, 0.5])
78+
AXD1 = LinearMap([1.0 0.1; -0.1 1.1], X1centered)
79+
Y1_expected =
80+
MinkowskiSum(AXD1, Hyperrectangle(; low = [0.0, -0.0625], high = [0.0, 0.0625]))
81+
82+
# First piecewise region
83+
X2 = Hyperrectangle(low = [0.0, 0.0], high = [1.0, 1.0])
84+
85+
# 1st-order Taylor expansion at [center(X2)] = [0.5; 0.5]:
86+
# z = x - center(X2)
87+
# y₁ = z₁ + 0.1 * z₂
88+
# y₂ = 0.1 * z₁ + 0.9 * z₂ + 0.1 * u₁ - 1/15 + [-0.0625, 0.0625]
89+
X2centered = Translation(X2, -[0.5, 0.5])
90+
AXD2 = AffineMap([1.0 0.1; 0.1 0.9], X2centered, [0.0, -1 / 15])
91+
U = Hyperrectangle(low = [-1.0], high = [-1 / 3])
92+
Ucentered = Translation(U, -center(U))
93+
AXDBU2 = MinkowskiSum(AXD2, LinearMap([0.0; 0.1], Ucentered))
94+
Y2_expected =
95+
MinkowskiSum(AXDBU2, Hyperrectangle(low = [0.0, -0.0625], high = [0.0, 0.0625]))
96+
97+
Y_expected = concretize(ConvexHull(Y1_expected, Y2_expected))
98+
@test isequivalent(Y, Y_expected)
99+
100+
# Vector inputs
101+
x = [0.5, 0.5]
102+
u = [0.0]
103+
104+
y = nominal(dyn, x, u)
105+
y_expected = g(x, u)
106+
@test y y_expected

0 commit comments

Comments
 (0)