From e37f8d8c9edb99d9d7cb148a86e14807715b17c5 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 8 Jan 2019 21:11:28 +0100 Subject: [PATCH 1/5] add validate_boundedness methods --- docs/src/lib/interfaces.md | 1 + docs/src/lib/representations.md | 1 + src/AbstractHPolygon.jl | 30 +++++++++++++++++++++++++++++- src/HPolytope.jl | 31 ++++++++++++++++++++++++++++++- test/unit_Polygon.jl | 6 ++++-- test/unit_Polytope.jl | 2 ++ 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/docs/src/lib/interfaces.md b/docs/src/lib/interfaces.md index c6d497c5a9..d5b0e9e82d 100644 --- a/docs/src/lib/interfaces.md +++ b/docs/src/lib/interfaces.md @@ -156,6 +156,7 @@ addconstraint!(::AbstractHPolygon{N}, ::LinearConstraint{N}) where {N<:Real} addconstraint!(::Vector{LinearConstraint{N}}, ::LinearConstraint{N}) where {N<:Real} constraints_list(::AbstractHPolygon{N}) where {N<:Real} vertices_list(::AbstractHPolygon{N}, ::Bool=false, ::Bool=true) where {N<:Real} +validate_boundedness(::AbstractHPolygon) ``` ### Centrally symmetric polytope diff --git a/docs/src/lib/representations.md b/docs/src/lib/representations.md index 0ea6527c93..b099ee729c 100644 --- a/docs/src/lib/representations.md +++ b/docs/src/lib/representations.md @@ -460,6 +460,7 @@ The following methods are specific for `HPolytope`. ```@docs rand(::Type{HPolytope}) vertices_list(::HPolytope{N}) where {N<:Real} +validate_boundedness(::HPolytope) ``` Inherited from [`AbstractPolytope`](@ref): diff --git a/src/AbstractHPolygon.jl b/src/AbstractHPolygon.jl index 31f131e785..01f5696363 100644 --- a/src/AbstractHPolygon.jl +++ b/src/AbstractHPolygon.jl @@ -5,7 +5,8 @@ export AbstractHPolygon, an_element, addconstraint!, vertices_list, - constraints_list + constraints_list, + validate_boundedness # This constant marks the threshold for the number of constraints of a polygon # above which we use a binary search to find the relevant constraint in a @@ -378,3 +379,30 @@ function binary_search_constraints(d::AbstractVector{N}, return upper end end + +""" + validate_boundedness(P::AbstractHPolygon)::Bool + +Check whether a polygon in constraint representation is indeed bounded. + +### Input + +- `P` -- polygon in constraint representation + +### Output + +`true` iff `P` is bounded. + +### Algorithm + +We convert `P` to an `HPolyhedron` `P2` and then use `isbounded(P2)`. + +### Notes + +This function is offered in addition to [`isbounded(P::AbstractPolytope)`](@ref) +which actually always returns `true` because boundedness is an implicit +assumption of `AbstractPolytope`. +""" +function validate_boundedness(P::AbstractHPolygon)::Bool + return isbounded(HPolyhedron(P.constraints)) +end diff --git a/src/HPolytope.jl b/src/HPolytope.jl index 11086867b6..31cf18a55b 100644 --- a/src/HPolytope.jl +++ b/src/HPolytope.jl @@ -1,7 +1,8 @@ import Base.rand export HPolytope, - vertices_list + vertices_list, + validate_boundedness """ HPolytope{N<:Real} <: AbstractPolytope{N} @@ -42,6 +43,7 @@ end HPolytope{N}(A::AbstractMatrix{N}, b::AbstractVector{N}) where {N<:Real} = HPolytope(A, b) + # --- LazySet interface functions --- @@ -87,6 +89,33 @@ function rand(::Type{HPolytope}; return convert(HPolytope, vpolytope) end +""" + validate_boundedness(P::HPolytope)::Bool + +Check whether a polytope in constraint representation is indeed bounded. + +### Input + +- `P` -- polytope in constraint representation + +### Output + +`true` iff `P` is bounded. + +### Algorithm + +We convert `P` to an `HPolyhedron` `P2` and then use `isbounded(P2)`. + +### Notes + +This function is offered in addition to [`isbounded(P::AbstractPolytope)`](@ref) +which actually always returns `true` because boundedness is an implicit +assumption of `AbstractPolytope`. +""" +function validate_boundedness(P::HPolytope)::Bool + return isbounded(HPolyhedron(P.constraints)) +end + # --- functions that use Polyhedra.jl --- diff --git a/test/unit_Polygon.jl b/test/unit_Polygon.jl index 5d8bf82faa..bfc7e1b3f4 100644 --- a/test/unit_Polygon.jl +++ b/test/unit_Polygon.jl @@ -42,8 +42,10 @@ for N in [Float64, Float32, Rational{Int}] @test_throws AssertionError σ(N[0], HPolygonOpt(HPolygon{N}())) # boundedness - @test isbounded(p) - @test isbounded(po) + @test isbounded(p) && isbounded(po) + @test validate_boundedness(p) && validate_boundedness(po) + @test !validate_boundedness(HPolygon{N}()) && + !validate_boundedness(HPolygonOpt{N}()) # isempty @test !isempty(p) diff --git a/test/unit_Polytope.jl b/test/unit_Polytope.jl index 093d162875..06e0549d27 100644 --- a/test/unit_Polytope.jl +++ b/test/unit_Polytope.jl @@ -52,6 +52,8 @@ for N in [Float64, Rational{Int}, Float32] # boundedness @test isbounded(p) + @test validate_boundedness(p) + @test !validate_boundedness(HPolytope{N}()) # membership @test ∈(N[5 / 4, 7 / 4], p) From db3e506dd559d5b4f75331c9319993e817aeaf97 Mon Sep 17 00:00:00 2001 From: schillic Date: Tue, 8 Jan 2019 21:51:27 +0100 Subject: [PATCH 2/5] add validate_boundedness option to H-rep constructors --- src/HPolygon.jl | 26 ++++++++++++++++++++------ src/HPolygonOpt.jl | 35 +++++++++++++++++++++++++---------- src/HPolytope.jl | 32 ++++++++++++++++++++++++++++---- test/unit_Polygon.jl | 12 ++++++++++++ test/unit_Polytope.jl | 7 ++++++- 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/HPolygon.jl b/src/HPolygon.jl index 1f22c0a90e..3a0b750e6c 100644 --- a/src/HPolygon.jl +++ b/src/HPolygon.jl @@ -29,24 +29,31 @@ struct HPolygon{N<:Real} <: AbstractHPolygon{N} # default constructor that applies sorting of the given constraints function HPolygon{N}(constraints::Vector{LinearConstraint{N}}; - sort_constraints::Bool=true) where {N<:Real} + sort_constraints::Bool=true, + validate_boundedness::Bool=false) where {N<:Real} if sort_constraints sorted_constraints = Vector{LinearConstraint{N}}() sizehint!(sorted_constraints, length(constraints)) for ci in constraints addconstraint!(sorted_constraints, ci) end - return new{N}(sorted_constraints) + P = new{N}(sorted_constraints) else - return new{N}(constraints) + P = new{N}(constraints) end + @assert (!validate_boundedness || + LazySets.validate_boundedness(P)) "the polygon is not bounded" + return P end end # convenience constructor without type parameter HPolygon(constraints::Vector{LinearConstraint{N}}; - sort_constraints::Bool=true) where {N<:Real} = - HPolygon{N}(constraints; sort_constraints=sort_constraints) + sort_constraints::Bool=true, + validate_boundedness::Bool=false) where {N<:Real} = + HPolygon{N}(constraints; + sort_constraints=sort_constraints, + validate_boundedness=validate_boundedness) # constructor for an HPolygon with no constraints HPolygon{N}() where {N<:Real} = HPolygon{N}(Vector{LinearConstraint{N}}()) @@ -55,7 +62,14 @@ HPolygon{N}() where {N<:Real} = HPolygon{N}(Vector{LinearConstraint{N}}()) HPolygon() = HPolygon{Float64}() # conversion constructor -HPolygon(S::LazySet) = convert(HPolygon, S) +function HPolygon(S::LazySet; validate_boundedness::Bool=false) + P = convert(HPolygon, S) + if validate_boundedness + # trigger boundedness check in constructor + HPolygon(P.constraints; validate_boundedness=true) + end + return P +end # --- LazySet interface functions --- diff --git a/src/HPolygonOpt.jl b/src/HPolygonOpt.jl index e33d05cce2..cbd4f18b8a 100644 --- a/src/HPolygonOpt.jl +++ b/src/HPolygonOpt.jl @@ -37,34 +37,49 @@ mutable struct HPolygonOpt{N<:Real} <: AbstractHPolygon{N} # default constructor that applies sorting of the given constraints function HPolygonOpt{N}(constraints::Vector{LinearConstraint{N}}, ind::Int=1; - sort_constraints::Bool=true) where {N<:Real} + sort_constraints::Bool=true, + validate_boundedness::Bool=false) where {N<:Real} if sort_constraints sorted_constraints = Vector{LinearConstraint{N}}() sizehint!(sorted_constraints, length(constraints)) for ci in constraints addconstraint!(sorted_constraints, ci) end - return new{N}(sorted_constraints, ind) + P = new{N}(sorted_constraints, ind) else - return new{N}(constraints, ind) + P = new{N}(constraints, ind) end + @assert (!validate_boundedness || + LazySets.validate_boundedness(P)) "the polygon is not bounded" + return P end end # convenience constructor without type parameter HPolygonOpt(constraints::Vector{LinearConstraint{N}}, - ind::Int=1) where {N<:Real} = - HPolygonOpt{N}(constraints, ind) + ind::Int=1; + sort_constraints::Bool=true, + validate_boundedness::Bool=false) where {N<:Real} = + HPolygonOpt{N}(constraints, ind; + sort_constraints=sort_constraints, + validate_boundedness=validate_boundedness) -# constructor for an HPolygon with no constraints -HPolygonOpt{N}() where {N<:Real} = - HPolygonOpt{N}(Vector{LinearConstraint{N}}()) +# constructor for an HPolygonOpt with no constraints +HPolygonOpt{N}() where {N<:Real} = HPolygonOpt{N}(Vector{LinearConstraint{N}}()) -# constructor for an HPolygon with no constraints of type Float64 +# constructor for an HPolygonOpt with no constraints of type Float64 HPolygonOpt() = HPolygonOpt{Float64}() # conversion constructor -HPolygonOpt(S::LazySet) = convert(HPolygonOpt, S) +# conversion constructor +function HPolygonOpt(S::LazySet; validate_boundedness::Bool=false) + P = convert(HPolygonOpt, S) + if validate_boundedness + # trigger boundedness check in constructor + HPolygonOpt(P.constraints; validate_boundedness=true) + end + return P +end diff --git a/src/HPolytope.jl b/src/HPolytope.jl index 31cf18a55b..43e03a2f70 100644 --- a/src/HPolytope.jl +++ b/src/HPolytope.jl @@ -20,8 +20,22 @@ assumption in this type. """ struct HPolytope{N<:Real} <: AbstractPolytope{N} constraints::Vector{LinearConstraint{N}} + + function HPolytope{N}(constraints::Vector{LinearConstraint{N}}; + validate_boundedness::Bool=false + ) where {N<:Real} + P = new{N}(constraints) + @assert (!validate_boundedness || + LazySets.validate_boundedness(P)) "the polytope is not bounded" + return P + end end +# convenience constructor without type parameter +HPolytope(constraints::Vector{LinearConstraint{N}}; + validate_boundedness::Bool=false) where {N<:Real} = + HPolytope{N}(constraints; validate_boundedness=validate_boundedness) + # constructor for an HPolytope with no constraints HPolytope{N}() where {N<:Real} = HPolytope{N}(Vector{LinearConstraint{N}}()) @@ -29,19 +43,29 @@ HPolytope{N}() where {N<:Real} = HPolytope{N}(Vector{LinearConstraint{N}}()) HPolytope() = HPolytope{Float64}() # conversion constructor -HPolytope(S::LazySet) = convert(HPolytope, S) +function HPolytope(S::LazySet; validate_boundedness::Bool=false) + P = convert(HPolytope, S) + if validate_boundedness + # trigger boundedness check in constructor + HPolytope(P.constraints; validate_boundedness=true) + end + return P +end # constructor for an HPolytope from a simple H-representation -function HPolytope(A::AbstractMatrix{N}, b::AbstractVector{N}) where {N<:Real} +function HPolytope(A::AbstractMatrix{N}, b::AbstractVector{N}; + validate_boundedness::Bool=false) where {N<:Real} m = size(A, 1) constraints = LinearConstraint{N}[] @inbounds for i in 1:m push!(constraints, LinearConstraint(A[i, :], b[i])) end - return HPolytope(constraints) + return HPolytope(constraints; validate_boundedness=validate_boundedness) end -HPolytope{N}(A::AbstractMatrix{N}, b::AbstractVector{N}) where {N<:Real} = HPolytope(A, b) +HPolytope{N}(A::AbstractMatrix{N}, b::AbstractVector{N}; + validate_boundedness::Bool=false) where {N<:Real} = + HPolytope(A, b; validate_boundedness=validate_boundedness) # --- LazySet interface functions --- diff --git a/test/unit_Polygon.jl b/test/unit_Polygon.jl index bfc7e1b3f4..670207ca68 100644 --- a/test/unit_Polygon.jl +++ b/test/unit_Polygon.jl @@ -37,6 +37,14 @@ for N in [Float64, Float32, Rational{Int}] HPolytope(p) HPolytope(po) + # conversion from other set type + H = Hyperrectangle(low=N[-1, -1], high=N[1, 1]) + HPolygon(H) + HPolygonOpt(H) + # check boundedness after conversion + HPolygon(H; validate_boundedness=true) + HPolygonOpt(H; validate_boundedness=true) + # support vector of polygon with no constraints @test_throws AssertionError σ(N[0], HPolygon{N}()) @test_throws AssertionError σ(N[0], HPolygonOpt(HPolygon{N}())) @@ -46,6 +54,10 @@ for N in [Float64, Float32, Rational{Int}] @test validate_boundedness(p) && validate_boundedness(po) @test !validate_boundedness(HPolygon{N}()) && !validate_boundedness(HPolygonOpt{N}()) + @test_throws AssertionError HPolygon(LinearConstraint{N}[]; + validate_boundedness=true) + @test_throws AssertionError HPolygonOpt(LinearConstraint{N}[]; + validate_boundedness=true) # isempty @test !isempty(p) diff --git a/test/unit_Polytope.jl b/test/unit_Polytope.jl index 06e0549d27..ae30f3d2b9 100644 --- a/test/unit_Polytope.jl +++ b/test/unit_Polytope.jl @@ -21,6 +21,8 @@ for N in [Float64, Rational{Int}, Float32] @test c isa Vector{LinearConstraint{N}} @test c[1].a == N[1, 2] && c[1].b == N(1) @test c[2].a == N[-1, 1] && c[2].b == N(2) + @test_throws AssertionError HPolytope(N[1 0; 0 1], N[1, 1]; + validate_boundedness=true) # convert back to matrix and vector A2, b2 = tosimplehrep(p) @@ -53,7 +55,8 @@ for N in [Float64, Rational{Int}, Float32] # boundedness @test isbounded(p) @test validate_boundedness(p) - @test !validate_boundedness(HPolytope{N}()) + p2 = HPolytope{N}() + @test isbounded(p2) && !validate_boundedness(p2) # membership @test ∈(N[5 / 4, 7 / 4], p) @@ -81,6 +84,8 @@ for N in [Float64, Rational{Int}, Float32] P = convert(HPolytope, H) vlist = vertices_list(P) @test ispermutation(vlist, [N[3, 3], N[3, -1], N[-1, -1], N[-1, 3]]) + # check boundedness after conversion + HPolytope(H; validate_boundedness=true) # isempty @test !isempty(p) From 00b551c79b30f864ce00b6f77c3268d568e42a36 Mon Sep 17 00:00:00 2001 From: schillic Date: Wed, 16 Jan 2019 22:09:44 +0100 Subject: [PATCH 3/5] add arguments to docstring --- src/HPolygon.jl | 8 +++++++- src/HPolygonOpt.jl | 12 +++++++++--- src/HPolytope.jl | 5 ++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/HPolygon.jl b/src/HPolygon.jl index 3a0b750e6c..55f3ae4a7c 100644 --- a/src/HPolygon.jl +++ b/src/HPolygon.jl @@ -10,7 +10,13 @@ are sorted in counter-clockwise fashion with respect to their normal directions. ### Fields -- `constraints` -- list of linear constraints, sorted by the angle +- `constraints` -- list of linear constraints, sorted by the angle +- `sort_constraints` -- (optional, default: `true`) flag for sorting the + constraints (sortedness is a running assumption of + this type) +- `validate_boundedness` -- (optional, default: `true`) flag for checking if the + constraints make the polygon bounded; (boundedness + is a running assumption of this type) ### Notes diff --git a/src/HPolygonOpt.jl b/src/HPolygonOpt.jl index cbd4f18b8a..d509e15ca6 100644 --- a/src/HPolygonOpt.jl +++ b/src/HPolygonOpt.jl @@ -11,9 +11,15 @@ This is a refined version of `HPolygon`. ### Fields -- `constraints` -- list of linear constraints -- `ind` -- index in the list of constraints to begin the search to evaluate the - support function +- `constraints` -- list of linear constraints +- `ind` -- index in the list of constraints to begin the search + to evaluate the support function +- `sort_constraints` -- (optional, default: `true`) flag for sorting the + constraints (sortedness is a running assumption of + this type) +- `validate_boundedness` -- (optional, default: `true`) flag for checking if the + constraints make the polygon bounded; (boundedness + is a running assumption of this type) ### Notes diff --git a/src/HPolytope.jl b/src/HPolytope.jl index 43e03a2f70..bc5f257046 100644 --- a/src/HPolytope.jl +++ b/src/HPolytope.jl @@ -11,7 +11,10 @@ Type that represents a convex polytope in H-representation. ### Fields -- `constraints` -- vector of linear constraints +- `constraints` -- vector of linear constraints +- `validate_boundedness` -- (optional, default: `true`) flag for checking if the + constraints make the polytope bounded; (boundedness + is a running assumption of this type) ### Note From b03bcb1104c24677c2f7e19f2be4d9579fcdba0f Mon Sep 17 00:00:00 2001 From: schillic Date: Fri, 18 Jan 2019 20:17:48 +0100 Subject: [PATCH 4/5] remove unpopular conversion constructors --- src/HPolygon.jl | 11 ----------- src/HPolygonOpt.jl | 13 ------------- src/HPolytope.jl | 10 ---------- test/unit_Polygon.jl | 22 +++++++++++----------- test/unit_Polytope.jl | 2 +- 5 files changed, 12 insertions(+), 46 deletions(-) diff --git a/src/HPolygon.jl b/src/HPolygon.jl index 55f3ae4a7c..fe3ca5199d 100644 --- a/src/HPolygon.jl +++ b/src/HPolygon.jl @@ -28,7 +28,6 @@ Use `addconstraint!` to iteratively add the edges in a sorted way. -- default constructor - `HPolygon()` -- constructor with no constraints -- `HPolygon(S::LazySet)` -- constructor from another set """ struct HPolygon{N<:Real} <: AbstractHPolygon{N} constraints::Vector{LinearConstraint{N}} @@ -67,16 +66,6 @@ HPolygon{N}() where {N<:Real} = HPolygon{N}(Vector{LinearConstraint{N}}()) # constructor for an HPolygon with no constraints of type Float64 HPolygon() = HPolygon{Float64}() -# conversion constructor -function HPolygon(S::LazySet; validate_boundedness::Bool=false) - P = convert(HPolygon, S) - if validate_boundedness - # trigger boundedness check in constructor - HPolygon(P.constraints; validate_boundedness=true) - end - return P -end - # --- LazySet interface functions --- diff --git a/src/HPolygonOpt.jl b/src/HPolygonOpt.jl index d509e15ca6..2133b02f6b 100644 --- a/src/HPolygonOpt.jl +++ b/src/HPolygonOpt.jl @@ -34,7 +34,6 @@ Use `addconstraint!` to iteratively add the edges in a sorted way. - `HPolygonOpt(constraints::Vector{LinearConstraint{<:Real}}, [ind]::Int)` -- default constructor with optional index -- `HPolygonOpt(S::LazySet)` -- constructor from another set """ mutable struct HPolygonOpt{N<:Real} <: AbstractHPolygon{N} constraints::Vector{LinearConstraint{N}} @@ -76,18 +75,6 @@ HPolygonOpt{N}() where {N<:Real} = HPolygonOpt{N}(Vector{LinearConstraint{N}}()) # constructor for an HPolygonOpt with no constraints of type Float64 HPolygonOpt() = HPolygonOpt{Float64}() -# conversion constructor -# conversion constructor -function HPolygonOpt(S::LazySet; validate_boundedness::Bool=false) - P = convert(HPolygonOpt, S) - if validate_boundedness - # trigger boundedness check in constructor - HPolygonOpt(P.constraints; validate_boundedness=true) - end - return P -end - - # --- LazySet interface functions --- diff --git a/src/HPolytope.jl b/src/HPolytope.jl index bc5f257046..e8b61a203b 100644 --- a/src/HPolytope.jl +++ b/src/HPolytope.jl @@ -45,16 +45,6 @@ HPolytope{N}() where {N<:Real} = HPolytope{N}(Vector{LinearConstraint{N}}()) # constructor for an HPolytope with no constraints of type Float64 HPolytope() = HPolytope{Float64}() -# conversion constructor -function HPolytope(S::LazySet; validate_boundedness::Bool=false) - P = convert(HPolytope, S) - if validate_boundedness - # trigger boundedness check in constructor - HPolytope(P.constraints; validate_boundedness=true) - end - return P -end - # constructor for an HPolytope from a simple H-representation function HPolytope(A::AbstractMatrix{N}, b::AbstractVector{N}; validate_boundedness::Bool=false) where {N<:Real} diff --git a/test/unit_Polygon.jl b/test/unit_Polygon.jl index 670207ca68..9f4f68c8f0 100644 --- a/test/unit_Polygon.jl +++ b/test/unit_Polygon.jl @@ -22,32 +22,32 @@ for N in [Float64, Float32, Rational{Int}] @test p.constraints[4] == c4 # conversion to optimized polygon - po = HPolygonOpt(p) + po = convert(HPolygonOpt, p) # conversion back - HPolygon(po) + convert(HPolygon, po) # conversion from HPolytope polytope = HPolytope{N}() addconstraint!(polytope, c1) addconstraint!(polytope, c2) addconstraint!(polytope, c3) addconstraint!(polytope, c4) - HPolygon(polytope) - HPolygonOpt(polytope) + convert(HPolygon, polytope) + convert(HPolygonOpt, polytope) # conversion to HPolytope - HPolytope(p) - HPolytope(po) + HPolytope(constraints_list(p)) + HPolytope(constraints_list(po)) # conversion from other set type H = Hyperrectangle(low=N[-1, -1], high=N[1, 1]) - HPolygon(H) - HPolygonOpt(H) + HPolygon(constraints_list(H)) + HPolygonOpt(constraints_list(H)) # check boundedness after conversion - HPolygon(H; validate_boundedness=true) - HPolygonOpt(H; validate_boundedness=true) + HPolygon(constraints_list(H); validate_boundedness=true) + HPolygonOpt(constraints_list(H); validate_boundedness=true) # support vector of polygon with no constraints @test_throws AssertionError σ(N[0], HPolygon{N}()) - @test_throws AssertionError σ(N[0], HPolygonOpt(HPolygon{N}())) + @test_throws AssertionError σ(N[0], HPolygonOpt{N}()) # boundedness @test isbounded(p) && isbounded(po) diff --git a/test/unit_Polytope.jl b/test/unit_Polytope.jl index ae30f3d2b9..3e06826e20 100644 --- a/test/unit_Polytope.jl +++ b/test/unit_Polytope.jl @@ -85,7 +85,7 @@ for N in [Float64, Rational{Int}, Float32] vlist = vertices_list(P) @test ispermutation(vlist, [N[3, 3], N[3, -1], N[-1, -1], N[-1, 3]]) # check boundedness after conversion - HPolytope(H; validate_boundedness=true) + HPolytope(constraints_list(H); validate_boundedness=true) # isempty @test !isempty(p) From 46e95695fcb978ad5996c01ab7df289e3678bad2 Mon Sep 17 00:00:00 2001 From: schillic Date: Fri, 18 Jan 2019 20:42:11 +0100 Subject: [PATCH 5/5] merge/rename function/kwarg --- docs/src/lib/interfaces.md | 2 +- docs/src/lib/representations.md | 7 ++--- src/AbstractHPolygon.jl | 27 +++++++++-------- src/HPolygon.jl | 24 +++++++-------- src/HPolygonOpt.jl | 35 ++++++++++----------- src/HPolytope.jl | 54 +++++++++++++++++---------------- test/unit_Polygon.jl | 14 ++++----- test/unit_Polytope.jl | 9 +++--- 8 files changed, 87 insertions(+), 85 deletions(-) diff --git a/docs/src/lib/interfaces.md b/docs/src/lib/interfaces.md index d5b0e9e82d..92f14364f8 100644 --- a/docs/src/lib/interfaces.md +++ b/docs/src/lib/interfaces.md @@ -156,7 +156,7 @@ addconstraint!(::AbstractHPolygon{N}, ::LinearConstraint{N}) where {N<:Real} addconstraint!(::Vector{LinearConstraint{N}}, ::LinearConstraint{N}) where {N<:Real} constraints_list(::AbstractHPolygon{N}) where {N<:Real} vertices_list(::AbstractHPolygon{N}, ::Bool=false, ::Bool=true) where {N<:Real} -validate_boundedness(::AbstractHPolygon) +isbounded(::AbstractHPolygon, ::Bool=true) ``` ### Centrally symmetric polytope diff --git a/docs/src/lib/representations.md b/docs/src/lib/representations.md index b099ee729c..d884f4f5e4 100644 --- a/docs/src/lib/representations.md +++ b/docs/src/lib/representations.md @@ -328,7 +328,6 @@ Inherited from [`LazySet`](@ref): * [`diameter`](@ref diameter(::LazySet, ::Real)) Inherited from [`AbstractPolytope`](@ref): -* [`isbounded`](@ref isbounded(::AbstractPolytope)) * [`isempty`](@ref isempty(::AbstractPolytope)) * [`singleton_list`](@ref singleton_list(::AbstractPolytope{N}) where {N<:Real}) * [`linear_map`](@ref linear_map(::AbstractMatrix{N}, ::AbstractPolytope{N}) where {N<:Real}) @@ -342,6 +341,7 @@ Inherited from [`AbstractHPolygon`](@ref): * [`vertices_list`](@ref vertices_list(::AbstractHPolygon{N}, ::Bool=false, ::Bool=true) where {N<:Real}) * [`tohrep`](@ref tohrep(::HPOLYGON) where {HPOLYGON<:AbstractHPolygon}) * [`tovrep`](@ref tovrep(::AbstractHPolygon{N}) where {N<:Real}) +* [`isbounded`](@ref isbounded(::AbstractHPolygon, ::Bool=true)) * [`addconstraint!`](@ref addconstraint!(::AbstractHPolygon{N}, ::LinearConstraint{N}) where {N<:Real}) * [`constraints_list`](@ref constraints_list(::AbstractHPolygon{N}) where {N<:Real}) @@ -357,7 +357,6 @@ Inherited from [`LazySet`](@ref): * [`diameter`](@ref diameter(::LazySet, ::Real)) Inherited from [`AbstractPolytope`](@ref): -* [`isbounded`](@ref isbounded(::AbstractPolytope)) * [`isempty`](@ref isempty(::AbstractPolytope)) * [`singleton_list`](@ref singleton_list(::AbstractPolytope{N}) where {N<:Real}) * [`linear_map`](@ref linear_map(::AbstractMatrix{N}, ::AbstractPolytope{N}) where {N<:Real}) @@ -371,6 +370,7 @@ Inherited from [`AbstractHPolygon`](@ref): * [`vertices_list`](@ref vertices_list(::AbstractHPolygon{N}, ::Bool=false, ::Bool=true) where {N<:Real}) * [`tohrep`](@ref tohrep(::HPOLYGON) where {HPOLYGON<:AbstractHPolygon}) * [`tovrep`](@ref tovrep(::AbstractHPolygon{N}) where {N<:Real}) +* [`isbounded`](@ref isbounded(::AbstractHPolygon, ::Bool=true)) * [`addconstraint!`](@ref addconstraint!(::AbstractHPolygon{N}, ::LinearConstraint{N}) where {N<:Real}) * [`constraints_list`](@ref constraints_list(::AbstractHPolygon{N}) where {N<:Real}) @@ -460,11 +460,10 @@ The following methods are specific for `HPolytope`. ```@docs rand(::Type{HPolytope}) vertices_list(::HPolytope{N}) where {N<:Real} -validate_boundedness(::HPolytope) +isbounded(::HPolytope, ::Bool=true) ``` Inherited from [`AbstractPolytope`](@ref): -* [`isbounded`](@ref isbounded(::AbstractPolytope)) * [`singleton_list`](@ref singleton_list(::AbstractPolytope{N}) where {N<:Real}) #### Polyhedra diff --git a/src/AbstractHPolygon.jl b/src/AbstractHPolygon.jl index 01f5696363..985a6f386e 100644 --- a/src/AbstractHPolygon.jl +++ b/src/AbstractHPolygon.jl @@ -6,7 +6,7 @@ export AbstractHPolygon, addconstraint!, vertices_list, constraints_list, - validate_boundedness + isbounded # This constant marks the threshold for the number of constraints of a polygon # above which we use a binary search to find the relevant constraint in a @@ -381,28 +381,29 @@ function binary_search_constraints(d::AbstractVector{N}, end """ - validate_boundedness(P::AbstractHPolygon)::Bool + isbounded(P::AbstractHPolygon, [use_type_assumption]::Bool=true)::Bool -Check whether a polygon in constraint representation is indeed bounded. +Determine whether a polygon in constraint representation is bounded. ### Input -- `P` -- polygon in constraint representation +- `P` -- polygon in constraint representation +- `use_type_assumption` -- (optional, default: `true`) flag for ignoring the + type assumption that polygons are bounded ### Output -`true` iff `P` is bounded. +`true` if `use_type_assumption` is activated. +Otherwise, `true` iff `P` is bounded. ### Algorithm -We convert `P` to an `HPolyhedron` `P2` and then use `isbounded(P2)`. - -### Notes - -This function is offered in addition to [`isbounded(P::AbstractPolytope)`](@ref) -which actually always returns `true` because boundedness is an implicit -assumption of `AbstractPolytope`. +If `!use_type_assumption`, we convert `P` to an `HPolyhedron` `P2` and then use +`isbounded(P2)`. """ -function validate_boundedness(P::AbstractHPolygon)::Bool +function isbounded(P::AbstractHPolygon, use_type_assumption::Bool=true)::Bool + if use_type_assumption + return true + end return isbounded(HPolyhedron(P.constraints)) end diff --git a/src/HPolygon.jl b/src/HPolygon.jl index fe3ca5199d..30ad1e64e4 100644 --- a/src/HPolygon.jl +++ b/src/HPolygon.jl @@ -10,13 +10,13 @@ are sorted in counter-clockwise fashion with respect to their normal directions. ### Fields -- `constraints` -- list of linear constraints, sorted by the angle -- `sort_constraints` -- (optional, default: `true`) flag for sorting the - constraints (sortedness is a running assumption of - this type) -- `validate_boundedness` -- (optional, default: `true`) flag for checking if the - constraints make the polygon bounded; (boundedness - is a running assumption of this type) +- `constraints` -- list of linear constraints, sorted by the angle +- `sort_constraints` -- (optional, default: `true`) flag for sorting the + constraints (sortedness is a running assumption of this + type) +- `check_boundedness` -- (optional, default: `false`) flag for checking if the + constraints make the polygon bounded; (boundedness is a + running assumption of this type) ### Notes @@ -35,7 +35,7 @@ struct HPolygon{N<:Real} <: AbstractHPolygon{N} # default constructor that applies sorting of the given constraints function HPolygon{N}(constraints::Vector{LinearConstraint{N}}; sort_constraints::Bool=true, - validate_boundedness::Bool=false) where {N<:Real} + check_boundedness::Bool=false) where {N<:Real} if sort_constraints sorted_constraints = Vector{LinearConstraint{N}}() sizehint!(sorted_constraints, length(constraints)) @@ -46,8 +46,8 @@ struct HPolygon{N<:Real} <: AbstractHPolygon{N} else P = new{N}(constraints) end - @assert (!validate_boundedness || - LazySets.validate_boundedness(P)) "the polygon is not bounded" + @assert (!check_boundedness || + isbounded(P, false)) "the polygon is not bounded" return P end end @@ -55,10 +55,10 @@ end # convenience constructor without type parameter HPolygon(constraints::Vector{LinearConstraint{N}}; sort_constraints::Bool=true, - validate_boundedness::Bool=false) where {N<:Real} = + check_boundedness::Bool=false) where {N<:Real} = HPolygon{N}(constraints; sort_constraints=sort_constraints, - validate_boundedness=validate_boundedness) + check_boundedness=check_boundedness) # constructor for an HPolygon with no constraints HPolygon{N}() where {N<:Real} = HPolygon{N}(Vector{LinearConstraint{N}}()) diff --git a/src/HPolygonOpt.jl b/src/HPolygonOpt.jl index 2133b02f6b..505e89b3ce 100644 --- a/src/HPolygonOpt.jl +++ b/src/HPolygonOpt.jl @@ -11,15 +11,15 @@ This is a refined version of `HPolygon`. ### Fields -- `constraints` -- list of linear constraints -- `ind` -- index in the list of constraints to begin the search - to evaluate the support function -- `sort_constraints` -- (optional, default: `true`) flag for sorting the - constraints (sortedness is a running assumption of - this type) -- `validate_boundedness` -- (optional, default: `true`) flag for checking if the - constraints make the polygon bounded; (boundedness - is a running assumption of this type) +- `constraints` -- list of linear constraints +- `ind` -- index in the list of constraints to begin the search + to evaluate the support function +- `sort_constraints` -- (optional, default: `true`) flag for sorting the + constraints (sortedness is a running assumption of this + type) +- `check_boundedness` -- (optional, default: `false`) flag for checking if the + constraints make the polygon bounded; (boundedness is a + running assumption of this type) ### Notes @@ -32,7 +32,7 @@ The default constructor assumes that the given list of edges is sorted. It *does not perform* any sorting. Use `addconstraint!` to iteratively add the edges in a sorted way. -- `HPolygonOpt(constraints::Vector{LinearConstraint{<:Real}}, [ind]::Int)` +- `HPolygonOpt(constraints::Vector{LinearConstraint{<:Real}}, [ind]::Int=1)` -- default constructor with optional index """ mutable struct HPolygonOpt{N<:Real} <: AbstractHPolygon{N} @@ -43,7 +43,7 @@ mutable struct HPolygonOpt{N<:Real} <: AbstractHPolygon{N} function HPolygonOpt{N}(constraints::Vector{LinearConstraint{N}}, ind::Int=1; sort_constraints::Bool=true, - validate_boundedness::Bool=false) where {N<:Real} + check_boundedness::Bool=false) where {N<:Real} if sort_constraints sorted_constraints = Vector{LinearConstraint{N}}() sizehint!(sorted_constraints, length(constraints)) @@ -54,8 +54,8 @@ mutable struct HPolygonOpt{N<:Real} <: AbstractHPolygon{N} else P = new{N}(constraints, ind) end - @assert (!validate_boundedness || - LazySets.validate_boundedness(P)) "the polygon is not bounded" + @assert (!check_boundedness || + isbounded(P, false)) "the polygon is not bounded" return P end end @@ -64,10 +64,11 @@ end HPolygonOpt(constraints::Vector{LinearConstraint{N}}, ind::Int=1; sort_constraints::Bool=true, - validate_boundedness::Bool=false) where {N<:Real} = - HPolygonOpt{N}(constraints, ind; - sort_constraints=sort_constraints, - validate_boundedness=validate_boundedness) + check_boundedness::Bool=false) where {N<:Real} = + HPolygonOpt{N}(constraints, + ind; + sort_constraints=sort_constraints, + check_boundedness=check_boundedness) # constructor for an HPolygonOpt with no constraints HPolygonOpt{N}() where {N<:Real} = HPolygonOpt{N}(Vector{LinearConstraint{N}}()) diff --git a/src/HPolytope.jl b/src/HPolytope.jl index e8b61a203b..e530985734 100644 --- a/src/HPolytope.jl +++ b/src/HPolytope.jl @@ -2,7 +2,7 @@ import Base.rand export HPolytope, vertices_list, - validate_boundedness + isbounded """ HPolytope{N<:Real} <: AbstractPolytope{N} @@ -11,10 +11,10 @@ Type that represents a convex polytope in H-representation. ### Fields -- `constraints` -- vector of linear constraints -- `validate_boundedness` -- (optional, default: `true`) flag for checking if the - constraints make the polytope bounded; (boundedness - is a running assumption of this type) +- `constraints` -- vector of linear constraints +- `check_boundedness` -- (optional, default: `false`) flag for checking if the + constraints make the polytope bounded; (boundedness is + a running assumption of this type) ### Note @@ -25,19 +25,19 @@ struct HPolytope{N<:Real} <: AbstractPolytope{N} constraints::Vector{LinearConstraint{N}} function HPolytope{N}(constraints::Vector{LinearConstraint{N}}; - validate_boundedness::Bool=false + check_boundedness::Bool=false ) where {N<:Real} P = new{N}(constraints) - @assert (!validate_boundedness || - LazySets.validate_boundedness(P)) "the polytope is not bounded" + @assert (!check_boundedness || + isbounded(P, false)) "the polytope is not bounded" return P end end # convenience constructor without type parameter HPolytope(constraints::Vector{LinearConstraint{N}}; - validate_boundedness::Bool=false) where {N<:Real} = - HPolytope{N}(constraints; validate_boundedness=validate_boundedness) + check_boundedness::Bool=false) where {N<:Real} = + HPolytope{N}(constraints; check_boundedness=check_boundedness) # constructor for an HPolytope with no constraints HPolytope{N}() where {N<:Real} = HPolytope{N}(Vector{LinearConstraint{N}}()) @@ -47,18 +47,18 @@ HPolytope() = HPolytope{Float64}() # constructor for an HPolytope from a simple H-representation function HPolytope(A::AbstractMatrix{N}, b::AbstractVector{N}; - validate_boundedness::Bool=false) where {N<:Real} + check_boundedness::Bool=false) where {N<:Real} m = size(A, 1) constraints = LinearConstraint{N}[] @inbounds for i in 1:m push!(constraints, LinearConstraint(A[i, :], b[i])) end - return HPolytope(constraints; validate_boundedness=validate_boundedness) + return HPolytope(constraints; check_boundedness=check_boundedness) end HPolytope{N}(A::AbstractMatrix{N}, b::AbstractVector{N}; - validate_boundedness::Bool=false) where {N<:Real} = - HPolytope(A, b; validate_boundedness=validate_boundedness) + check_boundedness::Bool=false) where {N<:Real} = + HPolytope(A, b; check_boundedness=check_boundedness) # --- LazySet interface functions --- @@ -107,33 +107,35 @@ function rand(::Type{HPolytope}; end """ - validate_boundedness(P::HPolytope)::Bool + isbounded(P::HPolytope, [use_type_assumption]::Bool=true)::Bool -Check whether a polytope in constraint representation is indeed bounded. +Determine whether a polytope in constraint representation is bounded. ### Input -- `P` -- polytope in constraint representation +- `P` -- polytope in constraint representation +- `use_type_assumption` -- (optional, default: `true`) flag for ignoring the + type assumption that polytopes are bounded ### Output -`true` iff `P` is bounded. +`true` if `use_type_assumption` is activated. +Otherwise, `true` iff `P` is bounded. ### Algorithm -We convert `P` to an `HPolyhedron` `P2` and then use `isbounded(P2)`. - -### Notes - -This function is offered in addition to [`isbounded(P::AbstractPolytope)`](@ref) -which actually always returns `true` because boundedness is an implicit -assumption of `AbstractPolytope`. +If `!use_type_assumption`, we convert `P` to an `HPolyhedron` `P2` and then use +`isbounded(P2)`. """ -function validate_boundedness(P::HPolytope)::Bool +function isbounded(P::HPolytope, use_type_assumption::Bool=true)::Bool + if use_type_assumption + return true + end return isbounded(HPolyhedron(P.constraints)) end + # --- functions that use Polyhedra.jl --- diff --git a/test/unit_Polygon.jl b/test/unit_Polygon.jl index 9f4f68c8f0..073239e3a0 100644 --- a/test/unit_Polygon.jl +++ b/test/unit_Polygon.jl @@ -42,8 +42,8 @@ for N in [Float64, Float32, Rational{Int}] HPolygon(constraints_list(H)) HPolygonOpt(constraints_list(H)) # check boundedness after conversion - HPolygon(constraints_list(H); validate_boundedness=true) - HPolygonOpt(constraints_list(H); validate_boundedness=true) + HPolygon(constraints_list(H); check_boundedness=true) + HPolygonOpt(constraints_list(H); check_boundedness=true) # support vector of polygon with no constraints @test_throws AssertionError σ(N[0], HPolygon{N}()) @@ -51,13 +51,13 @@ for N in [Float64, Float32, Rational{Int}] # boundedness @test isbounded(p) && isbounded(po) - @test validate_boundedness(p) && validate_boundedness(po) - @test !validate_boundedness(HPolygon{N}()) && - !validate_boundedness(HPolygonOpt{N}()) + @test isbounded(p, false) && isbounded(po, false) + @test !isbounded(HPolygon{N}(), false) && + !isbounded(HPolygonOpt{N}(), false) @test_throws AssertionError HPolygon(LinearConstraint{N}[]; - validate_boundedness=true) + check_boundedness=true) @test_throws AssertionError HPolygonOpt(LinearConstraint{N}[]; - validate_boundedness=true) + check_boundedness=true) # isempty @test !isempty(p) diff --git a/test/unit_Polytope.jl b/test/unit_Polytope.jl index 3e06826e20..decde1efcd 100644 --- a/test/unit_Polytope.jl +++ b/test/unit_Polytope.jl @@ -22,7 +22,7 @@ for N in [Float64, Rational{Int}, Float32] @test c[1].a == N[1, 2] && c[1].b == N(1) @test c[2].a == N[-1, 1] && c[2].b == N(2) @test_throws AssertionError HPolytope(N[1 0; 0 1], N[1, 1]; - validate_boundedness=true) + check_boundedness=true) # convert back to matrix and vector A2, b2 = tosimplehrep(p) @@ -53,10 +53,9 @@ for N in [Float64, Rational{Int}, Float32] @test_throws ErrorException σ(N[0], HPolytope{N}()) # boundedness - @test isbounded(p) - @test validate_boundedness(p) + @test isbounded(p) && isbounded(p, false) p2 = HPolytope{N}() - @test isbounded(p2) && !validate_boundedness(p2) + @test isbounded(p2) && !isbounded(p2, false) # membership @test ∈(N[5 / 4, 7 / 4], p) @@ -85,7 +84,7 @@ for N in [Float64, Rational{Int}, Float32] vlist = vertices_list(P) @test ispermutation(vlist, [N[3, 3], N[3, -1], N[-1, -1], N[-1, 3]]) # check boundedness after conversion - HPolytope(constraints_list(H); validate_boundedness=true) + HPolytope(constraints_list(H); check_boundedness=true) # isempty @test !isempty(p)