From fcfae2aca259da7aa68fe8524bc8f779fd9408c9 Mon Sep 17 00:00:00 2001 From: mforets Date: Tue, 22 Jan 2019 15:01:51 -0300 Subject: [PATCH 01/22] add new tosimplehrep and constraints_list methods for lists of constraints --- src/HPolyhedron.jl | 76 ++++++++++++++++++++++++++++++++++++++++----- src/Intersection.jl | 8 ++++- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index b9aa080c53..fa8c437093 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -383,17 +383,37 @@ The tuple `(A, b)` where `A` is the matrix of normal directions and `b` are the offsets. """ function tosimplehrep(P::HPoly{N}) where {N<:Real} - n = length(constraints_list(P)) + return tosimplehrep(P.constraints) +end + +""" + tosimplehrep(constraints::AbstractVector{LinearConstraint{N}}) where {N<:Real} + +Return the simple H-representation ``Ax ≤ b`` of a list of constraints. + +### Input + +- `constraints` -- a list of constraints + +### Output + +The tuple `(A, b)` where `A` is the matrix of normal directions and `b` are the +offsets. +""" +function tosimplehrep(constraints::AbstractVector{LinearConstraint{N}}) where {N<:Real} + n = length(constraints) if n == 0 A = Matrix{N}(undef, 0, 0) b = Vector{N}(undef, 0) return (A, b) end - A = zeros(N, n, dim(P)) + A = zeros(N, n, dim(first(constraints))) b = zeros(N, n) - for (i, Pi) in enumerate(constraints_list(P)) - A[i, :] = Pi.a - b[i] = Pi.b + @inbounds begin + for (i, Pi) in enumerate(constraints) + A[i, :] = Pi.a + b[i] = Pi.b + end end return (A, b) end @@ -476,8 +496,40 @@ FAQ](https://www.cs.mcgill.ca/~fukuda/soft/polyfaq/node24.html). """ function remove_redundant_constraints!(P::PT; backend=GLPKSolverLP())::Bool where {N, PT<:HPoly{N}} + remove_redundant_constraints!(P.constraints, backend=backend) +end - A, b = tosimplehrep(P) +""" + remove_redundant_constraints!(constraints::Vector{LinearConstraint{N}}; + backend=GLPKSolverLP())::Bool where {N} + +Remove the redundant constraints of a given list of linear constraints; the list +is updated in-place. + +### Input + +- `constraints` -- list of constraints +- `backend` -- (optional, default: `GLPKSolverLP`) the numeric LP solver backend + +### Output + +`true` if the method was successful and the list of constraints `constraints` is +modified by removing the redundant constraints, and `false` if the constraints +are infeasible. + +### Algorithm + +If there are `m` constraints in `n` dimensions, this function checks one by one +if each of the `m` constraints is implied by the remaining ones. +To check if the `k`-th constraint is redundant, an LP is formulated. + +For details, see [Fukuda's Polyhedra +FAQ](https://www.cs.mcgill.ca/~fukuda/soft/polyfaq/node24.html). +""" +function remove_redundant_constraints!(constraints::AbstractVector{LinearConstraint{N}}; + backend=GLPKSolverLP())::Bool where {N} + + A, b = tosimplehrep(constraints) m, n = size(A) non_redundant_indices = 1:m @@ -506,10 +558,20 @@ function remove_redundant_constraints!(P::PT; end end - deleteat!(P.constraints, setdiff(1:m, non_redundant_indices)) + deleteat!(constraints, setdiff(1:m, non_redundant_indices)) return true end +function remove_redundant_constraints(constraints::AbstractVector{LinearConstraint{N}}; + backend=GLPKSolverLP())::Bool where {N} + constraints_copy = copy(constraints) + if remove_redundant_constraints!(constraints_copy, backend=backend) + return constraints_copy + else # the constraints are infeasible + return N[] + end +end + """ linear_map(M::AbstractMatrix{N}, P::PT; [cond_tol=DEFAULT_COND_TOL]::Number) where {N<:Real, PT<:HPoly{N}} diff --git a/src/Intersection.jl b/src/Intersection.jl index 794c256b1b..f4cc69c025 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -6,7 +6,8 @@ export Intersection, swap, use_precise_ρ, IntersectionArray, - array + array, + constraints_list """ IntersectionCache @@ -510,6 +511,11 @@ function ∈(x::AbstractVector{N}, cap::Intersection{N})::Bool where {N<:Real} return (x ∈ cap.X) && (x ∈ cap.Y) end +function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, + S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} + constraints = [constraints_list(cap.X); constraints_list(cap.Y)] + return remove_redundant_constraints!(constraints) +end # --- Intersection functions --- From 477f49d881a569a896966a80d2a8a2c2deb65724 Mon Sep 17 00:00:00 2001 From: mforets Date: Tue, 22 Jan 2019 15:13:15 -0300 Subject: [PATCH 02/22] simplify tosimplehrep of a lazyset --- src/AbstractPolytope.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AbstractPolytope.jl b/src/AbstractPolytope.jl index ddb29eb735..c31a3f472d 100644 --- a/src/AbstractPolytope.jl +++ b/src/AbstractPolytope.jl @@ -124,9 +124,8 @@ function isempty(P::AbstractPolytope)::Bool return isempty(vertices_list(P)) end -# TODO simplify this function tosimplehrep(P::LazySet) - return tosimplehrep(HPolyhedron(constraints_list(P))) + return tosimplehrep(constraints_list(P)) end function default_polyhedra_backend(P, N) From 2ad024df334fc2f4647c89a96716ac279775343e Mon Sep 17 00:00:00 2001 From: mforets Date: Tue, 22 Jan 2019 23:45:50 -0300 Subject: [PATCH 03/22] update branch --- docs/src/lib/interfaces.md | 1 + docs/src/lib/representations.md | 1 + src/AbstractPolytope.jl | 4 ---- src/HPolyhedron.jl | 24 ++++++++++++++++++++++-- src/LazySet.jl | 23 +++++++++++++++++++++++ 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/docs/src/lib/interfaces.md b/docs/src/lib/interfaces.md index 92f14364f8..2138fd7c68 100644 --- a/docs/src/lib/interfaces.md +++ b/docs/src/lib/interfaces.md @@ -63,6 +63,7 @@ RecipesBase.apply_recipe(::Dict{Symbol,Any}, ::LazySet) RecipesBase.apply_recipe(::Dict{Symbol,Any}, ::Vector{S}) where {S<:LazySet} RecipesBase.apply_recipe(::Dict{Symbol,Any}, ::LazySet, ::Float64) RecipesBase.apply_recipe(::Dict{Symbol,Any}, ::Vector{S}, ::Float64) where {S<:LazySet} +tosimplehrep(::LazySet) ``` ### Set functions that override Base functions diff --git a/docs/src/lib/representations.md b/docs/src/lib/representations.md index 817d1c435e..9e96a58cba 100644 --- a/docs/src/lib/representations.md +++ b/docs/src/lib/representations.md @@ -173,6 +173,7 @@ constraints_list(::HalfSpace{N}) where {N<:Real} constrained_dimensions(::HalfSpace{N}) where {N<:Real} halfspace_left(::AbstractVector{N}, ::AbstractVector{N}) where {N<:Real} halfspace_right(::AbstractVector{N}, ::AbstractVector{N}) where {N<:Real} +tosimplehrep(::AbstractVector{HalfSpace{N}}) where {N<:Real} ``` Inherited from [`LazySet`](@ref): * [`norm`](@ref norm(::LazySet, ::Real)) diff --git a/src/AbstractPolytope.jl b/src/AbstractPolytope.jl index c31a3f472d..bbc074597c 100644 --- a/src/AbstractPolytope.jl +++ b/src/AbstractPolytope.jl @@ -124,10 +124,6 @@ function isempty(P::AbstractPolytope)::Bool return isempty(vertices_list(P)) end -function tosimplehrep(P::LazySet) - return tosimplehrep(constraints_list(P)) -end - function default_polyhedra_backend(P, N) @assert isdefined(@__MODULE__, :Polyhedra) "this function needs the package 'Polyhedra' to be loaded" error("no default backend for numeric type $N") diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index fa8c437093..7e9344dd63 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -562,13 +562,33 @@ function remove_redundant_constraints!(constraints::AbstractVector{LinearConstra return true end +""" + remove_redundant_constraints(constraints::AbstractVector{LinearConstraint{N}}; + backend=GLPKSolverLP())::Union{AbstractVector{LinearConstraint{N}}, EmptySet{N}} where {N} + +Remove the redundant constraints of a given list of linear constraints + +### Input + +- `constraints` -- list of constraints +- `backend` -- (optional, default: `GLPKSolverLP`) the numeric LP solver backend + +### Output + +A listr of equivalent constraints but removing the redundant ones, or an empty set +if the given constraints are infeasible. + +### Algorithm + +See [`remove_redundant_constraints!`](@ref) for details. +""" function remove_redundant_constraints(constraints::AbstractVector{LinearConstraint{N}}; - backend=GLPKSolverLP())::Bool where {N} + backend=GLPKSolverLP())::Union{AbstractVector{LinearConstraint{N}}, EmptySet{N}} where {N} constraints_copy = copy(constraints) if remove_redundant_constraints!(constraints_copy, backend=backend) return constraints_copy else # the constraints are infeasible - return N[] + return EmptySet{N}() end end diff --git a/src/LazySet.jl b/src/LazySet.jl index 061cd4b6c9..db2445f6e2 100644 --- a/src/LazySet.jl +++ b/src/LazySet.jl @@ -320,3 +320,26 @@ completely independent object. See the documentation of `?deepcopy` for further details. """ copy(S::LazySet) = deepcopy(S) + +""" + tosimplehrep(S::LazySet) + +Return the simple H-representation ``Ax ≤ b`` of a set from its list of +constraints. + +### Input + +- `S` -- set + +### Output + +The tuple `(A, b)` where `A` is the matrix of normal directions and `b` are the +offsets. + +### Notes + +This is a fallback implementation that works only for those sets that can be +represented by a list of linear constraints, and that this list can be calculated +through the `constraints_list` function. +""" +tosimplehrep(S::LazySet) = tosimplehrep(constraints_list(S)) From db2eccf80a903f96db2a27cf5d02021260b8e088 Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Wed, 23 Jan 2019 07:15:40 -0300 Subject: [PATCH 04/22] Update src/HPolyhedron.jl Co-Authored-By: mforets --- src/HPolyhedron.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index 7e9344dd63..f22187133a 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -566,7 +566,7 @@ end remove_redundant_constraints(constraints::AbstractVector{LinearConstraint{N}}; backend=GLPKSolverLP())::Union{AbstractVector{LinearConstraint{N}}, EmptySet{N}} where {N} -Remove the redundant constraints of a given list of linear constraints +Remove the redundant constraints of a given list of linear constraints. ### Input From ce4aa5c1ada619f63d20fc86bd573e1cb1da86da Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Wed, 23 Jan 2019 07:15:49 -0300 Subject: [PATCH 05/22] Update src/HPolyhedron.jl Co-Authored-By: mforets --- src/HPolyhedron.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index f22187133a..4cfb88ac43 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -575,7 +575,7 @@ Remove the redundant constraints of a given list of linear constraints. ### Output -A listr of equivalent constraints but removing the redundant ones, or an empty set +The list of constraints with the redundant ones removed, or an empty set if the given constraints are infeasible. ### Algorithm From 21adcae33260237588e6569927a1e4c791e4adda Mon Sep 17 00:00:00 2001 From: mforets Date: Wed, 23 Jan 2019 07:27:19 -0300 Subject: [PATCH 06/22] remove tosimplehrep from HPolyhedron, docs --- src/LazySet.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/LazySet.jl b/src/LazySet.jl index db2445f6e2..ebc03cf543 100644 --- a/src/LazySet.jl +++ b/src/LazySet.jl @@ -338,8 +338,9 @@ offsets. ### Notes -This is a fallback implementation that works only for those sets that can be -represented by a list of linear constraints, and that this list can be calculated -through the `constraints_list` function. +This function uses `constraints_list(S)`. It is a fallback implementation that +works only for those sets that can be represented exactly by a list of linear +constraints, and that this list is available through the `constraints_list(S)` +function. """ tosimplehrep(S::LazySet) = tosimplehrep(constraints_list(S)) From 34493bb9d4402ab005b5a9e9f3eaa5b274a15cb2 Mon Sep 17 00:00:00 2001 From: mforets Date: Wed, 23 Jan 2019 07:39:02 -0300 Subject: [PATCH 07/22] really remove tosimplehrep --- src/HPolyhedron.jl | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index 4cfb88ac43..411921334d 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -8,7 +8,6 @@ export HPolyhedron, dim, σ, ∈, addconstraint!, constraints_list, - tosimplehrep, tohrep, tovrep, convex_hull, cartesian_product, @@ -368,24 +367,6 @@ function constraints_list(P::HPoly{N} return P.constraints end -""" - tosimplehrep(P::HPoly{N}) where {N} - -Return the simple H-representation ``Ax ≤ b`` of a polyhedron. - -### Input - -- `P` -- polyhedron - -### Output - -The tuple `(A, b)` where `A` is the matrix of normal directions and `b` are the -offsets. -""" -function tosimplehrep(P::HPoly{N}) where {N<:Real} - return tosimplehrep(P.constraints) -end - """ tosimplehrep(constraints::AbstractVector{LinearConstraint{N}}) where {N<:Real} From 0ca36f0af8266cf03e2f7976293ee8b6521d2412 Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Wed, 23 Jan 2019 07:39:23 -0300 Subject: [PATCH 08/22] Update src/LazySet.jl Co-Authored-By: mforets --- src/LazySet.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LazySet.jl b/src/LazySet.jl index ebc03cf543..db8ae68da8 100644 --- a/src/LazySet.jl +++ b/src/LazySet.jl @@ -340,7 +340,7 @@ offsets. This function uses `constraints_list(S)`. It is a fallback implementation that works only for those sets that can be represented exactly by a list of linear -constraints, and that this list is available through the `constraints_list(S)` +constraints, which is available through the `constraints_list(S)` function. """ tosimplehrep(S::LazySet) = tosimplehrep(constraints_list(S)) From bed3e3083542d647799a24dc55c1e8a79dc3111b Mon Sep 17 00:00:00 2001 From: mforets Date: Wed, 23 Jan 2019 08:45:24 -0300 Subject: [PATCH 09/22] update docs --- docs/src/lib/representations.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/src/lib/representations.md b/docs/src/lib/representations.md index 9e96a58cba..f6d52be7fc 100644 --- a/docs/src/lib/representations.md +++ b/docs/src/lib/representations.md @@ -438,7 +438,6 @@ dim(::HPoly{N}) where {N<:Real} ∈(::AbstractVector{N}, ::HPoly{N}) where {N<:Real} addconstraint!(::HPoly{N}, ::LinearConstraint{N}) where {N<:Real} constraints_list(::HPoly{N}) where {N<:Real} -tosimplehrep(::HPoly{N}) where {N<:Real} tohrep(::HPoly{N}) where {N<:Real} isempty(::HPoly{N}) where {N<:Real} cartesian_product(::HPoly{N}, ::HPoly{N}) where {N<:Real} From be1ba561aafe24aa04ccce6aa6c469953aa7fba4 Mon Sep 17 00:00:00 2001 From: mforets Date: Wed, 23 Jan 2019 09:02:17 -0300 Subject: [PATCH 10/22] add export --- src/LazySet.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LazySet.jl b/src/LazySet.jl index db8ae68da8..cb0ba26b72 100644 --- a/src/LazySet.jl +++ b/src/LazySet.jl @@ -10,7 +10,8 @@ export LazySet, an_element, isbounded, isbounded_unit_dimensions, neutral, - absorbing + absorbing, + tosimplehrep """ LazySet{N} From df39fc7658593671bad346c4dc9491043658dc7b Mon Sep 17 00:00:00 2001 From: schillic Date: Wed, 23 Jan 2019 20:12:35 +0100 Subject: [PATCH 11/22] fix return value --- src/Intersection.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Intersection.jl b/src/Intersection.jl index f4cc69c025..e260872b4c 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -514,7 +514,8 @@ end function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} constraints = [constraints_list(cap.X); constraints_list(cap.Y)] - return remove_redundant_constraints!(constraints) + remove_redundant_constraints!(constraints) + return constraints end # --- Intersection functions --- From 91a0a9778f8985bcee7397318bc65ad0c71671fb Mon Sep 17 00:00:00 2001 From: schillic Date: Wed, 23 Jan 2019 20:21:18 +0100 Subject: [PATCH 12/22] add tests --- test/unit_Intersection.jl | 7 +++++++ test/unit_Polyhedron.jl | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/unit_Intersection.jl b/test/unit_Intersection.jl index ba9528f6c8..f1b6453ac2 100644 --- a/test/unit_Intersection.jl +++ b/test/unit_Intersection.jl @@ -27,6 +27,13 @@ for N in [Float64, Rational{Int}, Float32] @test isempty_known(I) @test !isempty(I) + # constraints_list for polytopic intersection + @test ispermutation(constraints_list(I), + [HalfSpace{Float64}(N[1, 0], N(2)), + HalfSpace{Float64}(N[0, 1], N(2)), + HalfSpace{Float64}(N[-1, 0], N(0)), + HalfSpace{Float64}(N[0, -1], N(0))]) + # ================= # IntersectionArray # ================= diff --git a/test/unit_Polyhedron.jl b/test/unit_Polyhedron.jl index ef7b221fc6..3ed3d40f03 100644 --- a/test/unit_Polyhedron.jl +++ b/test/unit_Polyhedron.jl @@ -28,6 +28,13 @@ for N in [Float64, Rational{Int}, Float32] addconstraint!(p, c4) addconstraint!(p, c2) + # tosimplehrep for other polytopic types + A, b = tosimplehrep(BallInf(N[0], N(2))) + @test (A == hcat(N[1; -1]) || A == hcat(N[-1; 1])) && b == N[2, 2] + # tosimplehrep from list of constraints + A, b = tosimplehrep([HalfSpace(N[1], N(2)), HalfSpace(N[-1], N(2))]) + @test (A == hcat(N[1; -1]) || A == hcat(N[-1; 1])) && b == N[2, 2] + # support vector d = N[1, 0] @test σ(d, p) == N[4, 2] @@ -154,6 +161,16 @@ if test_suite_polyhedra Ar, br = tosimplehrep(p1) @test Ar == A[1:2, :] && br == b[1:2] + # removing redundant constraints from a list of constraints + constraints = [HalfSpace(N[1], N(1)), HalfSpace(N[1], N(0))] + constraints2 = remove_redundant_constraints(constraints) + result = remove_redundant_constraints!(constraints) + @test result + @test ispermutation(constraints, constraints2) + constraints = [HalfSpace(N[1], N(0)), HalfSpace(N[-1], N(-1))] + result = remove_redundant_constraints!(constraints) + @test !result + # checking for empty intersection (also test symmetric calls) P = convert(HPolytope, BallInf(zeros(N, 2), N(1))) Q = convert(HPolytope, BallInf(ones(N, 2), N(1))) From db9ced1f1c6f1f864ac9c9cbc15a40aca385cfed Mon Sep 17 00:00:00 2001 From: schillic Date: Wed, 23 Jan 2019 21:33:39 +0100 Subject: [PATCH 13/22] add constraints_list for IntersectionArray --- docs/src/lib/operations.md | 1 + src/Intersection.jl | 58 ++++++++++++++++++++++++++++++++++++++ test/unit_Intersection.jl | 7 +++++ 3 files changed, 66 insertions(+) diff --git a/docs/src/lib/operations.md b/docs/src/lib/operations.md index 149c0c99db..bcb44907cb 100644 --- a/docs/src/lib/operations.md +++ b/docs/src/lib/operations.md @@ -149,6 +149,7 @@ dim(::IntersectionArray) isbounded(::IntersectionArray) ∈(::AbstractVector{N}, ::IntersectionArray{N}) where {N<:Real} array(::IntersectionArray{N, S}) where {N<:Real, S<:LazySet{N}} +constraints_list(::IntersectionArray{N}) where {N<:Real} ``` Inherited from [`LazySet`](@ref): * [`norm`](@ref norm(::LazySet, ::Real)) diff --git a/src/Intersection.jl b/src/Intersection.jl index e260872b4c..356e6bdeb9 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -511,6 +511,30 @@ function ∈(x::AbstractVector{N}, cap::Intersection{N})::Bool where {N<:Real} return (x ∈ cap.X) && (x ∈ cap.Y) end +""" + constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, + S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} + +Return the list of constraints of an intersection of two (polyhedral) sets. + +### Input + +- `cap` -- intersection of two (polyhedral) sets + +### Output + +The list of constraints of the intersection. + +### Notes + +We assume that the underlying sets are polyhedral, i.e., offer a method +`constraints_list`. + +### Algorithm + +We create the polyhedron from the `constraints_list`s of the sets and remove +redundant constraints. +""" function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} constraints = [constraints_list(cap.X); constraints_list(cap.Y)] @@ -518,6 +542,7 @@ function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, return constraints end + # --- Intersection functions --- @@ -711,6 +736,39 @@ function ∈(x::AbstractVector{N}, ia::IntersectionArray{N})::Bool where {N<:Rea return true end +""" + constraints_list(ia::IntersectionArray{N}) where {N<:Real} + +Return the list of constraints of an intersection of a finite number of +(polyhedral) sets. + +### Input + +- `ia` -- intersection of a finite number of (polyhedral) sets + +### Output + +The list of constraints of the intersection. + +### Notes + +We assume that the underlying sets are polyhedral, i.e., offer a method +`constraints_list`. + +### Algorithm + +We create the polyhedron from the `constraints_list`s of the sets and remove +redundant constraints. +""" +function constraints_list(ia::IntersectionArray{N}) where {N<:Real} + constraints = Vector{LinearConstraint{N}}() + for X in array(ia) + append!(constraints, constraints_list(X)) + end + remove_redundant_constraints!(constraints) + return constraints +end + # ================================== # Algorithms for lazy intersection # ================================== diff --git a/test/unit_Intersection.jl b/test/unit_Intersection.jl index f1b6453ac2..af8f5d907d 100644 --- a/test/unit_Intersection.jl +++ b/test/unit_Intersection.jl @@ -71,6 +71,13 @@ for N in [Float64, Rational{Int}, Float32] # constructor with size hint and type IntersectionArray(10, N) + # constraints_list for polytopic intersection + @test ispermutation(constraints_list(IA), + [HalfSpace{Float64}(N[1, 0], N(2)), + HalfSpace{Float64}(N[0, 1], N(2)), + HalfSpace{Float64}(N[-1, 0], N(0)), + HalfSpace{Float64}(N[0, -1], N(0))]) + # ================ # common functions # ================ From 804a0a4d9df752c64e2bba011096813e9c083409 Mon Sep 17 00:00:00 2001 From: mforets Date: Thu, 24 Jan 2019 14:56:45 -0300 Subject: [PATCH 14/22] update remove redundant constraints docstrings --- src/HPolyhedron.jl | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index 411921334d..765da78a3c 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -431,11 +431,12 @@ Remove the redundant constraints in a polyhedron in H-representation. ### Output A polyhedron equivalent to `P` but with no redundant constraints, or an empty set -if `P` is detected to be empty (which happens if the constraints are infeasible). +if `P` is detected to be empty, which may happen if the constraints are infeasible. ### Algorithm -See [`remove_redundant_constraints!`](@ref) for details. +See [`remove_redundant_constraints!(::Vector{LinearConstraint{N}})`](@ref) for +details. """ function remove_redundant_constraints(P::PT; backend=GLPKSolverLP())::Union{PT, EmptySet{N}} where {N, PT<:HPoly{N}} @@ -462,18 +463,13 @@ is updated in-place. ### Output `true` if the method was successful and the polyhedron `P` is modified by -removing its redundant constraints, and `false` if `P` is detected to be empty -(which happens if the constraints are infeasible). +removing its redundant constraints, and `false` if `P` is detected to be empty, +which may happen if the constraints are infeasible. ### Algorithm -If the polyhedron `P` has `m` constraints and its dimension is `n`, -this function checks one by one if each of the `m` constraints is -implied by the remaining ones. To check if the `k`-th constraint -is redundant, an LP is formulated. - -For details, see [Fukuda's Polyhedra -FAQ](https://www.cs.mcgill.ca/~fukuda/soft/polyfaq/node24.html). +See [`remove_redundant_constraints!(::Vector{LinearConstraint{N}})`](@ref) for +details. """ function remove_redundant_constraints!(P::PT; backend=GLPKSolverLP())::Bool where {N, PT<:HPoly{N}} @@ -502,7 +498,15 @@ are infeasible. If there are `m` constraints in `n` dimensions, this function checks one by one if each of the `m` constraints is implied by the remaining ones. -To check if the `k`-th constraint is redundant, an LP is formulated. + +To check if the `k`-th constraint is redundant, an LP is formulated using the +constraints that have not yet being removed. If, at an intermediate step, it is +detected that a subgruop of the constraints is infeasible, this function returns +`false`; if the calculation finished successfully it returns `true`. + +Note that `false` does not imply that the set of constraints is empty; for example, +`x <= 0 && x >= 0` will return `true` without removing any constraint. To check +if the constraints are infeasible use `isempty(HPolyhedron(constraints)`. For details, see [Fukuda's Polyhedra FAQ](https://www.cs.mcgill.ca/~fukuda/soft/polyfaq/node24.html). From e20176801b7f5904ca469cd48398327e44e2ad15 Mon Sep 17 00:00:00 2001 From: schillic Date: Thu, 24 Jan 2019 19:03:57 +0100 Subject: [PATCH 15/22] fix test --- test/unit_Polyhedron.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit_Polyhedron.jl b/test/unit_Polyhedron.jl index 3ed3d40f03..68f2de0101 100644 --- a/test/unit_Polyhedron.jl +++ b/test/unit_Polyhedron.jl @@ -167,7 +167,7 @@ if test_suite_polyhedra result = remove_redundant_constraints!(constraints) @test result @test ispermutation(constraints, constraints2) - constraints = [HalfSpace(N[1], N(0)), HalfSpace(N[-1], N(-1))] + constraints = [HalfSpace(N[1], N(0)), HalfSpace(N[-1], N(-1)), HalfSpace(N[-1], N(-1))] result = remove_redundant_constraints!(constraints) @test !result From 6c98105d830e690eae5cfd7e8398a874e291435a Mon Sep 17 00:00:00 2001 From: mforets Date: Thu, 24 Jan 2019 15:05:04 -0300 Subject: [PATCH 16/22] update constraints list of intersection --- src/Intersection.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Intersection.jl b/src/Intersection.jl index 356e6bdeb9..b9087ad194 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -532,8 +532,13 @@ We assume that the underlying sets are polyhedral, i.e., offer a method ### Algorithm -We create the polyhedron from the `constraints_list`s of the sets and remove -redundant constraints. +We create the polyhedron by taking the intersection of the `constraints_list`s of +the sets and remove redundant constraints. + +If the constraints are infeasible, which may be detected by the in-place version +of this function, then the constraints returned when the infeasibility was detected. +If you want to check for feasibility use the output flag of +`remove_redundant_constraints!`. """ function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} From 03d03aa8f59a17e6b2cdbac4711871e06f088f46 Mon Sep 17 00:00:00 2001 From: mforets Date: Thu, 24 Jan 2019 15:16:16 -0300 Subject: [PATCH 17/22] tweaks in docstring --- src/Intersection.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Intersection.jl b/src/Intersection.jl index b9087ad194..47760a770c 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -535,10 +535,9 @@ We assume that the underlying sets are polyhedral, i.e., offer a method We create the polyhedron by taking the intersection of the `constraints_list`s of the sets and remove redundant constraints. -If the constraints are infeasible, which may be detected by the in-place version -of this function, then the constraints returned when the infeasibility was detected. -If you want to check for feasibility use the output flag of -`remove_redundant_constraints!`. +This function ignores the boolean output from the in-place `remove_redundant_constraints!`, +which may inform the user that the constraints are infeasible. In that case, the +list of constraints at the moment when the infeasibility was detected is returned. """ function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} From 508dca37844d1590341732e5ab3ae1994efe5c8b Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Thu, 24 Jan 2019 15:21:03 -0300 Subject: [PATCH 18/22] Update src/HPolyhedron.jl --- src/HPolyhedron.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index 765da78a3c..f149be1c19 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -505,7 +505,7 @@ detected that a subgruop of the constraints is infeasible, this function returns `false`; if the calculation finished successfully it returns `true`. Note that `false` does not imply that the set of constraints is empty; for example, -`x <= 0 && x >= 0` will return `true` without removing any constraint. To check +`x <= 0 && x >= 1` will return `true` without removing any constraint. To check if the constraints are infeasible use `isempty(HPolyhedron(constraints)`. For details, see [Fukuda's Polyhedra From 8b3b8f0efec49f6f7278c18d2caa60e73e0f4a04 Mon Sep 17 00:00:00 2001 From: mforets Date: Thu, 24 Jan 2019 15:26:39 -0300 Subject: [PATCH 19/22] fix docstring again --- src/HPolyhedron.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index f149be1c19..e4e3794096 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -504,9 +504,9 @@ constraints that have not yet being removed. If, at an intermediate step, it is detected that a subgruop of the constraints is infeasible, this function returns `false`; if the calculation finished successfully it returns `true`. -Note that `false` does not imply that the set of constraints is empty; for example, -`x <= 0 && x >= 1` will return `true` without removing any constraint. To check -if the constraints are infeasible use `isempty(HPolyhedron(constraints)`. +Note that the constraints being infeasible does not imply that `false` is returned. +For example, `x <= 0 && x >= 1` will return `true` without removing any constraint. +To check if the constraints are infeasible use `isempty(HPolyhedron(constraints)`. For details, see [Fukuda's Polyhedra FAQ](https://www.cs.mcgill.ca/~fukuda/soft/polyfaq/node24.html). From c500228f936326e6ed34944c75f40c0262fc98ba Mon Sep 17 00:00:00 2001 From: schillic Date: Thu, 24 Jan 2019 19:40:03 +0100 Subject: [PATCH 20/22] fix type in tests --- test/unit_Intersection.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/unit_Intersection.jl b/test/unit_Intersection.jl index af8f5d907d..efc314b1da 100644 --- a/test/unit_Intersection.jl +++ b/test/unit_Intersection.jl @@ -29,10 +29,10 @@ for N in [Float64, Rational{Int}, Float32] # constraints_list for polytopic intersection @test ispermutation(constraints_list(I), - [HalfSpace{Float64}(N[1, 0], N(2)), - HalfSpace{Float64}(N[0, 1], N(2)), - HalfSpace{Float64}(N[-1, 0], N(0)), - HalfSpace{Float64}(N[0, -1], N(0))]) + [HalfSpace(N[1, 0], N(2)), + HalfSpace(N[0, 1], N(2)), + HalfSpace(N[-1, 0], N(0)), + HalfSpace(N[0, -1], N(0))]) # ================= # IntersectionArray @@ -73,10 +73,10 @@ for N in [Float64, Rational{Int}, Float32] # constraints_list for polytopic intersection @test ispermutation(constraints_list(IA), - [HalfSpace{Float64}(N[1, 0], N(2)), - HalfSpace{Float64}(N[0, 1], N(2)), - HalfSpace{Float64}(N[-1, 0], N(0)), - HalfSpace{Float64}(N[0, -1], N(0))]) + [HalfSpace(N[1, 0], N(2)), + HalfSpace(N[0, 1], N(2)), + HalfSpace(N[-1, 0], N(0)), + HalfSpace(N[0, -1], N(0))]) # ================ # common functions From 0e5994d2021af9119ec5f3aebd271f38c0c612d8 Mon Sep 17 00:00:00 2001 From: schillic Date: Thu, 24 Jan 2019 20:02:53 +0100 Subject: [PATCH 21/22] generalize constraints_list(::Intersection) --- src/Intersection.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Intersection.jl b/src/Intersection.jl index 47760a770c..675c666030 100644 --- a/src/Intersection.jl +++ b/src/Intersection.jl @@ -512,8 +512,7 @@ function ∈(x::AbstractVector{N}, cap::Intersection{N})::Bool where {N<:Real} end """ - constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, - S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} + constraints_list(cap::Intersection{N}) where {N<:Real} Return the list of constraints of an intersection of two (polyhedral) sets. @@ -539,8 +538,7 @@ This function ignores the boolean output from the in-place `remove_redundant_con which may inform the user that the constraints are infeasible. In that case, the list of constraints at the moment when the infeasibility was detected is returned. """ -function constraints_list(cap::Intersection{N, S1, S2}) where {N<:Real, - S1<:AbstractPolytope{N}, S2<:AbstractPolytope{N}} +function constraints_list(cap::Intersection{N}) where {N<:Real} constraints = [constraints_list(cap.X); constraints_list(cap.Y)] remove_redundant_constraints!(constraints) return constraints From f4b6d90344e6d5cd4b2bf612399a9454790da0ae Mon Sep 17 00:00:00 2001 From: schillic Date: Thu, 24 Jan 2019 20:37:54 +0100 Subject: [PATCH 22/22] fix issues in docs --- docs/src/lib/operations.md | 1 + src/HPolyhedron.jl | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/src/lib/operations.md b/docs/src/lib/operations.md index f1e80f608f..cbad8f9823 100644 --- a/docs/src/lib/operations.md +++ b/docs/src/lib/operations.md @@ -120,6 +120,7 @@ dim(::Intersection) isbounded(::Intersection) isempty(::Intersection) ∈(::AbstractVector{N}, ::Intersection{N}) where {N<:Real} +constraints_list(::Intersection{N}) where {N<:Real} isempty_known(::Intersection) set_isempty!(::Intersection, ::Bool) swap(::Intersection) diff --git a/src/HPolyhedron.jl b/src/HPolyhedron.jl index 78418be6bb..e583ed2e3a 100644 --- a/src/HPolyhedron.jl +++ b/src/HPolyhedron.jl @@ -435,8 +435,9 @@ if `P` is detected to be empty, which may happen if the constraints are infeasib ### Algorithm -See [`remove_redundant_constraints!(::Vector{LinearConstraint{N}})`](@ref) for -details. +See +[`remove_redundant_constraints!(::Vector{LinearConstraint{N}}) where {N<:Real}`](@ref) +for details. """ function remove_redundant_constraints(P::PT; backend=GLPKSolverLP())::Union{PT, EmptySet{N}} where {N, PT<:HPoly{N}} @@ -468,8 +469,9 @@ which may happen if the constraints are infeasible. ### Algorithm -See [`remove_redundant_constraints!(::Vector{LinearConstraint{N}})`](@ref) for -details. +See +[`remove_redundant_constraints!(::Vector{LinearConstraint{N}}) where {N<:Real}`](@ref) +for details. """ function remove_redundant_constraints!(P::PT; backend=GLPKSolverLP())::Bool where {N, PT<:HPoly{N}} @@ -565,7 +567,9 @@ if the given constraints are infeasible. ### Algorithm -See [`remove_redundant_constraints!`](@ref) for details. +See +[`remove_redundant_constraints!(::AbstractVector{LinearConstraint{N}}) where {N<:Real}`](@ref) +for details. """ function remove_redundant_constraints(constraints::AbstractVector{LinearConstraint{N}}; backend=GLPKSolverLP())::Union{AbstractVector{LinearConstraint{N}}, EmptySet{N}} where {N}