Skip to content

Commit 85e8b86

Browse files
committed
Formatted and fixed doctests
1 parent 7c03de1 commit 85e8b86

File tree

12 files changed

+278
-5
lines changed

12 files changed

+278
-5
lines changed

docs/make.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ makedocs(; sitename="LazySets.jl",
8888
"VPolygon" => "lib/sets/VPolygon.md",
8989
"VPolytope" => "lib/sets/VPolytope.md",
9090
"ZeroSet" => "lib/sets/ZeroSet.md",
91-
"Zonotope" => "lib/sets/Zonotope.md"
91+
"Zonotope" => "lib/sets/Zonotope.md",
92+
"ZonotopeMD" => "lib/sets/ZonotopeMD.md"
9293
#
9394
],
9495
"Lazy Operations" => [

docs/src/lib/sets/ZonotopeMD.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
```@meta
2+
CurrentModule = LazySets.ZonotopeModule
3+
```
4+
5+
# [ZonotopeMD](@id def_ZonotopeMD)
6+
7+
```@docs
8+
ZonotopeMD
9+
```
10+
11+
## Operations
12+
13+
```@docs
14+
genmat(::ZonotopeMD)
15+
cartesian_product(::ZonotopeMD, ::ZonotopeMD)
16+
center(::ZonotopeMD)
17+
ngens(::ZonotopeMD)
18+
```

src/LazySets.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ include("Sets/Zonotope/ZonotopeModule.jl")
218218
@reexport using ..ZonotopeModule: Zonotope,
219219
remove_zero_generators,
220220
linear_map!,
221-
split!
221+
split!,
222+
ZonotopeMD
222223
using ..ZonotopeModule: _split
223224

224225
include("LazyOperations/UnionSet.jl") # must come before IntervalModule
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""
2+
struct ZonotopeMD{N, VN<:AbstractVector{N}, MN<:AbstractMatrix{N}, DN<:AbstractVector{N}} <: AbstractZonotope{N}
3+
4+
Type that represents a zonotope of order `k` in normal form.
5+
6+
### Fields
7+
8+
- `center::VN` — the center of the zonotope
9+
- `M::MN` — matrix of general (non-axis-aligned) generators
10+
- `d::DN` — vector of axis-aligned (diagonal) generators
11+
12+
### Notes
13+
A zonotope is of order `k` if it has `n * k` generators in `ℝⁿ`, where `n` is the ambient dimension.
14+
15+
A zonotope of order `k` in *normal form* is defined as the set
16+
17+
```math
18+
Z = \\left\\{ x ∈ ℝ^n : x = c + Mξ + d ⊙ η, ~~ ξ ∈ [-1, 1]^m, ~~ η ∈ [-1, 1]^n \\right\\},
19+
```
20+
21+
where `M ∈ ℝ^{n×m}` is a matrix of general generators with `m = n*(k -1)` and `d ∈ ℝⁿ` is a vector of axis-aligned generators.
22+
Equivalently, this can be seen as a zonotope with generator matrix `[M D]`, where `D` is the diagonal matrix
23+
formed from the vector `d`.
24+
ZonotopeMD can be constructed in two ways: by passing the full generator matrix `[M D]` in normal form
25+
or by passing `M` and a vector `d` separately.
26+
27+
### Examples
28+
29+
Constructing a zonotope in normal form from a center, general generator matrix `M`, and diagonal vector `d`:
30+
31+
```jldoctest zonotopemd_label
32+
julia> c = [0.0, 0.0];
33+
34+
julia> M = [1.0 2.0; 3.0 1.0];
35+
36+
julia> d = [0.1, 0.2];
37+
38+
julia> Z = ZonotopeMD(c, M, d)
39+
ZonotopeMD{Float64, Vector{Float64}, Matrix{Float64}, Vector{Float64}}([0.0, 0.0], [1.0 2.0; 3.0 1.0], [0.1, 0.2])
40+
41+
julia> center(Z)
42+
2-element Vector{Float64}:
43+
0.0
44+
0.0
45+
46+
julia> genmat(Z)
47+
2×4 SparseArrays.SparseMatrixCSC{Float64, Int64} with 6 stored entries:
48+
1.0 2.0 0.1 ⋅
49+
3.0 1.0 ⋅ 0.1
50+
```
51+
52+
The generator matrix returned by `genmat` is the concatenation `[M D]`, where `D` is the diagonal matrix formed from `d`.
53+
THe resulting matrix is stored as a sparse matrix.
54+
Constructing the same zonotope by passing the full generator matrix `[M D]` directly:
55+
56+
```jldoctest zonotopemd_label
57+
julia> G = [1.0 2.0 0.1 0.0;
58+
3.0 1.0 0.0 0.2];
59+
60+
julia> Z2 = ZonotopeMD([0.0, 0.0], G)
61+
ZonotopeMD{Float64, Vector{Float64}, Matrix{Float64}, Vector{Float64}}([0.0, 0.0], [1.0 2.0; 3.0 1.0], [0.1, 0.2])
62+
63+
julia> genmat(Z2) == G
64+
true
65+
```
66+
You can also convert back to a standard `Zonotope` if needed:
67+
68+
```jldoctest zonotopemd_label
69+
julia> Zstd = Zonotope(Z)
70+
Zonotope{Float64, Vector{Float64}, SparseArrays.SparseMatrixCSC{Float64, Int64}}([0.0, 0.0], sparse([1, 2, 1, 2, 1, 2], [1, 1, 2, 2, 3, 4], [1.0, 3.0, 2.0, 1.0, 0.1, 0.2], 2, 4))
71+
```
72+
73+
"""
74+
struct ZonotopeMD{N,VN<:AbstractVector{N},MN<:AbstractMatrix{N},DN<:AbstractVector{N}} <:
75+
AbstractZonotope{N}
76+
center::VN
77+
M::MN
78+
d::DN
79+
80+
function ZonotopeMD(center::VN, M::MN,
81+
d::DN) where {N,
82+
VN<:AbstractVector{N},
83+
MN<:AbstractMatrix{N},
84+
DN<:AbstractVector{N}}
85+
@assert length(center) == size(M, 1) == length(d) "Dimensions must match"
86+
return new{N,VN,MN,DN}(center, M, d)
87+
end
88+
end
89+
90+
# constructor from generator matrix
91+
function ZonotopeMD(center::VN, G::AbstractMatrix{N}) where {N,VN<:AbstractVector{N}}
92+
n, p = size(G)
93+
@assert p % n == 0 "The generator matrix must contain a multiple of n columns"
94+
@assert p >= 2n "Expected at least order 2 zonotope"
95+
96+
M = G[:, 1:(p - n)]
97+
D = G[:, (end - n + 1):end]
98+
99+
@assert isdiag(D) "The last n columns of the generator matrix must be diagonal"
100+
d = diag(D)
101+
return ZonotopeMD(center, M, d)
102+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
"""
3+
cartesian_product(Z1::ZonotopeMD, Z2::ZonotopeMD)
4+
5+
Return the Cartesian product of two zonotopes in normal form (`ZonotopeMD`).
6+
7+
### Input
8+
9+
- `Z1`, `Z2` -- zonotopes in normal form (`ZonotopeMD`)
10+
11+
### Output
12+
13+
A new `ZonotopeMD` representing the Cartesian product `Z1 × Z2`.
14+
"""
15+
function cartesian_product(Z1::ZonotopeMD, Z2::ZonotopeMD)
16+
c = vcat(Z1.center, Z2.center)
17+
d = vcat(Z1.d, Z2.d)
18+
M = blockdiag(sparse(Z1.M), sparse(Z2.M))
19+
return ZonotopeMD(c, M, d)
20+
end
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function center(Z::ZonotopeMD)
2+
return Z.center
3+
end
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Zonotope(Z::ZonotopeMD) = convert(Zonotope, Z)
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
genmat(Z::ZonotopeMD)
3+
4+
Return the generator matrix of a ZonotopeMD.
5+
6+
### Input
7+
8+
- `Z` -- zonotope in normal form
9+
10+
### Output
11+
12+
A matrix where each column represents one generator of the zonotope `Z`.
13+
"""
14+
function genmat(Z::ZonotopeMD)
15+
D = spdiagm(0 => Z.d)
16+
return hcat(Z.M, D)
17+
end

src/Sets/Zonotope/ZonotopeMD/ngens.jl

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ngens(Z::ZonotopeMD) = size(Z.M, 2) + length(Z.d)

src/Sets/Zonotope/ZonotopeModule.jl

+13-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ module ZonotopeModule
33
using Reexport, Requires
44

55
using ..LazySets: AbstractZonotope, generators_fallback, _scale_copy_inplace
6-
using LinearAlgebra: mul!
6+
using LinearAlgebra: mul!, Diagonal, isdiag, diag
7+
using SparseArrays: blockdiag, sparse, spdiagm
78
using Random: AbstractRNG, GLOBAL_RNG
89
using ReachabilityBase.Arrays: ismultiple, remove_zero_columns, to_matrix,
910
vector_type
1011
using ReachabilityBase.Distribution: reseed!
1112
using ReachabilityBase.Require: require
1213

1314
@reexport import ..API: center, high, isoperationtype, low, rand,
14-
permute, scale, scale!, translate!
15+
permute, scale, scale!, translate!, cartesian_product
1516
@reexport import ..LazySets: generators, genmat, ngens, reduce_order,
1617
remove_redundant_generators, togrep
1718
import Base: convert
@@ -20,7 +21,8 @@ import Base: convert
2021
export Zonotope,
2122
remove_zero_generators,
2223
linear_map!,
23-
split!
24+
split!,
25+
ZonotopeMD
2426

2527
include("Zonotope.jl")
2628

@@ -47,4 +49,12 @@ include("convert.jl")
4749

4850
include("init.jl")
4951

52+
#ZonotopeMD
53+
include("ZonotopeMD/ZonotopeMD.jl")
54+
include("ZonotopeMD/convert.jl")
55+
include("ZonotopeMD/center.jl")
56+
include("ZonotopeMD/genmat.jl")
57+
include("ZonotopeMD/cartesian_product.jl")
58+
include("ZonotopeMD/ngens.jl")
59+
5060
end # module

test/Sets/ZonotopeMD.jl

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
for N in [Float64, Float32, Rational{Int}]
2+
# For order 2:
3+
# center ∈ ℝ²,
4+
# M is 2×2 and d ∈ ℝ²
5+
6+
# Direct construction via (center, M, d)
7+
c = [N(1), N(2)]
8+
M = [N(1) N(0); N(0) N(1)]
9+
d = [N(1//10), N(2)]
10+
Zmd = ZonotopeMD(c, M, d)
11+
@test Zmd isa ZonotopeMD{N}
12+
@test center(Zmd) == c
13+
@test genmat(Zmd) == hcat(M, Diagonal(d))
14+
@test length(Zmd.center) == size(Zmd.M, 1) == length(Zmd.d)
15+
16+
@test length(center(Zmd)) == 2
17+
@test genmat(Zmd) !== nothing
18+
19+
# Construction from generator matrix
20+
G = hcat(M, Diagonal(d))
21+
Zmd2 = ZonotopeMD(c, G)
22+
@test Zmd2 == Zmd
23+
24+
# Generator matrix with wrong shape
25+
G_wrong = hcat(M, ones(N, 2, 1))
26+
@test_throws AssertionError ZonotopeMD(c, G_wrong)
27+
28+
# Convert to standard Zonotope
29+
Zstd = convert(Zonotope, Zmd)
30+
@test center(Zstd) == c
31+
@test genmat(Zstd) == G
32+
33+
# Cartesian product
34+
c2 = [N(3), N(4)]
35+
M2 = [N(2) N(0); N(0) N(2)]
36+
d2 = [N(3), N(4)]
37+
Zmd_2 = ZonotopeMD(c2, M2, d2)
38+
cp_md = cartesian_product(Zmd, Zmd_2)
39+
cp_std = cartesian_product(Zstd, Zonotope(Zmd_2))
40+
@test isequivalent(cp_md, cp_std)
41+
42+
# Apply a linear map
43+
A = [N(1) N(1); N(0) N(1)]
44+
Zlm = linear_map(A, Zmd)
45+
expected_c = A * c
46+
expected_genmat = A * hcat(M, Diagonal(d))
47+
@test center(Zlm) == expected_c
48+
@test genmat(Zlm) == expected_genmat
49+
50+
# For order 3:
51+
# center ∈ ℝ²,
52+
# M is 2×4 and d ∈ ℝ².
53+
54+
# Direct construction via (center, M, d)
55+
c = [N(1), N(2)]
56+
M1 = [N(1) N(0) N(2) N(0); N(0) N(1) N(3) N(0)]
57+
d = [N(1//10), N(2)]
58+
Zmd = ZonotopeMD(c, M1, d)
59+
@test Zmd isa ZonotopeMD{N}
60+
@test center(Zmd) == c
61+
@test genmat(Zmd) == hcat(M1, Diagonal(d))
62+
@test length(Zmd.center) == size(Zmd.M, 1) == length(Zmd.d)
63+
64+
@test length(center(Zmd)) == 2
65+
@test genmat(Zmd) !== nothing
66+
67+
# Construction from generator matrix
68+
G = hcat(M1, Diagonal(d))
69+
Zmd2 = ZonotopeMD(c, G)
70+
@test Zmd2 == Zmd
71+
72+
G_wrong = hcat(M1, ones(N, 2, 1)) # here p = 3 instead of 4 (2n)
73+
@test_throws AssertionError ZonotopeMD(c, G_wrong)
74+
75+
# Convert to standard Zonotope
76+
Zstd = convert(Zonotope, Zmd)
77+
@test center(Zstd) == c
78+
@test genmat(Zstd) == G
79+
80+
# Cartesian product
81+
c2 = [N(3), N(4)]
82+
M2 = [N(2) N(0) N(1) N(0); N(0) N(2) N(3) N(0)]
83+
d2 = [N(3), N(4)]
84+
Zmd_2 = ZonotopeMD(c2, M2, d2)
85+
cp_md = cartesian_product(Zmd, Zmd_2)
86+
cp_std = cartesian_product(Zstd, Zonotope(Zmd_2))
87+
@test isequivalent(cp_md, cp_std)
88+
89+
# Apply a linear map
90+
A = [N(1) N(1); N(0) N(1)]
91+
Zlm = linear_map(A, Zmd)
92+
expected_c = A * c
93+
expected_genmat = A * hcat(M1, Diagonal(d))
94+
@test center(Zlm) == expected_c
95+
@test genmat(Zlm) == expected_genmat
96+
end

test/runtests.jl

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ if test_suite_basic
150150
@testset "LazySets.Zonotope" begin
151151
include("Sets/Zonotope.jl")
152152
end
153+
@testset "LazySets.ZonotopeMD" begin
154+
include("Sets/ZonotopeMD.jl")
155+
end
153156
@testset "LazySets.ZeroSet" begin
154157
include("Sets/ZeroSet.jl")
155158
end

0 commit comments

Comments
 (0)