Skip to content

Commit 460b3dd

Browse files
authored
Merge pull request #3896 from JuliaReach/schillic/linear_map_universe
Fix and improve `linear_map` for `Universe` with invertible map
2 parents e085e92 + 77a89e1 commit 460b3dd

File tree

4 files changed

+54
-37
lines changed

4 files changed

+54
-37
lines changed

docs/src/lib/sets/Universe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Undocumented implementations:
8484
* [`reflect`](@ref reflect(::LazySet))
8585
* [`distance`](@ref distance(::AbstractVector, ::LazySet))
8686
* [``](@ref ∈(::AbstractVector, ::LazySet))
87+
* [`linear_map`](@ref linear_map(::AbstractMatrix, ::LazySet))
8788
* [`permute`](@ref permute(::LazySet, ::AbstractVector{Int}))
8889
* [`project`](@ref project(::LazySet, ::AbstractVector{Int}))
8990
* [`rationalize`](@ref rationalize(::LazySet))
@@ -116,7 +117,6 @@ Inherited from [`LazySet`](@ref):
116117
* [`affine_map`](@ref affine_map(::AbstractMatrix, ::LazySet, ::AbstractVector))
117118
* [`exponential_map`](@ref exponential_map(::AbstractMatrix, ::LazySet))
118119
* [`is_interior_point`](@ref is_interior_point(::AbstractVector, ::LazySet))
119-
* [`linear_map`](@ref linear_map(::AbstractMatrix, ::LazySet))
120120
* [`sample`](@ref sample(::LazySet, ::Int=1))
121121
* [`isapprox`](@ref isapprox(::LazySet, ::LazySet))
122122
* [`isdisjoint`](@ref isdisjoint(::LazySet, ::LazySet))

src/Sets/Universe/UniverseModule.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ module UniverseModule
33
using Reexport, Requires
44

55
using ..LazySets: LazySet, AbstractPolyhedron, default_polyhedra_backend,
6-
_witness_result_empty, @validate, @validate_commutative
6+
_linear_map_polyhedron, _witness_result_empty, @validate,
7+
@validate_commutative
78
using Random: AbstractRNG, GLOBAL_RNG
8-
using ReachabilityBase.Arrays: SingleEntryVector
9+
using ReachabilityBase.Arrays: SingleEntryVector, isinvertible
910
using ReachabilityBase.Distribution: reseed!
1011
using ReachabilityBase.Iteration: EmptyIterator
1112
using ReachabilityBase.Require: require
@@ -14,9 +15,9 @@ using ReachabilityBase.Require: require
1415
constraints_list, diameter, dim, isbounded,
1516
isboundedtype, isempty, isoperationtype, isuniversal,
1617
norm, radius, rand, rectify, reflect, volume, ,
17-
permute, project, scale, scale!, ρ, σ, translate,
18-
translate!, cartesian_product, convex_hull, difference,
19-
distance, intersection, isdisjoint, ,
18+
linear_map, permute, project, scale, scale!, ρ, σ,
19+
translate, translate!, cartesian_product, convex_hull,
20+
difference, distance, intersection, isdisjoint, ,
2021
linear_combination, minkowski_difference, minkowski_sum
2122
@reexport import ..LazySets: constrained_dimensions, linear_map_inverse,
2223
rationalize, tosimplehrep, triangulate
@@ -49,6 +50,7 @@ include("rectify.jl")
4950
include("reflect.jl")
5051
include("volume.jl")
5152
include("in.jl")
53+
include("linear_map.jl")
5254
include("permute.jl")
5355
include("project.jl")
5456
include("scale.jl")

