diff --git a/docs/src/lib/sets/HPolygon.md b/docs/src/lib/sets/HPolygon.md index 93b5015ea1..71a432ea1c 100644 --- a/docs/src/lib/sets/HPolygon.md +++ b/docs/src/lib/sets/HPolygon.md @@ -1,21 +1,31 @@ ```@meta -CurrentModule = LazySets +CurrentModule = LazySets.HPolygonModule ``` # [Polygon in constraint representation (HPolygon)](@id def_HPolygon) ```@docs HPolygon +``` + +## Operations + +```@docs σ(::AbstractVector, ::HPolygon) translate(::HPolygon, ::AbstractVector) ``` + +```@meta +CurrentModule = LazySets +``` + Inherited from [`LazySet`](@ref): +* [`diameter`](@ref diameter(::LazySet, ::Real)) * [`norm`](@ref norm(::LazySet, ::Real)) * [`radius`](@ref radius(::LazySet, ::Real)) -* [`diameter`](@ref diameter(::LazySet, ::Real)) +* [`reflect`](@ref reflect(::LazySet)) * [`singleton_list`](@ref singleton_list(::LazySet)) * [`linear_map`](@ref linear_map(::AbstractMatrix, ::LazySet) -* [`reflect`](@ref reflect(::LazySet)) Inherited from [`AbstractPolytope`](@ref): * [`isempty`](@ref isempty(::AbstractPolytope)) @@ -27,13 +37,13 @@ Inherited from [`AbstractPolygon`](@ref): Inherited from [`AbstractHPolygon`](@ref): * [`an_element`](@ref an_element(::AbstractHPolygon)) -* [`∈`](@ref ∈(::AbstractVector, ::AbstractHPolygon)) -* [`vertices_list`](@ref vertices_list(::AbstractHPolygon{N}) where {N}) +* [`constraints_list`](@ref constraints_list(::AbstractHPolygon)) +* [`isbounded`](@ref isbounded(::AbstractHPolygon, ::Bool=true)) +* [`normalize`](@ref normalize(::AbstractHPolygon{N}, p::Real=N(2)) where {N}) +* [`remove_redundant_constraints!`](@ref remove_redundant_constraints!(::AbstractHPolygon)) * [`tohrep`](@ref tohrep(::HPOLYGON) where {HPOLYGON<:AbstractHPolygon}) * [`tovrep`](@ref tovrep(::AbstractHPolygon)) -* [`normalize`](@ref normalize(::AbstractHPolygon{N}, p::Real=N(2)) where {N}) -* [`isbounded`](@ref isbounded(::AbstractHPolygon, ::Bool=true)) +* [`vertices_list`](@ref vertices_list(::AbstractHPolygon{N}) where {N}) * [`addconstraint!`](@ref addconstraint!(::AbstractHPolygon, ::HalfSpace)) +* [`∈`](@ref ∈(::AbstractVector, ::AbstractHPolygon)) * [`isredundant`](@ref isredundant(::HalfSpace, ::HalfSpace, ::HalfSpace)) -* [`remove_redundant_constraints!`](@ref remove_redundant_constraints!(::AbstractHPolygon)) -* [`constraints_list`](@ref constraints_list(::AbstractHPolygon)) diff --git a/src/LazySets.jl b/src/LazySets.jl index 44a2f5fec9..d0396404a7 100644 --- a/src/LazySets.jl +++ b/src/LazySets.jl @@ -126,7 +126,8 @@ include("Sets/Ellipsoid/EllipsoidModule.jl") include("Sets/DensePolynomialZonotope/DensePolynomialZonotopeModule.jl") @reexport using ..DensePolynomialZonotopeModule: DensePolynomialZonotope -include("Sets/HPolygon.jl") +include("Sets/HPolygon/HPolygonModule.jl") +@reexport using ..HPolygonModule: HPolygon include("Sets/HPolygonOpt.jl") diff --git a/src/Sets/HPolygon.jl b/src/Sets/HPolygon/HPolygon.jl similarity index 58% rename from src/Sets/HPolygon.jl rename to src/Sets/HPolygon/HPolygon.jl index dad1403027..e751d1fd86 100644 --- a/src/Sets/HPolygon.jl +++ b/src/Sets/HPolygon/HPolygon.jl @@ -1,5 +1,3 @@ -export HPolygon - """ HPolygon{N, VN<:AbstractVector{N}} <: AbstractHPolygon{N} @@ -65,8 +63,6 @@ struct HPolygon{N,VN<:AbstractVector{N}} <: AbstractHPolygon{N} end end -isoperationtype(::Type{<:HPolygon}) = false - # constructor with no constraints function HPolygon{N,VN}() where {N,VN<:AbstractVector{N}} return HPolygon(Vector{HalfSpace{N,VN}}()) @@ -93,88 +89,3 @@ function HPolygon(A::AbstractMatrix, b::AbstractVector; sort_constraints::Bool=t return HPolygon(constraints_list(A, b); sort_constraints=sort_constraints, check_boundedness=check_boundedness, prune=prune) end - -""" - σ(d::AbstractVector, P::HPolygon; - [linear_search]::Bool=(length(P.constraints) < $BINARY_SEARCH_THRESHOLD)) - -Return a support vector of a polygon in a given direction. - -### Input - -- `d` -- direction -- `P` -- polygon in constraint representation -- `linear_search` -- (optional, default: see below) flag for controlling whether - to perform a linear search or a binary search - -### Output - -The support vector in the given direction. -The result is always one of the vertices; in particular, if the direction has -norm zero, any vertex is returned. - -### Algorithm - -Comparison of directions is performed using polar angles; see the definition of -`⪯` for two-dimensional vectors. - -For polygons with $BINARY_SEARCH_THRESHOLD or more constraints we use a binary -search by default. -""" -function σ(d::AbstractVector, P::HPolygon; - linear_search::Bool=(length(P.constraints) < BINARY_SEARCH_THRESHOLD)) - n = length(P.constraints) - @assert n > 0 "the polygon has no constraints" - - if linear_search - # linear search - k = 1 - while k <= n && P.constraints[k].a ⪯ d - k += 1 - end - else - # binary search - k = binary_search_constraints(d, P.constraints) - end - - if k == 1 || k == n + 1 - # corner cases: wrap-around in constraints list - return element(_intersection_line2d(P.constraints[1], P.constraints[n])) - else - return element(_intersection_line2d(P.constraints[k], P.constraints[k - 1])) - end -end - -""" - translate(v::AbstractVector, P::HPolygon; [share]::Bool=false) - -Translate (i.e., shift) a polygon in constraint representation by a given -vector. - -### Input - -- `P` -- polygon in constraint representation -- `v` -- translation vector -- `share` -- (optional, default: `false`) flag for sharing unmodified parts of - the original set representation - -### Output - -A translated polygon in constraint representation. - -### Notes - -The normal vectors of the constraints (vector `a` in `a⋅x ≤ b`) are shared with -the original constraints if `share == true`. - -### Algorithm - -We translate every constraint. -""" -function translate(P::HPolygon, v::AbstractVector; share::Bool=false) - @assert length(v) == dim(P) "cannot translate a $(dim(P))-dimensional " * - "set by a $(length(v))-dimensional vector" - constraints = [translate(c, v; share=share) for c in constraints_list(P)] - return HPolygon(constraints; sort_constraints=false, - check_boundedness=false, prune=false) -end diff --git a/src/Sets/HPolygon/HPolygonModule.jl b/src/Sets/HPolygon/HPolygonModule.jl new file mode 100644 index 0000000000..18754f3a42 --- /dev/null +++ b/src/Sets/HPolygon/HPolygonModule.jl @@ -0,0 +1,20 @@ +module HPolygonModule + +using Reexport + +using ..LazySets: AbstractHPolygon, BINARY_SEARCH_THRESHOLD, addconstraint!, + binary_search_constraints, element, ⪯, _intersection_line2d +using ..LazySets.HalfSpaceModule: HalfSpace, _normal_Vector + +@reexport import ..API: isoperationtype, σ, translate +@reexport using ..API + +export HPolygon + +include("HPolygon.jl") + +include("isoperationtype.jl") +include("support_vector.jl") +include("translate.jl") + +end # module diff --git a/src/Sets/HPolygon/isoperationtype.jl b/src/Sets/HPolygon/isoperationtype.jl new file mode 100644 index 0000000000..190a48aa04 --- /dev/null +++ b/src/Sets/HPolygon/isoperationtype.jl @@ -0,0 +1 @@ +isoperationtype(::Type{<:HPolygon}) = false diff --git a/src/Sets/HPolygon/support_vector.jl b/src/Sets/HPolygon/support_vector.jl new file mode 100644 index 0000000000..00a379436b --- /dev/null +++ b/src/Sets/HPolygon/support_vector.jl @@ -0,0 +1,50 @@ +""" + σ(d::AbstractVector, P::HPolygon; + [linear_search]::Bool=(length(P.constraints) < $BINARY_SEARCH_THRESHOLD)) + +Return a support vector of a polygon in a given direction. + +### Input + +- `d` -- direction +- `P` -- polygon in constraint representation +- `linear_search` -- (optional, default: see below) flag for controlling whether + to perform a linear search or a binary search + +### Output + +The support vector in the given direction. +The result is always one of the vertices; in particular, if the direction has +norm zero, any vertex is returned. + +### Algorithm + +Comparison of directions is performed using polar angles; see the definition of +`⪯` for two-dimensional vectors. + +For polygons with $BINARY_SEARCH_THRESHOLD or more constraints we use a binary +search by default. +""" +function σ(d::AbstractVector, P::HPolygon; + linear_search::Bool=(length(P.constraints) < BINARY_SEARCH_THRESHOLD)) + n = length(P.constraints) + @assert n > 0 "the polygon has no constraints" + + if linear_search + # linear search + k = 1 + while k <= n && P.constraints[k].a ⪯ d + k += 1 + end + else + # binary search + k = binary_search_constraints(d, P.constraints) + end + + if k == 1 || k == n + 1 + # corner cases: wrap-around in constraints list + return element(_intersection_line2d(P.constraints[1], P.constraints[n])) + else + return element(_intersection_line2d(P.constraints[k], P.constraints[k - 1])) + end +end diff --git a/src/Sets/HPolygon/translate.jl b/src/Sets/HPolygon/translate.jl new file mode 100644 index 0000000000..d54fc69975 --- /dev/null +++ b/src/Sets/HPolygon/translate.jl @@ -0,0 +1,33 @@ +""" + translate(v::AbstractVector, P::HPolygon; [share]::Bool=false) + +Translate (i.e., shift) a polygon in constraint representation by a given +vector. + +### Input + +- `P` -- polygon in constraint representation +- `v` -- translation vector +- `share` -- (optional, default: `false`) flag for sharing unmodified parts of + the original set representation + +### Output + +A translated polygon in constraint representation. + +### Notes + +The normal vectors of the constraints (vector `a` in `a⋅x ≤ b`) are shared with +the original constraints if `share == true`. + +### Algorithm + +We translate every constraint. +""" +function translate(P::HPolygon, v::AbstractVector; share::Bool=false) + @assert length(v) == dim(P) "cannot translate a $(dim(P))-dimensional " * + "set by a $(length(v))-dimensional vector" + constraints = [translate(c, v; share=share) for c in constraints_list(P)] + return HPolygon(constraints; sort_constraints=false, + check_boundedness=false, prune=false) +end diff --git a/src/Utils/helper_functions.jl b/src/Utils/helper_functions.jl index 3242d27b85..5800b86d54 100644 --- a/src/Utils/helper_functions.jl +++ b/src/Utils/helper_functions.jl @@ -266,3 +266,6 @@ function _vec end # internal function; defined here due to dependency SymEngine and submodules function _is_linearcombination end + +# internal function; defined here due to dependency in submodules +function _intersection_line2d end diff --git a/test/Sets/Polygon.jl b/test/Sets/Polygon.jl index f77026dd63..f609279db1 100644 --- a/test/Sets/Polygon.jl +++ b/test/Sets/Polygon.jl @@ -103,6 +103,14 @@ for N in [Float64, Float32, Rational{Int}] # HPolygon/HPolygonOpt tests for (hp, t_hp) in [(p, HPolygon), (po, HPolygonOpt)] + # constructors + @test t_hp{N}() isa t_hp{N} + @test t_hp{N,Vector{N}}() isa t_hp{N,Vector{N}} + clist = [HalfSpace(N[1, 0], N(1)), HalfSpace(sparsevec([1], N[-1], 2), N(-1))] + P = t_hp(clist) + @test P isa t_hp{N,Vector{N}} && + P.constraints == [HalfSpace(N[1, 0], N(1)), HalfSpace(N[-1, 0], N(-1))] + # Test Dimension @test dim(hp) == 2