diff --git a/docs/src/lib/representations.md b/docs/src/lib/representations.md index efd8490d75..456b58ab7a 100644 --- a/docs/src/lib/representations.md +++ b/docs/src/lib/representations.md @@ -147,6 +147,7 @@ dim(::HalfSpace) σ(::AbstractVector{Real}, ::HalfSpace{Real}) ∈(::AbstractVector{Real}, ::HalfSpace{Real}) an_element(::HalfSpace{N}) where {N<:Real} +constrained_dimensions(::HalfSpace{N}) where {N<:Real} LazySets.halfspace_left(::AbstractVector{Real}, ::AbstractVector{Real}) LazySets.halfspace_right(::AbstractVector{Real}, ::AbstractVector{Real}) ``` @@ -163,6 +164,7 @@ dim(::Hyperplane) σ(::AbstractVector{Real}, ::Hyperplane{Real}) ∈(::AbstractVector{Real}, ::Hyperplane{Real}) an_element(::Hyperplane{N}) where {N<:Real} +constrained_dimensions(::Hyperplane{N}) where {N<:Real} ``` Inherited from [`LazySet`](@ref): * [`norm`](@ref norm(::LazySet, ::Real)) @@ -233,6 +235,7 @@ dim(::Line) σ(::AbstractVector{Real}, ::Line{Real}) ∈(::AbstractVector{Real}, ::Line{Real}) an_element(::Line{N}) where {N<:Real} +constrained_dimensions(::Line{N}) where {N<:Real} ``` Inherited from [`LazySet`](@ref): * [`norm`](@ref norm(::LazySet, ::Real)) diff --git a/docs/src/lib/utils.md b/docs/src/lib/utils.md index 819dfa28f0..77bfb5719c 100644 --- a/docs/src/lib/utils.md +++ b/docs/src/lib/utils.md @@ -27,6 +27,7 @@ get_radius! an_element_helper σ_helper binary_search_constraints +nonzero_indices ``` ### Types diff --git a/src/HalfSpace.jl b/src/HalfSpace.jl index df2b36ee5a..3b1581aa0c 100644 --- a/src/HalfSpace.jl +++ b/src/HalfSpace.jl @@ -2,6 +2,7 @@ import Base.∈ export HalfSpace, LinearConstraint, an_element, + constrained_dimensions, halfspace_left, halfspace_right """ @@ -118,6 +119,28 @@ function ∈(x::AbstractVector{N}, hs::HalfSpace{N})::Bool where {N<:Real} return dot(x, hs.a) <= hs.b end +""" + constrained_dimensions(hs::HalfSpace{N})::Vector{Int} where N<:Real + +Return the indices in which a half-space is constrained. + +### Input + +- `hs` -- half-space + +### Output + +A vector of ascending indices `i` such that the half-space is constrained in +dimension `i`. + +### Notes + +A 2D half-space with constraint ``x1 ≥ 0`` is constrained in dimension 1 only. +""" +function constrained_dimensions(hs::HalfSpace{N})::Vector{Int} where N<:Real + return nonzero_indices(hs.a) +end + """ halfspace_left(p::AbstractVector{N}, q::AbstractVector{N})::HalfSpace{N} where {N<:Real} diff --git a/src/Hyperplane.jl b/src/Hyperplane.jl index af9a90220c..49b4696ab1 100644 --- a/src/Hyperplane.jl +++ b/src/Hyperplane.jl @@ -110,6 +110,28 @@ function ∈(x::AbstractVector{N}, hp::Hyperplane{N})::Bool where {N<:Real} return dot(x, hp.a) == hp.b end +""" + constrained_dimensions(hp::Hyperplane{N})::Vector{Int} where N<:Real + +Return the indices in which a hyperplane is constrained. + +### Input + +- `hp` -- hyperplane + +### Output + +A vector of ascending indices `i` such that the hyperplane is constrained in +dimension `i`. + +### Notes + +A 2D hyperplane with constraint ``x1 = 0`` is constrained in dimension 1 only. +""" +function constrained_dimensions(hp::Hyperplane{N})::Vector{Int} where N<:Real + return nonzero_indices(hp.a) +end + # --- Hyperplane functions --- diff --git a/src/Line.jl b/src/Line.jl index 84ba72c03b..e41e374f3a 100644 --- a/src/Line.jl +++ b/src/Line.jl @@ -134,3 +134,25 @@ function ∈(x::AbstractVector{N}, L::Line{N})::Bool where {N<:Real} @assert length(x) == dim(L) return dot(L.a, x) == L.b end + +""" + constrained_dimensions(L::Line{N})::Vector{Int} where N<:Real + +Return the indices in which a line is constrained. + +### Input + +- `L` -- line + +### Output + +A vector of ascending indices `i` such that the line is constrained in dimension +`i`. + +### Notes + +A line with constraint ``x1 = 0`` is constrained in dimension 1 only. +""" +function constrained_dimensions(L::Line{N})::Vector{Int} where N<:Real + return nonzero_indices(L.a) +end diff --git a/src/helper_functions.jl b/src/helper_functions.jl index 642978c7b4..3e9c366a71 100644 --- a/src/helper_functions.jl +++ b/src/helper_functions.jl @@ -86,6 +86,36 @@ function ispermutation(u::AbstractVector{T}, v::AbstractVector{T})::Bool where T return true end +""" + nonzero_indices(v::AbstractVector{N})::Vector{Int} where N<:Real + +Return the indices in which a vector is non-zero. + +### Input + +- `v` -- vector + +### Output + +A vector of ascending indices `i` such that the vector is non-zero in dimension +`i`. +""" +function nonzero_indices(v::AbstractVector{N})::Vector{Int} where N<:Real + n = length(v) + res = Vector{Int}() + sizehint!(res, n) + for i in 1:n + if v[i] != zero(N) + push!(res, i) + end + end + return res +end + +function nonzero_indices(v::SparseVector{N})::Vector{Int} where N<:Real + return x.nzind +end + """ @neutral(SET, NEUT) diff --git a/test/unit_HalfSpace.jl b/test/unit_HalfSpace.jl index 7d04aca6d1..9cfa31a3b7 100644 --- a/test/unit_HalfSpace.jl +++ b/test/unit_HalfSpace.jl @@ -30,6 +30,10 @@ for N in [Float64, Rational{Int}, Float32] # an_element function and membership function @test an_element(hs) ∈ hs + # constrained dimensions + @test constrained_dimensions(HalfSpace(N[1, 0, 1], N(1))) == [1, 3] + @test constrained_dimensions(HalfSpace(N[0, 1, 0], N(1))) == [2] + # halfspace_left & halfspace_right @test N[1, 2] ∈ halfspace_left(N[1, 1], N[2, 2]) @test N[2, 1] ∈ halfspace_right(N[1, 1], N[2, 2]) diff --git a/test/unit_Hyperplane.jl b/test/unit_Hyperplane.jl index 8b46001f63..05de5208d2 100644 --- a/test/unit_Hyperplane.jl +++ b/test/unit_Hyperplane.jl @@ -6,7 +6,7 @@ for N in [Float64, Rational{Int}, Float32] # dimension @test dim(hp) == 3 - # support vector and membership function + # support vector and membership function test_svec(hp) d1 = copy(hp.a) @test σ(d1, hp) ∈ hp @@ -25,9 +25,13 @@ for N in [Float64, Rational{Int}, Float32] a = zeros(N, 3); a[3] = N(1) test_svec(Hyperplane(a, N(5))) - # an_element function and membership function + # an_element and membership @test an_element(hp) ∈ hp + # constrained dimensions + @test constrained_dimensions(Hyperplane(N[1, 0, 1], N(1))) == [1, 3] + @test constrained_dimensions(Hyperplane(N[0, 1, 0], N(1))) == [2] + # intersection emptiness b = BallInf(zeros(N, 3), N(1)) empty_intersection, v = is_intersection_empty(b, hp, true) diff --git a/test/unit_Line.jl b/test/unit_Line.jl index 4f041083bc..15c00ceb43 100644 --- a/test/unit_Line.jl +++ b/test/unit_Line.jl @@ -20,6 +20,10 @@ for N in [Float64, Rational{Int}, Float32] an_element(l3) ∈ l3 an_element(l4) ∈ l4 + # constrained dimensions + @test constrained_dimensions(l1) == [2] + @test constrained_dimensions(l4) == [1, 2] + # concrete intersection cap11 = intersection(l1, l1_copy) cap12 = intersection(l1, l2)