src/Sets/Universe/linear_map.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
# Extended help
3+
4+
linear_map(M::AbstractMatrix, U::Universe)
5+
6+
### Output
7+
8+
Either a `Universe` if the map is invertible, or the result of the default implementation, which
9+
typically yields an `HPolyhedron`.
10+
"""
11+
@validate function linear_map(M::AbstractMatrix, U::Universe)
12+
if isinvertible(M)
13+
N = eltype(U)
14+
m = size(M, 1)
15+
return Universe{N}(m)
16+
end
17+
return _linear_map_polyhedron(M, U)
18+
end

test/Sets/Universe.jl

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ for N in @tN([Float64, Float32, Rational{Int}])
209209
# affine_map (part 1)
210210
@test_throws DimensionMismatch affine_map(ones(N, 2, 3), U, N[1, 1])
211211
@test_throws DimensionMismatch affine_map(ones(N, 2, 2), U, N[1])
212+
U2 = affine_map(N[1 0; 0 1], U, N[1, 1])
213+
@test isidentical(U2, U)
212214

213215
# distance (between point and set)
214216
@test_throws DimensionMismatch distance(U, N[0])
@@ -241,6 +243,8 @@ for N in @tN([Float64, Float32, Rational{Int}])
241243

242244
# linear_map (part 1)
243245
@test_throws DimensionMismatch linear_map(ones(N, 2, 1), U)
246+
U2 = linear_map(N[1 0; 0 1], U)
247+
@test isidentical(U2, U)
244248

245249
# linear_map_inverse
246250
U2 = LazySets.linear_map_inverse(ones(N, 2, 3), U)
@@ -459,34 +463,25 @@ for N in @tN([Float64, Float32])
459463
@test_throws MethodError rationalize(U2)
460464

461465
# affine_map (part 2)
462-
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
463-
@static if VERSION < v"1.12"
464-
# TODO this should work with older versions, see below
465-
@test_broken affine_map(N[1 0; 0 1; 0 0], U, N[1, 1, 3])
466-
else
467-
X = affine_map(N[1 0; 0 1; 0 0], U, N[1, 1, 3])
468-
@test X isa LazySet{N}
469-
@test isequivalent(X, Hyperplane(N[0, 0, 1], N(3)))
470-
end
466+
@static if VERSION < v"1.12"
467+
# TODO this should work with older versions, see below
468+
@test_broken affine_map(N[1 0; 0 1; 0 0], U, N[1, 1, 3])
469+
else
470+
X = affine_map(N[1 0; 0 1; 0 0], U, N[1, 1, 3])
471+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 0, 1], N(3)))
471472
end
472473

473474
# exponential_map
474475
U2 = exponential_map(ones(N, 2, 2), U)
475-
@test_broken isidentical(U2, U) # TODO this should change
476+
@test isidentical(U2, U)
476477

477478
# linear_map (part 2)
478-
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
479-
U2 = linear_map(N[1 0; 0 1], U)
480-
@test U2 isa HPolyhedron{N} # TODO this should change (HPolyhedron has no dimension)
481-
@test_broken isequivalent(U2, U)
482-
@static if VERSION < v"1.12"
483-
# TODO this should work with older versions, see below
484-
@test_broken linear_map(N[1 0; 0 1; 0 0], U)
485-
else
486-
X = linear_map(N[1 0; 0 1; 0 0], U)
487-
@test X isa HPolyhedron{N}
488-
@test isequivalent(X, Hyperplane(N[0, 0, 1], N(0)))
489-
end
479+
@static if VERSION < v"1.12"
480+
# TODO this should work with older versions, see below
481+
@test_broken linear_map(N[1 0; 0 1; 0 0], U)
482+
else
483+
X = linear_map(N[1 0; 0 1; 0 0], U)
484+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 0, 1], N(0)))
490485
end
491486
end
492487

@@ -496,32 +491,34 @@ for N in [Float64]
496491
# affine_map (part 3)
497492
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
498493
@static if VERSION < v"1.12"
499-
# TODO this should work, even without Polyhedra
494+
# TODO these should work with older versions, see below
495+
@test_broken affine_map(N[1 2; 0 0], U, N[1, 1])
500496
@test_broken affine_map(ones(N, 2, 2), U, N[2, 0])
501497
@test_broken affine_map(zeros(N, 2, 2), U, N[2, 0])
502498
else
499+
X = affine_map(N[1 2; 0 0], U, N[1, 1]) # projection to axis
500+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 1], N(1)))
503501
X = affine_map(ones(N, 2, 2), U, N[2, 0]) # projection to line
504-
@test X isa HPolyhedron{N}
505-
@test isequivalent(X, Line2D(N[1, -1], N(2)))
502+
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(2)))
506503
X = affine_map(zeros(N, 2, 2), U, N[2, 0]) # zero map
507-
@test X isa HPolyhedron{N}
508-
@test isequivalent(X, Singleton(N[2, 0]))
504+
@test X isa HPolyhedron{N} && isequivalent(X, Singleton(N[2, 0]))
509505
end
510506
end
511507

512508
# linear_map (part 3)
513509
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
514510
@static if VERSION < v"1.12"
515511
# TODO these should work with older versions, see below
512+
@test_broken linear_map(N[1 2; 0 0], U)
516513
@test_broken linear_map(ones(N, 2, 2), U)
517514
@test_broken linear_map(zeros(N, 2, 2), U)
518515
else
516+
X = linear_map(N[1 2; 0 0], U) # projection to axis
517+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 1], N(0)))
519518
X = linear_map(ones(N, 2, 2), U) # projection to line
520-
@test X isa HPolyhedron{N}
521-
@test isequivalent(X, Line2D(N[1, -1], N(0)))
519+
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(0)))
522520
X = linear_map(zeros(N, 2, 2), U) # zero map
523-
@test X isa HPolyhedron{N}
524-
@test isequivalent(X, ZeroSet{N}(2))
521+
@test X isa HPolyhedron{N} && isequivalent(X, ZeroSet{N}(2))
525522
end
526523
end
527524
end

0 commit comments

Comments
 (0)