diff --git a/Project.toml b/Project.toml index eedd899f7591..cec494303ee9 100644 --- a/Project.toml +++ b/Project.toml @@ -36,7 +36,7 @@ LazyArtifacts = "1.6" Markdown = "1.6" Nemo = "0.45.5" Pkg = "1.6" -Polymake = "0.11.14" +Polymake = "0.11.19" Random = "1.6" RandomExtensions = "0.4.3" Serialization = "1.6" diff --git a/src/Combinatorics/Graphs/functions.jl b/src/Combinatorics/Graphs/functions.jl index 46ebfed592ff..17d9a999fc2c 100644 --- a/src/Combinatorics/Graphs/functions.jl +++ b/src/Combinatorics/Graphs/functions.jl @@ -342,7 +342,7 @@ function reverse(e::Edge) end -struct EdgeIterator +mutable struct EdgeIterator pm_itr::Polymake.GraphEdgeIterator{T} where {T <: Union{Directed, Undirected}} l::Int64 end @@ -350,7 +350,7 @@ Base.length(eitr::EdgeIterator) = eitr.l Base.eltype(::Type{EdgeIterator}) = Edge function Base.iterate(eitr::EdgeIterator, index = 1) - if index > eitr.l + if eitr.l == 0 || Polymake.isdone(eitr.pm_itr) return nothing else e = Polymake.get_element(eitr.pm_itr) @@ -358,6 +358,7 @@ function Base.iterate(eitr::EdgeIterator, index = 1) t = Polymake.last(e) edge = Edge(s+1, t+1) Polymake.increment(eitr.pm_itr) + eitr.l -= 1 return (edge, index+1) end end diff --git a/src/PolyhedralGeometry/Polyhedron/properties.jl b/src/PolyhedralGeometry/Polyhedron/properties.jl index 41b276fce644..98ab089f37ed 100644 --- a/src/PolyhedralGeometry/Polyhedron/properties.jl +++ b/src/PolyhedralGeometry/Polyhedron/properties.jl @@ -731,7 +731,7 @@ julia> dim(P) dim(P::Polyhedron) = Polymake.polytope.dim(pm_object(P))::Int @doc raw""" - lattice_points(P::Polyhedron{QQFieldElem}) + lattice_points(P::Polyhedron) Return the integer points contained in the bounded polyhedron `P`. @@ -757,7 +757,7 @@ julia> matrix(ZZ, lattice_points(S)) [2 0] ``` """ -function lattice_points(P::Polyhedron{QQFieldElem}) +function lattice_points(P::Polyhedron) @req pm_object(P).BOUNDED "Polyhedron not bounded" return SubObjectIterator{PointVector{ZZRingElem}}( P, _lattice_point, size(pm_object(P).LATTICE_POINTS_GENERATORS[1], 1) @@ -765,7 +765,7 @@ function lattice_points(P::Polyhedron{QQFieldElem}) end _lattice_point( - T::Type{PointVector{ZZRingElem}}, P::Polyhedron{QQFieldElem}, i::Base.Integer + T::Type{PointVector{ZZRingElem}}, P::Polyhedron, i::Base.Integer ) = point_vector(ZZ, @view pm_object(P).LATTICE_POINTS_GENERATORS[1][i, 2:end])::T _point_matrix(::Val{_lattice_point}, P::Polyhedron; homogenized=false) = @@ -774,7 +774,7 @@ _point_matrix(::Val{_lattice_point}, P::Polyhedron; homogenized=false) = _matrix_for_polymake(::Val{_lattice_point}) = _point_matrix @doc raw""" - interior_lattice_points(P::Polyhedron{QQFieldElem}) + interior_lattice_points(P::Polyhedron) Return the integer points contained in the interior of the bounded polyhedron `P`. @@ -792,7 +792,7 @@ julia> matrix(ZZ, interior_lattice_points(c)) [0 0 0] ``` """ -function interior_lattice_points(P::Polyhedron{QQFieldElem}) +function interior_lattice_points(P::Polyhedron) @req pm_object(P).BOUNDED "Polyhedron not bounded" return SubObjectIterator{PointVector{ZZRingElem}}( P, _interior_lattice_point, size(pm_object(P).INTERIOR_LATTICE_POINTS, 1) @@ -800,7 +800,7 @@ function interior_lattice_points(P::Polyhedron{QQFieldElem}) end _interior_lattice_point( - T::Type{PointVector{ZZRingElem}}, P::Polyhedron{QQFieldElem}, i::Base.Integer + T::Type{PointVector{ZZRingElem}}, P::Polyhedron, i::Base.Integer ) = point_vector(ZZ, @view pm_object(P).INTERIOR_LATTICE_POINTS[i, 2:end])::T _point_matrix(::Val{_interior_lattice_point}, P::Polyhedron; homogenized=false) = @@ -813,7 +813,7 @@ _point_matrix(::Val{_interior_lattice_point}, P::Polyhedron; homogenized=false) _matrix_for_polymake(::Val{_interior_lattice_point}) = _point_matrix @doc raw""" - boundary_lattice_points(P::Polyhedron{QQFieldElem}) + boundary_lattice_points(P::Polyhedron) Return the integer points contained in the boundary of the bounded polyhedron `P`. @@ -841,7 +841,7 @@ julia> matrix(ZZ, boundary_lattice_points(c)) [ 1 0 0] ``` """ -function boundary_lattice_points(P::Polyhedron{QQFieldElem}) +function boundary_lattice_points(P::Polyhedron) @req pm_object(P).BOUNDED "Polyhedron not bounded" return SubObjectIterator{PointVector{ZZRingElem}}( P, _boundary_lattice_point, size(pm_object(P).BOUNDARY_LATTICE_POINTS, 1) @@ -849,7 +849,7 @@ function boundary_lattice_points(P::Polyhedron{QQFieldElem}) end _boundary_lattice_point( - T::Type{PointVector{ZZRingElem}}, P::Polyhedron{QQFieldElem}, i::Base.Integer + T::Type{PointVector{ZZRingElem}}, P::Polyhedron, i::Base.Integer ) = point_vector(ZZ, @view pm_object(P).BOUNDARY_LATTICE_POINTS[i, 2:end])::T _point_matrix(::Val{_boundary_lattice_point}, P::Polyhedron; homogenized=false) = diff --git a/src/PolyhedralGeometry/helpers.jl b/src/PolyhedralGeometry/helpers.jl index b1526c7b62f3..0df7324f58f6 100644 --- a/src/PolyhedralGeometry/helpers.jl +++ b/src/PolyhedralGeometry/helpers.jl @@ -676,6 +676,14 @@ end # oscarnumber helpers +function Polymake._fieldelem_to_floor(e::Union{EmbeddedNumFieldElem,QQBarFieldElem}) + return BigInt(floor(ZZRingElem, e)) +end + +function Polymake._fieldelem_to_ceil(e::Union{EmbeddedNumFieldElem,QQBarFieldElem}) + return BigInt(ceil(ZZRingElem, e)) +end + function Polymake._fieldelem_to_rational(e::EmbeddedNumFieldElem) return Rational{BigInt}(QQ(e)) end diff --git a/src/Serialization/polymake.jl b/src/Serialization/polymake.jl index eb9be9d34aa9..012346ab1f28 100644 --- a/src/Serialization/polymake.jl +++ b/src/Serialization/polymake.jl @@ -95,6 +95,9 @@ _pmdata_for_oscar(v::Polymake.Vector{<:Polymake.Rational}, coeff::Field) = colle _pmdata_for_oscar(v::Polymake.SparseVector, coeff::Field) = _pmdata_for_oscar(Polymake.common.dense(v), coeff) +_pmdata_for_oscar(nm::Polymake.NodeMap, coeff::Field) = _pmdata_for_oscar(Polymake.Array(nm), coeff) +_pmdata_for_oscar(bd::Polymake.BasicDecoration, coeff::Field) = (_pmdata_for_oscar(Polymake.decoration_face(bd), coeff), _pmdata_for_oscar(Polymake.decoration_rank(bd), coeff)) + _pmdata_for_oscar(s::Polymake.Integer, coeff::Field) = ZZ(s) _pmdata_for_oscar(s::Polymake.Rational, coeff::Field) = QQ(s) _pmdata_for_oscar(s::Polymake.OscarNumber, coeff::Field) = coeff(s) @@ -106,6 +109,8 @@ _pmdata_for_oscar(s::Polymake.TropicalNumber{A}, coeff::Field) where A = tropica _pmdata_for_oscar(s::Polymake.CxxWrap.StdString, coeff::Field) = String(s) _pmdata_for_oscar(a::Polymake.Array, coeff::Field) = [_pmdata_for_oscar(e, coeff) for e in a] +_pmdata_for_oscar(a::Polymake.Array{T}, coeff::Field) where T <: Union{Polymake.Matrix, Polymake.Vector} = Tuple(_pmdata_for_oscar.(a, Ref(coeff))) + _pmdata_for_oscar(s::Polymake.Set, coeff::Field) = Set(_pmdata_for_oscar(e, coeff) for e in s) @@ -145,6 +150,7 @@ function _polyhedral_object_as_dict(x::Oscar.PolyhedralObjectUnion) end function _load_bigobject_from_dict!(obj::Polymake.BigObject, dict::Dict, parent_key::String="") + delay_loading = Tuple{String,Any}[] for (k, v) in dict key_str = parent_key == "" ? k : parent_key * "." * k first(k) == '_' && continue @@ -152,9 +158,22 @@ function _load_bigobject_from_dict!(obj::Polymake.BigObject, dict::Dict, parent_ if v isa Dict _load_bigobject_from_dict!(obj, v, key_str) else - Polymake.take(obj, key_str, convert(Polymake.PolymakeType, v)) + pmv = convert(Polymake.PolymakeType, v) + bot = Polymake.bigobject_type(obj) + # NodeMaps need extra treatment since the constructor doesn't accept polymake c++ arrays + # and we can't create a nodemap from scratch without the graph + # so we convert it to a pure perl array and delay loading until the end of this level + if pmv isa Polymake.Array && Polymake.bigobject_prop_type(bot, key_str) in ["NodeMap", "EdgeMap"] + pmv = Polymake.as_perl_array_of_array(pmv) + push!(delay_loading, (key_str, pmv)) + else + Polymake.take(obj, key_str, pmv) + end end end + for (k, v) in delay_loading + Polymake.take(obj, k, v) + end if haskey(dict, "_description") Polymake.setdescription!(obj, dict["_description"]) end diff --git a/test/Combinatorics/Graph.jl b/test/Combinatorics/Graph.jl index 7259ba0716fc..0e6ef72156b8 100644 --- a/test/Combinatorics/Graph.jl +++ b/test/Combinatorics/Graph.jl @@ -160,6 +160,16 @@ @test n_vertices(G2) == 13 @test n_edges(G2) == 5 + ei = edges(G2) + @test length(ei) == 5 + + ee = collect(ei) + @test length(ei) == 0 + @test collect(ei) == Edge[] + + GG2 = graph_from_edges(Undirected, ee, 13) + @test is_isomorphic(G2, GG2) + end @testset "adjacency_matrix laplacian_matrix" begin diff --git a/test/PolyhedralGeometry/polyhedron.jl b/test/PolyhedralGeometry/polyhedron.jl index 9a44f26384f1..e5d742777787 100644 --- a/test/PolyhedralGeometry/polyhedron.jl +++ b/test/PolyhedralGeometry/polyhedron.jl @@ -46,7 +46,6 @@ @test !([-1, -1] in Q0) @test n_vertices(Q0) == 3 @test n_vertices.(faces(Q0, 1)) == [2, 2, 2] - if T == QQFieldElem @test lattice_points(Q0) isa SubObjectIterator{PointVector{ZZRingElem}} @test point_matrix(lattice_points(Q0)) == matrix(ZZ, [0 0; 0 1; 1 0]) @test matrix(ZZ, lattice_points(Q0)) == matrix(ZZ, [0 0; 0 1; 1 0]) @@ -63,6 +62,7 @@ @test length(boundary_lattice_points(square)) == 8 @test boundary_lattice_points(square) == [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]] + if T == QQFieldElem @test is_smooth(Q0) @test is_normal(Q0) @test is_lattice_polytope(Q0) diff --git a/test/PolyhedralGeometry/scalar_types.jl b/test/PolyhedralGeometry/scalar_types.jl index 9bc361413fb6..a2c4c2cade1a 100644 --- a/test/PolyhedralGeometry/scalar_types.jl +++ b/test/PolyhedralGeometry/scalar_types.jl @@ -43,6 +43,9 @@ @test _edge_length_for_test.(faces(sd, 1)) == repeat([4], 18) # scaling the Polyhedron by 3 yields edge lengths of 6 @test _edge_length_for_test.(faces(3 * sd, 1)) == repeat([36], 18) + # there are 11 lattice points + @test length(lattice_points(sd)) == 11 + let pc = polyhedral_complex( E, IncidenceMatrix(facets(sd)), vertices(sd); non_redundant=true ) @@ -62,6 +65,8 @@ @test number_of_vertices(qp) == 3 @test number_of_facets(qp) == 3 + @test length(lattice_points(qp)) == 1 + @testset "Scalar detection" begin let j = johnson_solid(12) @test j isa Polyhedron{QQFieldElem} diff --git a/test/Serialization/PolyhedralGeometry.jl b/test/Serialization/PolyhedralGeometry.jl index e75f5bee8dcf..6d16fd8f8b66 100644 --- a/test/Serialization/PolyhedralGeometry.jl +++ b/test/Serialization/PolyhedralGeometry.jl @@ -30,23 +30,30 @@ using Oscar: _integer_variables @testset "Polyhedron" begin square = cube(2) + f_vector(square) test_save_load_roundtrip(path, square) do loaded @test n_vertices(square) == n_vertices(loaded) @test dim(square) == dim(loaded) @test square == loaded + @test Polymake.exists(Oscar.pm_object(loaded), "HASSE_DIAGRAM.DECORATION") end n2 = (QQBarField()(5))^(QQ(4//5)) c = cube(QQBarField(), 3, -1, n2) - test_save_load_roundtrip(path, square) do loaded - @test n_vertices(square) == n_vertices(loaded) - @test dim(square) == dim(loaded) - @test square == loaded + f_vector(c) + lattice_points(c) + test_save_load_roundtrip(path, c) do loaded + @test n_vertices(c) == n_vertices(loaded) + @test dim(c) == dim(loaded) + @test c == loaded + @test Polymake.exists(Oscar.pm_object(loaded), "HASSE_DIAGRAM.DECORATION") end d_hedron = dodecahedron() facets(d_hedron) vertices(d_hedron) + f_vector(d_hedron) + lattice_points(d_hedron) dict_ps = Dict{String, Any}( "unprecise" => polyhedron(