Skip to content

Commit 569e12a

Browse files
authored
Merge pull request #3953 from JuliaReach/schillic/hrep
Fix `linear_map` elimination algorithm with empty constraints
2 parents 460b3dd + 8755f92 commit 569e12a

File tree

4 files changed

+38
-48
lines changed

4 files changed

+38
-48
lines changed

src/Interfaces/AbstractPolyhedron.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,14 @@ end
7373
# generic function for the AbstractPolyhedron interface => returns an HPolyhedron
7474
function _linear_map_hrep_helper(M::AbstractMatrix, P::LazySet,
7575
algo::AbstractLinearMapAlgorithm)
76-
constraints = _linear_map_hrep(M, P, algo)
77-
return HPolyhedron(constraints)
76+
clist = _linear_map_hrep(M, P, algo)
77+
if isempty(clist)
78+
N = promote_type(eltype(M), eltype(P))
79+
n = size(M, 1)
80+
return Universe{N}(n)
81+
else
82+
return HPolyhedron(clist)
83+
end
7884
end
7985

8086
# internal functions; defined here due to optional dependencies and submodules

src/Interfaces/AbstractPolyhedron_functions.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,12 @@ function _linear_map_hrep(M::AbstractMatrix, P::AbstractPolyhedron, algo::Linear
508508

509509
# append zeros to the existing constraints, in the last m-n coordinates
510510
# TODO: cast to common vector type instead of Vector(c.a), see #1942, #1952
511-
cext = [HalfSpace(vcat(Vector(c.a), zeros(N, m - n)), c.b) for c in constraints_list(P)]
511+
clist = constraints_list(P)
512+
if isempty(clist)
513+
cext = HalfSpace{N,Vector{N}}[]
514+
else
515+
cext = [HalfSpace(vcat(Vector(c.a), zeros(N, m - n)), c.b) for c in clist]
516+
end
512517

513518
# now fix the last m-n coordinates to zero
514519
id_out = Matrix(one(N) * I, m - n, m - n)
@@ -529,16 +534,20 @@ end
529534
function _linear_map_hrep(M::AbstractMatrix, P::AbstractPolyhedron, algo::LinearMapElimination)
530535
m, n = size(M)
531536
N = promote_type(eltype(M), eltype(P))
532-
₋Id_m = Matrix(-one(N) * I, m, m)
537+
Id_neg = Matrix(-one(N) * I, m, m)
533538
backend = algo.backend
534539
method = algo.method
535540

536541
# extend the polytope storing the y variables first
537542
# append zeros to the existing constraints, in the last m-n coordinates
538543
# TODO: cast to common vector type instead of hard-coding Vector(c.a), see #1942 and #1952
539-
Ax_leq_b = [Polyhedra.HalfSpace(vcat(zeros(N, m), Vector(c.a)), c.b)
540-
for c in constraints_list(P)]
541-
y_eq_Mx = [Polyhedra.HyperPlane(vcat(₋Id_m[i, :], Vector(M[i, :])), zero(N)) for i in 1:m]
544+
clist = constraints_list(P)
545+
if isempty(clist)
546+
Ax_leq_b = Polyhedra.HalfSpace{N,Vector{N}}[]
547+
else
548+
Ax_leq_b = [Polyhedra.HalfSpace(vcat(zeros(N, m), Vector(c.a)), c.b) for c in clist]
549+
end
550+
y_eq_Mx = [Polyhedra.HyperPlane(vcat(Id_neg[i, :], Vector(M[i, :])), zero(N)) for i in 1:m]
542551

543552
Phrep = Polyhedra.hrep(y_eq_Mx, Ax_leq_b)
544553
Phrep = polyhedron(Phrep, backend) # define concrete subtype

src/Interfaces/LazySet_functions.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,6 @@ end
572572
The default implementation applies the functions `exp` and `linear_map`.
573573
"""
574574
@validate function exponential_map(M::AbstractMatrix, X::LazySet)
575-
n = dim(X)
576575
return linear_map(exp(M), X)
577576
end
578577

test/Sets/Universe.jl

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -463,62 +463,38 @@ for N in @tN([Float64, Float32])
463463
@test_throws MethodError rationalize(U2)
464464

465465
# affine_map (part 2)
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)))
472-
end
466+
X = affine_map(N[1 0; 0 1; 0 0], U, N[1, 1, 3])
467+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 0, 1], N(3)))
473468

474469
# exponential_map
475470
U2 = exponential_map(ones(N, 2, 2), U)
476471
@test isidentical(U2, U)
477472

478473
# linear_map (part 2)
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)))
485-
end
474+
X = linear_map(N[1 0; 0 1; 0 0], U)
475+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 0, 1], N(0)))
486476
end
487477

488478
for N in [Float64]
489479
U = Universe{N}(2)
490480

491481
# affine_map (part 3)
492482
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
493-
@static if VERSION < v"1.12"
494-
# TODO these should work with older versions, see below
495-
@test_broken affine_map(N[1 2; 0 0], U, N[1, 1])
496-
@test_broken affine_map(ones(N, 2, 2), U, N[2, 0])
497-
@test_broken affine_map(zeros(N, 2, 2), U, N[2, 0])
498-
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)))
501-
X = affine_map(ones(N, 2, 2), U, N[2, 0]) # projection to line
502-
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(2)))
503-
X = affine_map(zeros(N, 2, 2), U, N[2, 0]) # zero map
504-
@test X isa HPolyhedron{N} && isequivalent(X, Singleton(N[2, 0]))
505-
end
483+
X = affine_map(N[1 2; 0 0], U, N[1, 1]) # projection to axis
484+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 1], N(1)))
485+
X = affine_map(ones(N, 2, 2), U, N[2, 0]) # projection to line
486+
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(2)))
487+
X = affine_map(zeros(N, 2, 2), U, N[2, 0]) # zero map
488+
@test X isa HPolyhedron{N} && isequivalent(X, Singleton(N[2, 0]))
506489
end
507490

508491
# linear_map (part 3)
509492
@static if isdefined(@__MODULE__, :Polyhedra) && isdefined(@__MODULE__, :CDDLib)
510-
@static if VERSION < v"1.12"
511-
# TODO these should work with older versions, see below
512-
@test_broken linear_map(N[1 2; 0 0], U)
513-
@test_broken linear_map(ones(N, 2, 2), U)
514-
@test_broken linear_map(zeros(N, 2, 2), U)
515-
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)))
518-
X = linear_map(ones(N, 2, 2), U) # projection to line
519-
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(0)))
520-
X = linear_map(zeros(N, 2, 2), U) # zero map
521-
@test X isa HPolyhedron{N} && isequivalent(X, ZeroSet{N}(2))
522-
end
493+
X = linear_map(N[1 2; 0 0], U) # projection to axis
494+
@test X isa HPolyhedron{N} && isequivalent(X, Hyperplane(N[0, 1], N(0)))
495+
X = linear_map(ones(N, 2, 2), U) # projection to line
496+
@test X isa HPolyhedron{N} && isequivalent(X, Line2D(N[1, -1], N(0)))
497+
X = linear_map(zeros(N, 2, 2), U) # zero map
498+
@test X isa HPolyhedron{N} && isequivalent(X, ZeroSet{N}(2))
523499
end
524500
end

0 commit comments

Comments
 (0)