diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 00000000..e4b544ee --- /dev/null +++ b/NEWS.md @@ -0,0 +1,14 @@ +# Polyhedra.jl v0.8 Release Notes + +## Load time improvements +- Dependencies on JuMP.jl, RecipesBase.jl, and GeometryBasics.jl were moved to + weak dependencies on Julia versions supporting package extensions, i.e. v1.9 + and above. On v1.10 this reduces installation time by 15% and load time by + 18% (see [#328]). + +## Breaking changes +- `JuMP.optimizer_with_attributes` is no longer exported. Call it from JuMP.jl instead. +- The following change is only breaking on Julia v1.9 and above: + `Polyhedra.Mesh` is now implemented in a package extension requiring + GeometryBasics.jl. It is sufficient to load your plotting package, i.e. + Makie.jl or MeshCat.jl, **before** calling `Polyhedra.Mesh` \ No newline at end of file diff --git a/Project.toml b/Project.toml index 99b32e04..b0db9219 100644 --- a/Project.toml +++ b/Project.toml @@ -6,18 +6,33 @@ version = "0.7.7" [deps] GenericLinearAlgebra = "14197337-ba66-59df-a3e3-ca00e7dcff7a" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +[weakdeps] +GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + +[extensions] +PolyhedraGeometryBasicsExt = "GeometryBasics" +PolyhedraJuMPExt = "JuMP" +PolyhedraRecipesBaseExt = "RecipesBase" + [compat] GenericLinearAlgebra = "0.2, 0.3" GeometryBasics = "0.2, 0.3, 0.4" JuMP = "0.23, 1" +LinearAlgebra = "1.6" +MathOptInterface = "1" MutableArithmetics = "1" RecipesBase = "0.7, 0.8, 1.0" +SparseArrays = "1.6" StaticArrays = "0.12, 1.0" julia = "1.6" + diff --git a/docs/src/plot.md b/docs/src/plot.md index a3903794..4770dbe1 100644 --- a/docs/src/plot.md +++ b/docs/src/plot.md @@ -63,26 +63,25 @@ Polyhedron DefaultPolyhedron{Rational{BigInt}, Polyhedra.Intersection{Rational{B Ray(Rational{BigInt}[0, 0, 1]) ``` -Then, we need to create a mesh from the polyhedron: -```jldoctest plots3 -julia> m = Polyhedra.Mesh(p) -Polyhedra.Mesh{3, Rational{BigInt}, DefaultPolyhedron{Rational{BigInt}, Polyhedra.Intersection{Rational{BigInt}, Vector{Rational{BigInt}}, Int64}, Polyhedra.Hull{Rational{BigInt}, Vector{Rational{BigInt}}, Int64}}}(convexhull([0, 0, 0]) + convexhull(Ray(Rational{BigInt}[1, 0, 0]), Ray(Rational{BigInt}[0, 1, 0]), Ray(Rational{BigInt}[0, 0, 1])), nothing, nothing, nothing) -``` - -```@docs -Polyhedra.Mesh -``` - -The polyhedron can be plotted with [MeshCat](https://github.com/rdeits/MeshCat.jl) as follows +The polyhedron can then be plotted with [MeshCat](https://github.com/rdeits/MeshCat.jl) as follows ```julia julia> using MeshCat +julia> m = Polyhedra.Mesh(p) + julia> vis = Visualizer() julia> setobject!(vis, m) julia> open(vis) ``` + +Note that the `Mesh` object should be created **after** loading the plotting package: + +```@docs +Polyhedra.Mesh +``` + To plot it in a notebook, replace `open(vis)` with `IJuliaCell(vis)`. To plot it with [Makie](https://github.com/JuliaPlots/Makie.jl) instead, you can use for instance `mesh` or `wireframe`. diff --git a/examples/3D Plotting a projection of the 4D permutahedron.ipynb b/examples/3D Plotting a projection of the 4D permutahedron.ipynb index be2d91e3..f8c18ea0 100644 --- a/examples/3D Plotting a projection of the 4D permutahedron.ipynb +++ b/examples/3D Plotting a projection of the 4D permutahedron.ipynb @@ -77,23 +77,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To get a plottable object, we transform the polyhedron into a mesh as follows." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "m = Polyhedra.Mesh(p3);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now plot this mesh with [MeshCat](https://github.com/rdeits/MeshCat.jl) or [Makie](https://github.com/JuliaPlots/Makie.jl) as follows:" + "We can now plot this polyhedron with [MeshCat](https://github.com/rdeits/MeshCat.jl) or [Makie](https://github.com/JuliaPlots/Makie.jl) as follows:" ] }, { @@ -129,6 +113,7 @@ ], "source": [ "using MeshCat\n", + "m = Polyhedra.Mesh(p3)\n", "vis = Visualizer()\n", "setobject!(vis, m)\n", "IJuliaCell(vis)" diff --git a/examples/Polyhedral Function.ipynb b/examples/Polyhedral Function.ipynb index 49358a5a..4f951718 100644 --- a/examples/Polyhedral Function.ipynb +++ b/examples/Polyhedral Function.ipynb @@ -840,15 +840,6 @@ "The top of the shape will have no face as it is unbounded in this direction." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "m = Polyhedra.Mesh(p)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -856,6 +847,7 @@ "outputs": [], "source": [ "using MeshCat\n", + "m = Polyhedra.Mesh(p)\n", "vis = Visualizer()\n", "setobject!(vis, m)\n", "IJuliaCell(vis)" diff --git a/src/decompose.jl b/ext/PolyhedraGeometryBasicsExt.jl similarity index 87% rename from src/decompose.jl rename to ext/PolyhedraGeometryBasicsExt.jl index 949f72a3..ae9e3090 100644 --- a/src/decompose.jl +++ b/ext/PolyhedraGeometryBasicsExt.jl @@ -1,11 +1,18 @@ +module PolyhedraGeometryBasicsExt + +using LinearAlgebra import GeometryBasics +using Polyhedra +using Polyhedra: FullDim, typed_fulldim, isapproxzero, _planar_hull, counterclockwise, rotate + +using StaticArrays """ struct Mesh{N, T, PT <: Polyhedron{T}} <: GeometryBasics.GeometryPrimitive{N, T} polyhedron::PT - coordinates::Union{Nothing, Vector{GeometryBasics.Point{3, T}}} - faces::Union{Nothing, Vector{GeometryBasics.TriangleFace{Int}}} - normals::Union{Nothing, Vector{GeometryBasics.Point{3, T}}} + coordinates::Vector{GeometryBasics.Point{3, T}} + faces::Vector{GeometryBasics.TriangleFace{Int}} + normals::Vector{GeometryBasics.Point{3, T}} end Mesh wrapper type that inherits from `GeometryPrimitive` to be used for plotting @@ -14,12 +21,12 @@ instead if it is known that `p` is defined in a 3-dimensional space. """ mutable struct Mesh{N, T, PT <: Polyhedron{T}} <: GeometryBasics.GeometryPrimitive{N, T} polyhedron::PT - coordinates::Union{Nothing, Vector{GeometryBasics.Point{N, T}}} - faces::Union{Nothing, Vector{GeometryBasics.TriangleFace{Int}}} - normals::Union{Nothing, Vector{GeometryBasics.Point{3, T}}} + coordinates::Vector{GeometryBasics.Point{N, T}} + faces::Vector{GeometryBasics.TriangleFace{Int}} + normals::Vector{GeometryBasics.Point{3, T}} end function Mesh{N}(polyhedron::Polyhedron{T}) where {N, T} - return Mesh{N, T, typeof(polyhedron)}(polyhedron, nothing, nothing, nothing) + return Mesh{N, T, typeof(polyhedron)}(polyhedron, [], [], []) end function Mesh(polyhedron::Polyhedron, ::StaticArrays.Size{N}) where N return Mesh{N[1]}(polyhedron) @@ -29,12 +36,12 @@ function Mesh(polyhedron::Polyhedron, N::Int) # use polyhedron built from StaticArrays vector to avoid that. return Mesh{N}(polyhedron) end -function Mesh(polyhedron::Polyhedron) - return Mesh(polyhedron, FullDim(polyhedron)) +function Polyhedra.Mesh(polyhedron::Polyhedron) + return Mesh(polyhedron, typed_fulldim(polyhedron)) end function fulldecompose!(mesh::Mesh) - if mesh.coordinates === nothing + if isempty(mesh.coordinates) mesh.coordinates, mesh.faces, mesh.normals = fulldecompose(mesh) end return @@ -189,7 +196,7 @@ function fulldecompose(poly_geom::Mesh, ::Type{T}) where T exit_point = scene(poly_geom, T) triangles = _Tri{2,T}[] z = StaticArrays.SVector(zero(T), zero(T), one(T)) - decompose_plane!(triangles, FullDim(poly), z, collect(points(poly)), lines(poly), rays(poly), exit_point, counterclockwise, rotate) + decompose_plane!(triangles, typed_fulldim(poly), z, collect(points(poly)), lines(poly), rays(poly), exit_point, counterclockwise, rotate) return fulldecompose(triangles) end @@ -201,7 +208,7 @@ function fulldecompose(poly_geom::Mesh{3}, ::Type{T}) where T zray = get(poly, hidx).a counterclockwise(a, b) = dot(cross(a, b), zray) rotate(r) = cross(zray, r) - decompose_plane!(triangles, FullDim(poly), zray, incidentpoints(poly, hidx), incidentlines(poly, hidx), incidentrays(poly, hidx), exit_point, counterclockwise, rotate) + decompose_plane!(triangles, typed_fulldim(poly), zray, incidentpoints(poly, hidx), incidentlines(poly, hidx), incidentrays(poly, hidx), exit_point, counterclockwise, rotate) end for hidx in eachindex(hyperplanes(poly)) decompose_plane(hidx) @@ -223,3 +230,5 @@ GeometryBasics.coordinates(poly::Mesh) = (fulldecompose!(poly); poly.coordinates GeometryBasics.faces(poly::Mesh) = (fulldecompose!(poly); poly.faces) GeometryBasics.texturecoordinates(poly::Mesh) = nothing GeometryBasics.normals(poly::Mesh) = (fulldecompose!(poly); poly.normals) + +end diff --git a/ext/PolyhedraJuMPExt.jl b/ext/PolyhedraJuMPExt.jl new file mode 100644 index 00000000..dfd92467 --- /dev/null +++ b/ext/PolyhedraJuMPExt.jl @@ -0,0 +1,53 @@ +module PolyhedraJuMPExt + +import JuMP +import Polyhedra: hrep, LPHRep, polyhedron, _optimize! +using Polyhedra: Rep, Projection, _moi_set, fulldim, dimension_names, PolyhedraToLPBridge, ProjectionBridge + +""" + hrep(model::JuMP.Model) + +Builds an H-representation from the feasibility set of the JuMP model `model`. +Note that if non-linear constraint are present in the model, they are ignored. +""" +hrep(model::JuMP.Model) = LPHRep(model) +LPHRep(model::JuMP.Model) = LPHRep(JuMP.backend(model)) +polyhedron(model::JuMP.Model, args...) = polyhedron(hrep(model), args...) +_optimize!(model::JuMP.Model) = JuMP.optimize!(model) + +struct VariableInSet{V <: JuMP.ScalarVariable, S <: Union{Rep, Projection}} <: JuMP.AbstractVariable + variables::Vector{V} + set::S +end +function JuMP.build_variable(error_fun::Function, variables::Vector{<:JuMP.ScalarVariable}, set::Union{Rep, Projection}) + if length(variables) != fulldim(set) + error("Number of variables ($(length(variables))) does not match the full dimension of the polyhedron ($(fulldim(set))).") + end + return VariableInSet(variables, set) +end +function JuMP.add_variable(model::JuMP.AbstractModel, v::VariableInSet, names) + dim_names = dimension_names(v.set) + if dim_names !== nothing + names = copy(names) + for i in eachindex(names) + if isempty(names[i]) && !isempty(dim_names[i]) + names[i] = dim_names[i] + end + end + end + JuMP.add_bridge(model, PolyhedraToLPBridge) + JuMP.add_bridge(model, ProjectionBridge) + return JuMP.add_variable(model, JuMP.VariablesConstrainedOnCreation(v.variables, _moi_set(v.set)), names) +end +function JuMP.build_constraint(error_fun::Function, func, set::Rep) + return JuMP.BridgeableConstraint( + JuMP.build_constraint(error_fun, func, _moi_set(set)), + PolyhedraToLPBridge) +end +function JuMP.build_constraint(error_fun::Function, func, set::Projection) + return JuMP.BridgeableConstraint(JuMP.BridgeableConstraint( + JuMP.build_constraint(error_fun, func, _moi_set(set)), + ProjectionBridge), PolyhedraToLPBridge) +end + +end diff --git a/src/recipe.jl b/ext/PolyhedraRecipesBaseExt.jl similarity index 86% rename from src/recipe.jl rename to ext/PolyhedraRecipesBaseExt.jl index 2919a043..dd341923 100644 --- a/src/recipe.jl +++ b/ext/PolyhedraRecipesBaseExt.jl @@ -1,4 +1,9 @@ +module PolyhedraRecipesBaseExt + +using LinearAlgebra using RecipesBase +using Polyhedra +using Polyhedra: basis, _semi_hull function planar_contour(p::Polyhedron) if fulldim(p) != 2 @@ -17,7 +22,7 @@ function planar_contour(p::Polyhedron) error("Plotting empty polyhedron is not supported.") end sort!(ps, by = x -> x[1]) - counterclockwise(p1, p2) = dot(cross([p1; 0], [p2; 0]), [0, 0, 1]) + counterclockwise = (p1, p2) -> dot(cross([p1; 0], [p2; 0]), [0, 0, 1]) sweep_norm = basis(eltype(ps), fulldim(p), 1) top = _semi_hull(ps, 1, counterclockwise, sweep_norm) bot = _semi_hull(ps, -1, counterclockwise, sweep_norm) @@ -39,3 +44,5 @@ end legend --> false planar_contour(p) end + +end diff --git a/src/Polyhedra.jl b/src/Polyhedra.jl index 5f1c0ff7..116911b5 100644 --- a/src/Polyhedra.jl +++ b/src/Polyhedra.jl @@ -12,14 +12,14 @@ import GenericLinearAlgebra import MutableArithmetics const MA = MutableArithmetics +import MathOptInterface as MOI +import MathOptInterface.Utilities as MOIU + export Polyhedron abstract type Library end abstract type Polyhedron{T} end -using JuMP -export optimizer_with_attributes - coefficient_type(::Union{AbstractVector{T}, Type{<:AbstractVector{T}}}) where T = T similar_type(::Type{<:Vector}, ::Int, ::Type{T}) where T = Vector{T} similar_type(::Type{SparseVector{S, IT}}, ::Int, ::Type{T}) where {S, IT, T} = SparseVector{T, IT} @@ -61,7 +61,7 @@ matrixtype(::Type{<:AbstractVector{T}}) where T = Matrix{T} matrixtype(::Type{<:AbstractSparseVector{T}}) where T = SparseMatrixCSC{T, Int} function similar_type(::Type{ET}, ::Type{Tout}) where {Tout, ET<:Union{HRepElement, VRepElement, Rep}} - similar_type(ET, FullDim(ET), Tout) + similar_type(ET, typed_fulldim(ET), Tout) end function similar_type(::Type{ET}, d::FullDim) where {ET<:Union{HRepElement, VRepElement, Rep}} similar_type(ET, d, coefficient_type(ET)) @@ -81,7 +81,6 @@ include("extended.jl") include("vecrep.jl") include("mixedrep.jl") include("lphrep.jl") -include("jump.jl") include("matrep.jl") include("liftedrep.jl") include("doubledescription.jl") # FIXME move it after projection.jl once it stops depending on LiftedRep @@ -100,7 +99,23 @@ include("projection_opt.jl") # Visualization include("show.jl") -include("recipe.jl") -include("decompose.jl") + +""" + Mesh(p::Polyhedron) + +Returns wrapper of a polyhedron suitable for plotting with MeshCat.jl and Makie.jl. + +!!! note "Extension in Julia 1.9 and above" + Although we require `using GeometryBasics` to use this function in Julia 1.9 and above, + in most use cases this extension dependency is loaded by the plotting package and no + further action is required. +""" +Mesh(p) = p isa Polyhedron ? error("this method requires using GeometryBasics") : throw(MethodError(Mesh, p)) + +if !isdefined(Base, :get_extension) + include("../ext/PolyhedraJuMPExt.jl") + include("../ext/PolyhedraRecipesBaseExt.jl") + include("../ext/PolyhedraGeometryBasicsExt.jl") +end end # module diff --git a/src/aff.jl b/src/aff.jl index 3c415998..dc4b3748 100644 --- a/src/aff.jl +++ b/src/aff.jl @@ -59,7 +59,7 @@ function HyperPlanesIntersection(d::FullDim, end HyperPlanesIntersection{T, AT}(d::FullDim) where {T, AT} = HyperPlanesIntersection(d, HyperPlane{T, AT}[]) -FullDim(h::HyperPlanesIntersection) = h.d +typed_fulldim(h::HyperPlanesIntersection) = h.d hvectortype(L::Type{<:HyperPlanesIntersection{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:HyperPlanesIntersection}, d::FullDim, ::Type{T}) where T = HyperPlanesIntersection{T, similar_type(hvectortype(PT), d, T), typeof(d)} @@ -101,7 +101,7 @@ function affinehull(h::HRep, current=false) if !current detecthlinearity!(h) end - HyperPlanesIntersection(FullDim(h), hyperplanes(h)) + HyperPlanesIntersection(typed_fulldim(h), hyperplanes(h)) end function detecthlinearity!(L::HyperPlanesIntersection, solver = nothing) @@ -113,7 +113,7 @@ function detecthlinearity!(L::HyperPlanesIntersection, solver = nothing) return L end function detecthlinearity(L::HyperPlanesIntersection{T, AT}, solver=nothing) where {T, AT} - H = HyperPlanesIntersection{T, AT}(FullDim(L)) + H = HyperPlanesIntersection{T, AT}(typed_fulldim(L)) for h in hyperplanes(L) hp = remproj(h, H) if !(h in H) @@ -127,9 +127,9 @@ end struct VEmptySpace{T, AT, D <: FullDim} <: VLinearSpace{T} d::D end -FullDim(v::VEmptySpace) = v.d +typed_fulldim(v::VEmptySpace) = v.d function emptyspace(v::VRep{T}) where {T} - d = FullDim(v) + d = typed_fulldim(v) return VEmptySpace{T, vvectortype(typeof(v)), typeof(d)}(d) end @@ -162,7 +162,7 @@ function LinesHull(d::FullDim, it::ElemIt{Line{T, AT}}) where {T, AT} end LinesHull{T, AT}(d::FullDim) where {T, AT} = LinesHull(d, Line{T, AT}[]) -FullDim(v::LinesHull) = v.d +typed_fulldim(v::LinesHull) = v.d vvectortype(::Type{<:LinesHull{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:LinesHull}, d::FullDim, ::Type{T}) where T = LinesHull{T, similar_type(vvectortype(PT), d, T), typeof(d)} @@ -189,7 +189,7 @@ function linespace(v::VRep, current=false) if !current detectvlinearity!(v) end - LinesHull(FullDim(v), lines(v)) + LinesHull(typed_fulldim(v), lines(v)) end function detectvlinearity!(L::LinesHull, solver = nothing) @@ -201,7 +201,7 @@ function detectvlinearity!(L::LinesHull, solver = nothing) return L end function detectvlinearity(L::LinesHull{T, AT}, solver = nothing) where {T, AT} - V = LinesHull{T, AT}(FullDim(L)) + V = LinesHull{T, AT}(typed_fulldim(L)) V.orthogonalized = true for l in lines(L) convexhull!(V, l) diff --git a/src/center.jl b/src/center.jl index 7a23254e..8dbc361c 100644 --- a/src/center.jl +++ b/src/center.jl @@ -1,5 +1,4 @@ export maximum_radius_with_center, hchebyshevcenter, vchebyshevcenter, chebyshevcenter -using JuMP """ maximum_radius_with_center(h::HRep, center) @@ -45,7 +44,7 @@ function _shrink(p::HRep, radius) # solver. `radius` is of type `Float64` so `T = typeof(radius)` is adequate. T = typeof(radius) f = (i, h) -> _shrink(h, radius, T) - d = FullDim(p) + d = typed_fulldim(p) return similar(p, d, T, hmap(f, d, T, p)...) end diff --git a/src/default.jl b/src/default.jl index 237d7d09..03a0c309 100644 --- a/src/default.jl +++ b/src/default.jl @@ -33,7 +33,7 @@ do `default_library(2, Float64)`. Given an `StaticArrays.SVector` `v`, to obtain a default library for points of the type of `v` in a type stable way, do -`default_library(Polyhedra.FullDim(v), eltype(v))`. +`default_library(Polyhedra.typed_fulldim(v), eltype(v))`. """ function default_library end @@ -186,6 +186,6 @@ function Base.similar(p::Tuple{Vararg{Rep}}, d::FullDim, it::It...; kws...) return similar(p, d, T, it...; kws...) end function Base.similar(p::Tuple{Vararg{Rep}}, it::It...; kws...) - return similar(p, FullDim(p[1]), it...; kws...) + return similar(p, typed_fulldim(p[1]), it...; kws...) end Base.similar(p::Rep, args...; kws...) = similar((p,), args...; kws...) diff --git a/src/defaultlibrary.jl b/src/defaultlibrary.jl index 5177eaf5..4c579316 100644 --- a/src/defaultlibrary.jl +++ b/src/defaultlibrary.jl @@ -39,7 +39,7 @@ function DefaultPolyhedron{T, HRepT, VRepT}(vrep::VRepresentation, solver) where DefaultPolyhedron{T, HRepT, VRepT}(convert(VRepT, vrep), solver) end -FullDim(p::DefaultPolyhedron) = FullDim_rep(p.hrep, p.vrep) +typed_fulldim(p::DefaultPolyhedron) = FullDim_rep(p.hrep, p.vrep) library(::Type{<:DefaultPolyhedron{T}}) where {T} = DefaultLibrary{T}() library(p::DefaultPolyhedron{T}) where {T} = DefaultLibrary{T}(p.solver) default_solver(p::DefaultPolyhedron; T = nothing) = p.solver diff --git a/src/dimension.jl b/src/dimension.jl index 6aca510b..5de3d883 100644 --- a/src/dimension.jl +++ b/src/dimension.jl @@ -11,18 +11,19 @@ similar_type(::Type{<:Vector}, ::StaticArrays.Size, T::Type) = Vector{T} # StaticArrays.Size for StaticArrays.SVector # This allows similar_type to be compiled as only one method for Vector # and SparseVector and be type stable for StaticArrays.SVector + +const FullDim = Union{Int, StaticArrays.Size} + """ - FullDim(p)::FullDim + typed_fulldim(p)::FullDim Similar to [`fulldim`](@ref) but used for type stability with the vector type. If the vector type is `StaticArrays.SVector` then it returns a `StaticArrays.Size`. """ -const FullDim = Union{Int, StaticArrays.Size} - -FullDim(::Type{<:AbstractVector}) = -1 # Shouldn't hurt as it will not be used -FullDim(v::AbstractVector) = length(v) -FullDim(v::Union{StaticArrays.SVector{N}, Type{<:StaticArrays.SVector{N}}}) where N = StaticArrays.Size(v) +typed_fulldim(::Type{<:AbstractVector}) = -1 # Shouldn't hurt as it will not be used +typed_fulldim(v::AbstractVector) = length(v) +typed_fulldim(v::Union{StaticArrays.SVector{N}, Type{<:StaticArrays.SVector{N}}}) where N = StaticArrays.Size(v) function FullDim_convert(::Type{StaticArrays.Size{N}}, d::Int) where N @assert N[1] == d @@ -44,7 +45,7 @@ Returns the dimension of the space in which polyhedron, representation, element or vector is defined. That is, a straight line in a 3D space has `fulldim` 3 even if its dimension is 1. """ -fulldim(p) = fulldim(FullDim(p)) +fulldim(p) = fulldim(typed_fulldim(p)) function fulldim end diff --git a/src/doubledescription.jl b/src/doubledescription.jl index 3eb7dbbb..0961a32b 100644 --- a/src/doubledescription.jl +++ b/src/doubledescription.jl @@ -15,7 +15,7 @@ function dualfullspace(rep::Representation, d::FullDim, ::Type{T}) where T dualfullspace(rep, d, T, polyvectortype(similar_type(vectortype(rep), d, T))) end function dualfullspace(rep::Representation{T}) where T - dualfullspace(rep, FullDim(rep), polytypefor(T)) + dualfullspace(rep, typed_fulldim(rep), polytypefor(T)) end """ diff --git a/src/elements.jl b/src/elements.jl index 31f2bef6..4a016e01 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -76,7 +76,7 @@ Base.:(*)(α::Real, h::HalfSpace) = HalfSpace(α * h.a, α * h.β) function Base.:(/)(h::ElemT, P::UniformScaling) where {T, ElemT<:HRepElement{T}} Tout = MA.promote_operation(*, eltype(P), T) - ElemTout = similar_type(ElemT, FullDim(h), Tout) + ElemTout = similar_type(ElemT, typed_fulldim(h), Tout) ElemTout(P * _vec(Tout, h.a), Tout(h.β)) end function Base.:(/)(h::ElemT, P::AbstractMatrix) where {T, ElemT<:HRepElement{T}} @@ -198,8 +198,8 @@ const StructElement{T, AT} = Union{VStruct{T, AT}, HRepElement{T, AT}} vectortype(::Type{<:StructElement{T, AT}}) where {T, AT} = AT vectortype(AT::Type{<:AbstractVector}) = AT -FullDim(::Type{<:StructElement{T, AT}}) where {T, AT} = FullDim(AT) -FullDim(el::StructElement) = FullDim(coord(el)) +typed_fulldim(::Type{<:StructElement{T, AT}}) where {T, AT} = typed_fulldim(AT) +typed_fulldim(el::StructElement) = typed_fulldim(coord(el)) coefficient_type(::Union{RepElement{T}, Type{<:RepElement{T}}}) where {T} = T islin(::Union{Line, Type{<:Line}}) = true diff --git a/src/extended.jl b/src/extended.jl index f81f8828..fe0154e9 100644 --- a/src/extended.jl +++ b/src/extended.jl @@ -57,7 +57,7 @@ end # TODO should be cartesian product with FullSpace function zeropad(p::HRep{T}, padding...) where T - d = map_fulldim(N -> N + abs(padding[1]), FullDim(p)) + d = map_fulldim(N -> N + abs(padding[1]), typed_fulldim(p)) f = (i, el) -> zeropad(el, padding...) return similar(p, d, T, hmap(f, d, T, p)...; dimension_map = i -> _pad_map(i, fulldim(p), padding...)) @@ -82,7 +82,7 @@ of `p1` and `p2`. SIAM Journal on Algebraic Discrete Methods, 6(3), pp.466-486. """ function convexhull(p1::HRepresentation, p2::HRepresentation) - d = FullDim(p1) + d = typed_fulldim(p1) T = promote_coefficient_type((p1, p2)) d_2 = map_fulldim(N -> 2N, d) d_3 = map_fulldim(N -> 3N, d) diff --git a/src/fulldim.jl b/src/fulldim.jl index 2552f246..7a8abcb3 100644 --- a/src/fulldim.jl +++ b/src/fulldim.jl @@ -1,13 +1,13 @@ FullDim_rec() = error("Cannot infer dimension of polyhedron constructed from no element.") function FullDim_rec(it::It, its::Union{Rep, It}...) if vectortype(eltype(it)) <: StaticArrays.SVector - return FullDim(eltype(it)) + return typed_fulldim(eltype(it)) elseif isempty(it) return FullDim_rec(its...) else - return FullDim(first(it)) + return typed_fulldim(first(it)) end end -FullDim_rep(rep::Rep, other_reps::Union{Nothing, Rep}...) = FullDim(rep) +FullDim_rep(rep::Rep, other_reps::Union{Nothing, Rep}...) = typed_fulldim(rep) FullDim_rep(rep::Nothing, other_reps::Union{Nothing, Rep}...) = FullDim_rep(other_reps...) diff --git a/src/interval.jl b/src/interval.jl index f31dbc23..9dd1b3b0 100644 --- a/src/interval.jl +++ b/src/interval.jl @@ -9,7 +9,7 @@ The library is also used as a fallback for libraries that do not support 1-dimen struct IntervalLibrary{T} <: Library end -similar_library(lib::IntervalLibrary, d::FullDim, ::Type{T}) where T = default_library(d, T) # default_library allows to fallback to DefaultLibrary if d is not FullDim(1) +similar_library(lib::IntervalLibrary, d::FullDim, ::Type{T}) where T = default_library(d, T) # default_library allows to fallback to DefaultLibrary if d is not typed_fulldim(1) mutable struct Interval{T, AT, D} <: Polyhedron{T} hrep::Intersection{T, AT, D} @@ -22,7 +22,7 @@ Interval{T, AT, D}(d::FullDim, it::VIt...) where {T, AT, D} = _vinterval(Hull{T, # If AT is an SVector, it will be StaticArrays.Size{(1,)} # otherwise, it will be 1 -FullDim(p::Interval) = FullDim(p.hrep) +typed_fulldim(p::Interval) = typed_fulldim(p.hrep) library(::Union{Interval{T}, Type{<:Interval{T}}}) where T = IntervalLibrary{T}() diff --git a/src/iterators.jl b/src/iterators.jl index 97627b30..1d5a4ff6 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -28,7 +28,7 @@ iterator(T::Type, ElemT::Type, f::Function, p::Rep) = SingleMapRepIterator{T, El # A representation can overwrite this if it can do something more efficient or if it simply does not support indexing function Base.eachindex(it::AbstractSingleRepIterator{<:Any, ElemT, RepT}) where {T, ElemT, RepT<:Rep{T}} - return Indices{T, similar_type(ElemT, FullDim(RepT), T)}(it.p) + return Indices{T, similar_type(ElemT, typed_fulldim(RepT), T)}(it.p) end element_and_index(::AbstractSingleRepIterator, ::Nothing) = nothing function element_and_index(it::AbstractSingleRepIterator, idx::AbstractIndex) @@ -122,9 +122,6 @@ for (isVrep, elt, loop_singular) in [(true, :AbstractVector, :point), end singularstr = string(singular) elemtype = Symbol(singularstr * "type") - donep = Symbol("done" * singularstr) - startp = Symbol("start" * singularstr) - nextp = Symbol("next" * singularstr) pluralstr = singularstr * "s" global plural = Symbol(pluralstr) global lenp = Symbol("n" * pluralstr) @@ -134,7 +131,7 @@ for (isVrep, elt, loop_singular) in [(true, :AbstractVector, :point), incidx = Symbol("incident" * singularstr * "indices") @eval begin - export $plural, $lenp, $isnotemptyp, $startp, $donep, $nextp, $elemtype + export $plural, $lenp, $isnotemptyp, $elemtype export $inc, $incidx """ @@ -179,9 +176,10 @@ for (isVrep, elt, loop_singular) in [(true, :AbstractVector, :point), $elemtype(p::$HorVRep{T}) where {T} = $elt{T, $vectortype_fun(typeof(p))} end - function $plural(p::$HorVRep{T}...) where {T} - ElemT = promote_type($elemtype.(p)...) - iterator(T, ElemT, p...) + function $plural(p::$HorVRep...) + ElemT = promote_type(map($elemtype, p)...) + CoefT = promote_type(map(coefficient_type, p)...) + iterator(CoefT, ElemT, p...) end function $mapit(f::F, d::FullDim, ::Type{T}, p::Vararg{$HorVRep,N}) where {F<:Function,T,N} @@ -350,17 +348,17 @@ function fillvits(d::FullDim, lines::ElemIt{Line{T, AT}}, return points, lines, rays end -FullDim_hreps(p...) = FullDim(p[1]), hreps(p...)... -FullDim_vreps(p...) = FullDim(p[1]), vreps(p...)... +FullDim_hreps(p...) = typed_fulldim(p[1]), hreps(p...)... +FullDim_vreps(p...) = typed_fulldim(p[1]), vreps(p...)... -hreps(p::HRep{T}...) where {T} = hyperplanes(p...), halfspaces(p...) -hreps(p::HAffineSpace{T}...) where {T} = tuple(hyperplanes(p...)) +hreps(p::HRep...) = hyperplanes(p...), halfspaces(p...) +hreps(p::HAffineSpace...) = tuple(hyperplanes(p...)) hmap(f, d::FullDim, ::Type{T}, p::HRep...) where T = maphyperplanes(f, d, T, p...), maphalfspaces(f, d, T, p...) hmap(f, d::FullDim, ::Type{T}, p::HAffineSpace...) where T = tuple(maphyperplanes(f, d, T, p...)) -hconvert(RepT::Type{<:HRep{T}}, p::HRep{T}) where {T} = constructpolyhedron(RepT, FullDim(p), (p,), hreps(p)...) -hconvert(RepT::Type{<:HRep{T}}, p::HRep) where {T} = constructpolyhedron(RepT, FullDim(p), (p,), change_coefficient_type.(hreps(p), T)...) +hconvert(RepT::Type{<:HRep{T}}, p::HRep{T}) where {T} = constructpolyhedron(RepT, typed_fulldim(p), (p,), hreps(p)...) +hconvert(RepT::Type{<:HRep{T}}, p::HRep) where {T} = constructpolyhedron(RepT, typed_fulldim(p), (p,), change_coefficient_type.(hreps(p), T)...) vreps(p...) = preps(p...)..., rreps(p...)... preps(p::VRep...) = tuple(points(p...)) @@ -368,6 +366,7 @@ preps(p::VCone...) = tuple() rreps(p::VRep...) = lines(p...), rays(p...) rreps(p::VLinearSpace...) = tuple(lines(p...)) rreps(p::VPolytope...) = tuple() +rreps() = tuple() # resolves ambiguity vmap(f, d::FullDim, ::Type{T}, p::VRep...) where T = pmap(f, d, T, p...)..., rmap(f, d, T, p...)... pmap(f, d::FullDim, ::Type{T}, p::VRep...) where T = tuple(mappoints(f, d, T, p...)) @@ -375,8 +374,9 @@ pmap(f, d::FullDim, ::Type, p::VCone...) = tuple() rmap(f, d::FullDim, ::Type{T}, p::VRep...) where T = maplines(f, d, T, p...), maprays(f, d, T, p...) rmap(f, d::FullDim, ::Type{T}, p::VLinearSpace...) where T = tuple(maplines(f, d, T, p...)) rmap(f, d::FullDim, ::Type, p::VPolytope...) = tuple() +rmap(f, d::FullDim, ::Type) = tuple() # resolves ambiguity -vconvert(RepT::Type{<:VRep{T}}, p::VRep{T}) where {T} = constructpolyhedron(RepT, FullDim(p), (p,), vreps(p)...) +vconvert(RepT::Type{<:VRep{T}}, p::VRep{T}) where {T} = constructpolyhedron(RepT, typed_fulldim(p), (p,), vreps(p)...) function vconvert(RepT::Type{<:VRep{T}}, p::VRep) where {T} - constructpolyhedron(RepT, FullDim(p), (p,), change_coefficient_type.(vreps(p), T)...) + constructpolyhedron(RepT, typed_fulldim(p), (p,), change_coefficient_type.(vreps(p), T)...) end diff --git a/src/jump.jl b/src/jump.jl deleted file mode 100644 index c93538a8..00000000 --- a/src/jump.jl +++ /dev/null @@ -1,11 +0,0 @@ -using JuMP - -""" - hrep(model::JuMP.Model) - -Builds an H-representation from the feasibility set of the JuMP model `model`. -Note that if non-linear constraint are present in the model, they are ignored. -""" -hrep(model::JuMP.Model) = LPHRep(model) -LPHRep(model::JuMP.Model) = LPHRep(backend(model)) -polyhedron(model::JuMP.Model, args...) = polyhedron(hrep(model), args...) diff --git a/src/liftedrep.jl b/src/liftedrep.jl index b1808e5d..45a315c9 100644 --- a/src/liftedrep.jl +++ b/src/liftedrep.jl @@ -15,7 +15,7 @@ mutable struct LiftedHRepresentation{T, MT<:AbstractMatrix{T}} <: MixedHRep{T} new{T, MT}(A, linset) end end -FullDim(rep::LiftedHRepresentation) = size(rep.A, 2) - 1 +typed_fulldim(rep::LiftedHRepresentation) = size(rep.A, 2) - 1 similar_type(::Type{LiftedHRepresentation{S, MT}}, ::FullDim, ::Type{T}) where {S, T, MT} = LiftedHRepresentation{T, similar_type(MT, T)} hvectortype(p::Type{LiftedHRepresentation{T, MT}}) where {T, MT} = vectortype(MT) @@ -63,7 +63,7 @@ mutable struct LiftedVRepresentation{T, MT<:AbstractMatrix{T}} <: MixedVRep{T} new{T, MT}(R, linset) end end -FullDim(rep::LiftedVRepresentation) = size(rep.R, 2) - 1 +typed_fulldim(rep::LiftedVRepresentation) = size(rep.R, 2) - 1 similar_type(::Type{LiftedVRepresentation{S, MT}}, ::FullDim, ::Type{T}) where {S, T, MT} = LiftedVRepresentation{T, similar_type(MT, T)} vvectortype(::Type{LiftedVRepresentation{T, MT}}) where {T, MT} = vectortype(MT) diff --git a/src/linearity.jl b/src/linearity.jl index 607a27fc..400382ce 100644 --- a/src/linearity.jl +++ b/src/linearity.jl @@ -151,7 +151,6 @@ function detect_new_linearities(rep::Representation, solver; verbose=0, kws...) nonlins = _nonlinearity(rep) isempty(nonlins) && return Int[] model, T = layered_optimizer(solver) - verbose >= 2 && (_model = JuMP.direct_model(model)) is_lin = falses(length(nonlins)) active = trues(length(nonlins)) # We pass `true` as we break homogeneity of the problem with `sum(λ) == 1`. @@ -173,7 +172,7 @@ function detect_new_linearities(rep::Representation, solver; verbose=0, kws...) end for i in 1:fulldim(rep) # safer than `while ...` (count(is_lin) == length(is_lin) || iszero(count(active))) && break - verbose >= 2 && println(_model) + verbose >= 2 && println(model) # FIXME stopping when we have enough hyperplanes to prove that it's empty # should also resolve the issue with presolve. is_feasible(model, "detecting new linearity (you may need to activate presolve for some solvers, e.g. by replacing `GLPK.Optimizer` with `optimizer_with_attributes(GLPK.Optimizer, \"presolve\" => GLPK.GLP_ON)`).") || break @@ -276,7 +275,7 @@ The remaining keyword arguments `kws` are passed to [`detect_new_linearities`](@ """ function detecthlinearity(hr::HRepresentation, solver; kws...) aff, hs = _detect_linearity(hr, solver; kws...) - typeof(hr)(FullDim(hr), aff.hyperplanes, hs) + typeof(hr)(typed_fulldim(hr), aff.hyperplanes, hs) end """ @@ -288,5 +287,5 @@ The remaining keyword arguments `kws` are passed to [`detect_new_linearities`](@ """ function detectvlinearity(vr::VRepresentation, solver; kws...) aff, rays = _detect_linearity(vr, solver; kws...) - typeof(vr)(FullDim(vr), preps(vr)..., aff.lines, rays) + typeof(vr)(typed_fulldim(vr), preps(vr)..., aff.lines, rays) end diff --git a/src/lphrep.jl b/src/lphrep.jl index e6ce99ec..384c64e9 100644 --- a/src/lphrep.jl +++ b/src/lphrep.jl @@ -51,7 +51,7 @@ function LPHRep(model::MOI.ModelLike, T::Type = Float64) return LPHRep(_model) end # returns `Int64` so need to convert for 32-bit system -FullDim(rep::LPHRep) = convert(Int, MOI.get(rep.model, MOI.NumberOfVariables())) +typed_fulldim(rep::LPHRep) = convert(Int, MOI.get(rep.model, MOI.NumberOfVariables())) hvectortype(::Type{LPHRep{T}}) where {T} = SparseVector{T, Int} similar_type(::Type{LPHRep{S}}, ::FullDim, ::Type{T}) where {S, T} = LPHRep{T} @@ -175,7 +175,7 @@ function Base.get(rep::LPHRep{T}, idx::HIndex{T}) where {T} # issues with, e.g. preimages with `spzeros(d, n)`, etc... indices = Int[t.variable.value for t in func.terms] values = [t.coefficient for t in func.terms] - a = sparsevec(indices, values, FullDim(rep)) + a = sparsevec(indices, values, typed_fulldim(rep)) set = MOI.get(rep.model, MOI.ConstraintSet(), ci) β = MOI.constant(set) - func.constant if idx isa HyperPlaneIndex diff --git a/src/matrep.jl b/src/matrep.jl index 8f46600d..7df94b22 100644 --- a/src/matrep.jl +++ b/src/matrep.jl @@ -26,7 +26,7 @@ mutable struct MixedMatHRep{T, MT<:AbstractMatrix{T}} <: MixedHRep{T} new{T, typeof(A)}(A, b, linset) end end -FullDim(rep::MixedMatHRep) = size(rep.A, 2) +typed_fulldim(rep::MixedMatHRep) = size(rep.A, 2) similar_type(::Type{<:MixedMatHRep{S, MT}}, ::FullDim, ::Type{T}) where {S, T, MT} = MixedMatHRep{T, similar_type(MT, T)} hvectortype(p::Type{MixedMatHRep{T, MT}}) where {T, MT} = vectortype(MT) @@ -97,7 +97,7 @@ mutable struct MixedMatVRep{T, MT<:AbstractMatrix{T}} <: MixedVRep{T} new{T, MT}(V, R, Rlinset) end end -FullDim(rep::MixedMatVRep) = size(rep.V, 2) +typed_fulldim(rep::MixedMatVRep) = size(rep.V, 2) similar_type(::Type{<:MixedMatVRep{S, MT}}, ::FullDim, ::Type{T}) where {S, T, MT} = MixedMatVRep{T, similar_type(MT, T)} vvectortype(::Type{MixedMatVRep{T, MT}}) where {T, MT} = vectortype(MT) diff --git a/src/opt.jl b/src/opt.jl index 2e78a427..623029a3 100644 --- a/src/opt.jl +++ b/src/opt.jl @@ -9,36 +9,6 @@ MOI.dimension(set::PolyhedraOptSet) = fulldim(set.rep) # will be modified so we don't copy it as it would be expensive. Base.copy(set::PolyhedraOptSet) = set -struct VariableInSet{V <: JuMP.ScalarVariable, S <: Union{Rep, Projection}} <: JuMP.AbstractVariable - variables::Vector{V} - set::S -end -function JuMP.build_variable(error_fun::Function, variables::Vector{<:JuMP.ScalarVariable}, set::Union{Rep, Projection}) - if length(variables) != fulldim(set) - _error("Number of variables ($(length(variables))) does not match the full dimension of the polyhedron ($(fulldim(set))).") - end - return VariableInSet(variables, set) -end -function JuMP.add_variable(model::JuMP.AbstractModel, v::VariableInSet, names) - dim_names = dimension_names(v.set) - if dim_names !== nothing - names = copy(names) - for i in eachindex(names) - if isempty(names[i]) && !isempty(dim_names[i]) - names[i] = dim_names[i] - end - end - end - JuMP.add_bridge(model, PolyhedraToLPBridge) - JuMP.add_bridge(model, ProjectionBridge) - return JuMP.add_variable(model, JuMP.VariablesConstrainedOnCreation(v.variables, _moi_set(v.set)), names) -end -_moi_set(set::Rep) = PolyhedraOptSet(set) -function JuMP.build_constraint(error_fun::Function, func, set::Rep) - return JuMP.BridgeableConstraint( - JuMP.build_constraint(error_fun, func, _moi_set(set)), - PolyhedraToLPBridge) -end abstract type AbstractPolyhedraOptimizer{T} <: MOI.AbstractOptimizer end @@ -153,7 +123,6 @@ function layered_optimizer(solver) return optimizer, T end -_optimize!(model::JuMP.Model) = JuMP.optimize!(model) _optimize!(model::MOI.ModelLike) = MOI.optimize!(model) function _unknown_status(model, status, message) @@ -195,3 +164,4 @@ function Base.isempty(p::Rep, solver=Polyhedra.linear_objective_solver(p)) x, cx = MOI.add_constrained_variables(model, PolyhedraOptSet(p)) return !is_feasible(model, "trying to determine whether the polyhedron is empty.") end +_moi_set(set::Rep) = PolyhedraOptSet(set) diff --git a/src/planar.jl b/src/planar.jl index 65c450db..39b5c216 100644 --- a/src/planar.jl +++ b/src/planar.jl @@ -190,6 +190,6 @@ counterclockwise(x, y) = x[1] * y[2] - x[2] * y[1] rotate(x) = convert(typeof(x), StaticArrays.SVector(-x[2], x[1])) function planar_hull(vr::VRepresentation) - d = FullDim(vr) - vrep(_planar_hull(FullDim(vr), collect(points(vr)), lines(vr), rays(vr), counterclockwise, rotate)...; d = d) + d = typed_fulldim(vr) + vrep(_planar_hull(typed_fulldim(vr), collect(points(vr)), lines(vr), rays(vr), counterclockwise, rotate)...; d = d) end diff --git a/src/polyhedron.jl b/src/polyhedron.jl index ec2d180a..5aa89ea3 100644 --- a/src/polyhedron.jl +++ b/src/polyhedron.jl @@ -8,7 +8,7 @@ export volume, volume_simplex, unscaled_volume_simplex, surface, center_of_mass Creates a polyhedron from the representation `rep` using the default library included in the Polyhedra package. """ -polyhedron(rep::Representation{T}) where {T} = polyhedron(rep, default_library(FullDim(rep), T)) +polyhedron(rep::Representation{T}) where {T} = polyhedron(rep, default_library(typed_fulldim(rep), T)) """ hrepiscomputed(p::Polyhedron) diff --git a/src/projection_opt.jl b/src/projection_opt.jl index 240a12b5..d1f819a8 100644 --- a/src/projection_opt.jl +++ b/src/projection_opt.jl @@ -58,8 +58,3 @@ function MOI.delete(model::MOI.ModelLike, b::ProjectionBridge) end end _moi_set(set::Projection) = ProjectionOptSet(set) -function JuMP.build_constraint(error_fun::Function, func, set::Projection) - return JuMP.BridgeableConstraint(JuMP.BridgeableConstraint( - JuMP.build_constraint(error_fun, func, _moi_set(set)), - ProjectionBridge), PolyhedraToLPBridge) -end diff --git a/src/redundancy.jl b/src/redundancy.jl index 48eaa3cd..8919e2b1 100644 --- a/src/redundancy.jl +++ b/src/redundancy.jl @@ -262,7 +262,7 @@ function removehredundancy(hr::HRep, solver) # the hyperplanes, it just remove the redundant ones. nr_hyperplanes = nonredundant_hyperplanes(hr, model, T, hull, true) nr_halfspaces = nonredundant_halfspaces(hr, model, T, hull, false) - return hrep(nr_hyperplanes..., nr_halfspaces...; d=FullDim(hr)) + return hrep(nr_hyperplanes..., nr_halfspaces...; d=typed_fulldim(hr)) end nonredundant_lines(vr::VPolytope, model, T, hull, clean) = tuple() @@ -298,7 +298,7 @@ function removevredundancy(vr::VRepresentation, solver) nr_lines = nonredundant_lines(vr, model, T, hull, true) nr_rays = nonredundant_rays(vr, model, T, hull, true) nr_points = nonredundant_points(vr, model, T, hull, false) - return vrep(nr_points..., nr_lines..., nr_rays...; d=FullDim(vr)) + return vrep(nr_points..., nr_lines..., nr_rays...; d=typed_fulldim(vr)) end _dim_for_hull(h::HRepresentation) = fulldim(h) + 1 @@ -356,7 +356,7 @@ end function _removevred_withhred(vrep::VRep, hrep::HRep; kws...) nl = nlines(vrep) typeof(vrep)( - FullDim(vrep), + typed_fulldim(vrep), removevredundancy.(vreps(vrep), hrep; nl=nl, kws...)... )::typeof(vrep) end @@ -367,6 +367,7 @@ end function removevredundancy(vrep::VRepresentation, hrep::HRep; kws...) _removevred_withhred(vrep, hrep; kws...) end +removevredundancy(vr::VEmptySpace, ::HRep) = vr # resolves ambiguity function removehredundancy(hrepit::HIt, vrep::VRep; strongly=false, d=dim(vrep)) _filter(h -> !isredundant(vrep, h, strongly=strongly, d=d), hrepit) @@ -377,7 +378,7 @@ end function _removehred_withvred(hrep::HRep, vrep::VRep; strongly=false) R = BitSet() d = dim(hrep, true) # TODO dim(hrep) - typeof(hrep)(FullDim(hrep), + typeof(hrep)(typed_fulldim(hrep), removehredundancy.(hreps(hrep), vrep, strongly=strongly, d=d)...) end @@ -492,12 +493,12 @@ function premovedups(vrep::VRepresentation, aff::VLinearSpace) end function removeduplicates(vrep::VPolytope) - typeof(vrep)(FullDim(vrep), premovedups(vrep, emptyspace(vrep))...) + typeof(vrep)(typed_fulldim(vrep), premovedups(vrep, emptyspace(vrep))...) end removeduplicates(vrep::VLinearSpace, solver = nothing) = detectvlinearity(vrep, solver) function removeduplicates(vrep::VRepresentation, solver = nothing) aff, rs = _detect_linearity(vrep, solver) - typeof(vrep)(FullDim(vrep), premovedups(vrep, aff)..., aff.lines, rs) + typeof(vrep)(typed_fulldim(vrep), premovedups(vrep, aff)..., aff.lines, rs) end # H-duplicates diff --git a/src/repelemop.jl b/src/repelemop.jl index 8049d900..80cb88aa 100644 --- a/src/repelemop.jl +++ b/src/repelemop.jl @@ -17,14 +17,14 @@ export translate function htranslate(p::HRep{T}, v::Union{AbstractVector{S}}) where {S, T} f = (i, h) -> translate(h, v) - d = FullDim(p) + d = typed_fulldim(p) Tout = Base.promote_op(+, T, S) similar(p, d, Tout, hmap(f, d, Tout, p)...) end function vtranslate(p::VRep{T}, v::Union{AbstractVector{S}}) where {S, T} f = (i, u) -> translate(u, v) - d = FullDim(p) + d = typed_fulldim(p) Tout = Base.promote_op(+, T, S) similar(p, d, Tout, vmap(f, d, Tout, p)...) end diff --git a/src/repop.jl b/src/repop.jl index 3c2b3365..09c44ac9 100644 --- a/src/repop.jl +++ b/src/repop.jl @@ -18,7 +18,7 @@ The coefficient type however, will be promoted as required taking both the coeff function Base.intersect(p1::HRep, p2::HRep, ps::HRep...) p = (p1, p2, ps...) T = promote_coefficient_type(p) - similar(p, hmap((i, x) -> convert(similar_type(typeof(x), T), x), FullDim(p[1]), T, p...)...) + similar(p, hmap((i, x) -> convert(similar_type(typeof(x), T), x), typed_fulldim(p[1]), T, p...)...) end Base.intersect(p::HRep, el::HRepElement) = p ∩ intersect(el) Base.intersect(el::HRepElement, p::HRep) = p ∩ el @@ -67,7 +67,7 @@ The coefficient type however, will be promoted as required taking both the coeff function convexhull(p1::VRep, p2::VRep, ps::VRep...) p = (p1, p2, ps...) T = promote_coefficient_type(p) - similar(p, vmap((i, x) -> convert(similar_type(typeof(x), T), x), FullDim(p[1]), T, p...)...) + similar(p, vmap((i, x) -> convert(similar_type(typeof(x), T), x), typed_fulldim(p[1]), T, p...)...) end convexhull(p::VRep, el::VRepElement) = convexhull(p, convexhull(el)) convexhull(el::VRepElement, p::VRep) = convexhull(p, el) @@ -119,6 +119,7 @@ function sumpoints(::FullDim, ::Type{T}, p1, p2) where {T} end sumpoints(::FullDim, ::Type{T}, p1::Rep, p2::VCone) where {T} = change_coefficient_type.(preps(p1), T) sumpoints(::FullDim, ::Type{T}, p1::VCone, p2::Rep) where {T} = change_coefficient_type.(preps(p2), T) +sumpoints(::FullDim, ::Type{T}, p1::VCone, p2::VCone) where {T} = tuple() # resolves ambiguity """ +(p1::VRep, p2::VRep) @@ -133,7 +134,7 @@ Same as `p + vrep([el])`. """ function Base.:+(p1::VRep{T1}, p2::VRep{T2}) where {T1, T2} T = typeof(zero(T1) + zero(T2)) - similar((p1, p2), FullDim(p1), T, sumpoints(FullDim(p1), T, p1, p2)..., change_coefficient_type.(rreps(p1, p2), T)...) + similar((p1, p2), typed_fulldim(p1), T, sumpoints(typed_fulldim(p1), T, p1, p2)..., change_coefficient_type.(rreps(p1, p2), T)...) end Base.:+(p::Rep, el::Union{Line, Ray}) = p + vrep([el]) Base.:+(el::Union{Line, Ray}, p::Rep) = p + el @@ -144,9 +145,9 @@ function usehrep(p1::Polyhedron, p2::Polyhedron) end function hcartesianproduct(p1::HRep, p2::HRep) - d = sum_fulldim(FullDim(p1), FullDim(p2)) + d = sum_fulldim(typed_fulldim(p1), typed_fulldim(p2)) T = promote_coefficient_type((p1, p2)) - f = (i, x) -> zeropad(x, i == 1 ? FullDim(p2) : neg_fulldim(FullDim(p1))) + f = (i, x) -> zeropad(x, i == 1 ? typed_fulldim(p2) : neg_fulldim(typed_fulldim(p1))) function dimension_map(i) if i <= fulldim(p1) return (1, i) @@ -158,11 +159,11 @@ function hcartesianproduct(p1::HRep, p2::HRep) dimension_map = dimension_map) end function vcartesianproduct(p1::VRep, p2::VRep) - d = sum_fulldim(FullDim(p1), FullDim(p2)) + d = sum_fulldim(typed_fulldim(p1), typed_fulldim(p2)) T = promote_coefficient_type((p1, p2)) # Always type of first arg - f1 = (i, x) -> zeropad(x, FullDim(p2)) - f2 = (i, x) -> zeropad(x, neg_fulldim(FullDim(p1))) + f1 = (i, x) -> zeropad(x, typed_fulldim(p2)) + f2 = (i, x) -> zeropad(x, neg_fulldim(typed_fulldim(p1))) q1 = similar(p1, d, T, vmap(f1, d, T, p1)...) q2 = similar(p2, d, T, vmap(f2, d, T, p2)...) q1 + q2 @@ -212,7 +213,7 @@ function Base.:(/)(p::HRep, P::AbstractMatrix) return linear_preimage_transpose(P, p, size(P, 1)) end function Base.:(/)(p::HRep, P::UniformScaling) - return linear_preimage_transpose(P, p, FullDim(p)) + return linear_preimage_transpose(P, p, typed_fulldim(p)) end function linear_image(P, p::VRep{Tin}, d) where Tin @@ -234,7 +235,7 @@ function Base.:(*)(P::AbstractMatrix, p::VRep) return linear_image(P, p, size(P, 1)) end function Base.:(*)(P::UniformScaling, p::VRep) - return linear_image(P, p, FullDim(p)) + return linear_image(P, p, typed_fulldim(p)) end """ @@ -304,7 +305,7 @@ function polar(hr::HRepresentation{T}) where T if isempty(points) push!(points, origin(V, fulldim(hr))) end - return vrep(points, lines, rays, d = FullDim(hr)) + return vrep(points, lines, rays, d = typed_fulldim(hr)) end function polar(p::Polyhedron) if hrepiscomputed(p) # TODO we should compute the polar of both rep if both are computed diff --git a/src/representation.jl b/src/representation.jl index e92dafd9..54bd5b64 100644 --- a/src/representation.jl +++ b/src/representation.jl @@ -36,9 +36,9 @@ Returns the type of the coefficients used in the representation of `rep`. """ coefficient_type(rep::Union{Rep{T}, Type{<:Rep{T}}}) where {T} = T -FullDim(rep::Type{<:VRep}) = FullDim(vvectortype(rep)) -FullDim(rep::Type{<:HRep}) = FullDim(hvectortype(rep)) -FullDim(rep::Type{<:Polyhedron}) = FullDim(hvectortype(rep)) +typed_fulldim(rep::Type{<:VRep}) = typed_fulldim(vvectortype(rep)) +typed_fulldim(rep::Type{<:HRep}) = typed_fulldim(hvectortype(rep)) +typed_fulldim(rep::Type{<:Polyhedron}) = typed_fulldim(hvectortype(rep)) # Check that it is either empty or it has a point vconsistencyerror() = error("Non-empty V-representation must contain at least one point. If it is a polyhedral cone, the origin should be added.") @@ -82,8 +82,8 @@ Base.convert(::Type{VRep}, p::VRepresentation) = p change_coefficient_type(p::Rep{T}, ::Type{T}) where {T} = p change_coefficient_type(p::Rep, T::Type) = convert(similar_type(typeof(p), T), p) -VRepresentation{T}(v::VRepresentation) where {T} = convert(similar_type(typeof(v), FullDim(v), T), v) -HRepresentation{T}(h::HRepresentation) where {T} = convert(similar_type(typeof(h), FullDim(h), T), h) +VRepresentation{T}(v::VRepresentation) where {T} = convert(similar_type(typeof(v), typed_fulldim(v), T), v) +HRepresentation{T}(h::HRepresentation) where {T} = convert(similar_type(typeof(h), typed_fulldim(h), T), h) VRep{T}(v::VRepresentation) where {T} = VRepresentation{T}(v) VRep{T}(p::Polyhedron) where {T} = Polyhedron{T}(p) diff --git a/src/vecrep.jl b/src/vecrep.jl index 7d0d60fc..4954d8c2 100644 --- a/src/vecrep.jl +++ b/src/vecrep.jl @@ -74,12 +74,12 @@ function Intersection(d::FullDim, hyperplanes::ElemIt{HyperPlane{T, AT}}, return Intersection{T, AT, typeof(d)}(d, hyperplanes, halfspaces) end -FullDim(h::Intersection) = FullDim(h.hyperplanes) +typed_fulldim(h::Intersection) = typed_fulldim(h.hyperplanes) hvectortype(::Type{<:Intersection{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:Intersection}, d::FullDim, ::Type{T}) where {T} = Intersection{T, similar_type(hvectortype(PT), d, T), typeof(d)} Intersection(h::HRepresentation{T}) where {T} = Intersection{T}(h) -Intersection{T}(h::HRepresentation) where {T} = convert(Intersection{T, similar_type(hvectortype(typeof(h)), T), typeof(FullDim(h))}, h) +Intersection{T}(h::HRepresentation) where {T} = convert(Intersection{T, similar_type(hvectortype(typeof(h)), T), typeof(typed_fulldim(h))}, h) function Base.intersect!(h::Intersection, hp::HyperPlane) intersect!(h.hyperplanes, hp) @@ -170,7 +170,7 @@ function PointsHull(d::FullDim, points::PointIt) return PointsHull{coefficient_type(eltype(points)), eltype(points), typeof(d)}(d, points) end -FullDim(v::PointsHull) = v.d +typed_fulldim(v::PointsHull) = v.d vvectortype(::Type{<:PointsHull{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:PointsHull}, d::FullDim, ::Type{T}) where {T} = PointsHull{T, similar_type(vvectortype(PT), d, T), typeof(d)} @@ -223,7 +223,7 @@ end function RaysHull(d::FullDim, ls::ElemIt{Line{T, AT}}, rs::ElemIt{Ray{T, AT}}) where {T, AT} RaysHull{T, AT, typeof(d)}(d, ls, rs) end -FullDim(v::RaysHull) = FullDim(v.lines) +typed_fulldim(v::RaysHull) = typed_fulldim(v.lines) vvectortype(::Type{<:RaysHull{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:RaysHull}, d::FullDim, ::Type{T}) where {T} = RaysHull{T, similar_type(vvectortype(PT), d, T), typeof(d)} @@ -265,12 +265,12 @@ function Hull(d::FullDim, points::ElemIt{AT}, lines::ElemIt{Line{T, AT}}, rays::ElemIt{Ray{T, AT}}) where {T, AT} Hull{T, AT, typeof(d)}(d, points, lines, rays) end -FullDim(v::Hull) = FullDim(v.points) +typed_fulldim(v::Hull) = typed_fulldim(v.points) vvectortype(::Type{<:Hull{T, AT}}) where {T, AT} = AT similar_type(PT::Type{<:Hull}, d::FullDim, ::Type{T}) where {T} = Hull{T, similar_type(vvectortype(PT), d, T), typeof(d)} Hull(v::VRepresentation{T}) where {T} = Hull{T}(v) -Hull{T}(v::VRepresentation) where {T} = convert(Hull{T, similar_type(vvectortype(typeof(v)), T), typeof(FullDim(v))}, v) +Hull{T}(v::VRepresentation) where {T} = convert(Hull{T, similar_type(vvectortype(typeof(v)), T), typeof(typed_fulldim(v))}, v) convexhull!(v::Hull, p::AbstractVector) = convexhull!(v.points, p) convexhull!(v::Hull, r::VStruct) = convexhull!(v.rays, r) diff --git a/test/Project.toml b/test/Project.toml index 3b9d50da..4daffe2e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,4 +1,5 @@ [deps] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" @@ -10,3 +11,6 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[compat] +Aqua = "0.8" \ No newline at end of file diff --git a/test/aqua.jl b/test/aqua.jl new file mode 100644 index 00000000..8127dbd3 --- /dev/null +++ b/test/aqua.jl @@ -0,0 +1,7 @@ +using Polyhedra +using Aqua + +@testset "aqua" begin + Aqua.test_ambiguities(Polyhedra, broken=true) + Aqua.test_all(Polyhedra, ambiguities=false) +end diff --git a/test/cartesian_interval.jl b/test/cartesian_interval.jl index a95ceb86..e5cc750a 100644 --- a/test/cartesian_interval.jl +++ b/test/cartesian_interval.jl @@ -5,11 +5,11 @@ function cartesian_interval_test(lib::Polyhedra.Library) F = [0, 0, 1] S = polyhedron(hrep(G, F), lib) U = polyhedron(convexhull([-2], [2])) - @test Polyhedra.FullDim(U) == StaticArrays.Size{(1,)}() + @test Polyhedra.typed_fulldim(U) == StaticArrays.Size{(1,)}() # Mixes `S` whose `FullDim` is `2` and `U` # whose `FullDim` has type `StaticArrays.Size`. poly = S * U - @test Polyhedra.FullDim(poly) == 3 + @test Polyhedra.typed_fulldim(poly) == 3 h = HalfSpace([-1, 0, 0], 0) ∩ HalfSpace([ 0, -1, 0], 0) ∩ HalfSpace([ 1, 1, 0], 1) ∩ diff --git a/test/decompose.jl b/test/decompose.jl index a5899d1c..ce8de6f6 100644 --- a/test/decompose.jl +++ b/test/decompose.jl @@ -23,7 +23,8 @@ end nfaces(d::Dict{<:Any, Face}) = length(d) nfaces(d::Dict{<:Any, <:Vector}) = sum(map(length, values(d))) -function test_decompose(p::Polyhedra.Mesh{N}, d::Dict) where N +function test_decompose(p, d::Dict) + N = ndims(p) P = GeometryBasics.Point{N, Float64} NR = GeometryBasics.Point{3, Float64} points = GeometryBasics.decompose(P, p) diff --git a/test/inconsistentvrep.jl b/test/inconsistentvrep.jl index d58b775c..ec2b9c48 100644 --- a/test/inconsistentvrep.jl +++ b/test/inconsistentvrep.jl @@ -7,7 +7,7 @@ struct InconsistentVRep{T, AT, D<:Polyhedra.FullDim} <: VRepresentation{T} Polyhedra.RaysHull(d, lines, rays)) end end -Polyhedra.FullDim(rep::InconsistentVRep) = Polyhedra.FullDim(rep.points) +Polyhedra.typed_fulldim(rep::InconsistentVRep) = Polyhedra.typed_fulldim(rep.points) Polyhedra.dualtype(::Type{InconsistentVRep{T, AT, D}}, ::Type{AT}) where {T, AT, D} = Polyhedra.Intersection{T, AT, D} Polyhedra.hvectortype(::Type{<:InconsistentVRep{T, AT}}) where {T, AT} = AT Polyhedra.vvectortype(::Type{<:InconsistentVRep{T, AT}}) where {T, AT} = AT diff --git a/test/matrep.jl b/test/matrep.jl index 98137cff..05d0abd1 100644 --- a/test/matrep.jl +++ b/test/matrep.jl @@ -21,7 +21,7 @@ function test_MixMatRep() @test_throws ErrorException hrep(A, b, BitSet([4])) ine = hrep(A, b, linset) @test fulldim(ine) == 2 - @test (@inferred Polyhedra.FullDim(ine)) == 2 + @test (@inferred Polyhedra.typed_fulldim(ine)) == 2 @test Polyhedra.coefficient_type(ine) == Int @test translate(ine, [1, 0]).b == [2, -1, 0] @@ -31,7 +31,7 @@ function test_MixMatRep() @test_throws ErrorException vrep(V, [1 1], BitSet([2])) ext = vrep(V) @test fulldim(ext) == 2 - @test (@inferred Polyhedra.FullDim(ine)) == 2 + @test (@inferred Polyhedra.typed_fulldim(ine)) == 2 @test Polyhedra.coefficient_type(ext) == Int @test translate(ext, [1, 0]).V == [1 1; 2 0] end diff --git a/test/redundancy.jl b/test/redundancy.jl index c4b32dac..e99f33b5 100644 --- a/test/redundancy.jl +++ b/test/redundancy.jl @@ -15,7 +15,7 @@ include("dummy_optimizer.jl") ((@SVector [1, 0]), (@SVector [0, 1]), (@SVector [1, 1]), (@SVector [0, 0]))] T = eltype(x) AT = typeof(x) - d = Polyhedra.FullDim(x) + d = Polyhedra.typed_fulldim(x) @testset "HAffineSpace" begin for hr in [hrep(typeof(HyperPlane(x, one(T)))[]; d=d), intersect(HyperPlane(w, zero(T)))] @@ -77,7 +77,7 @@ include("dummy_optimizer.jl") ((@SVector [1, 0]), (@SVector [0, 1]), (@SVector [1, 1]), (@SVector [0, 0]))] T = eltype(x) AT = typeof(x) - d = Polyhedra.FullDim(x) + d = Polyhedra.typed_fulldim(x) @testset "VLinearSpace" begin for vr in [vrep(typeof(Line(x))[]; d=d), convexhull(Line(w))] diff --git a/test/representation.jl b/test/representation.jl index f974092d..adac5171 100644 --- a/test/representation.jl +++ b/test/representation.jl @@ -30,8 +30,8 @@ function change_fulldim_test(Rep) @test Polyhedra.coefficient_type(RepT) == T changedrep = Polyhedra.similar_type(RepT, 10) @test fulldim(changedrep) == -1 - @test Polyhedra.FullDim(changedrep) == -1 - #@test (@inferred Polyhedra.FullDim(changedrep)) == 10 + @test Polyhedra.typed_fulldim(changedrep) == -1 + #@test (@inferred Polyhedra.typed_fulldim(changedrep)) == 10 @test Polyhedra.coefficient_type(changedrep) == T end end diff --git a/test/runtests.jl b/test/runtests.jl index 0af2b70d..64a72280 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,6 +6,7 @@ using Test using StaticArrays +include("aqua.jl") include("utils.jl") include("solvers.jl")