diff --git a/docs/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties.md b/docs/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties.md index 230fbfcdac93..93f47153d9ff 100644 --- a/docs/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties.md +++ b/docs/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties.md @@ -18,31 +18,13 @@ the affine and non-affine case: ## Equality of Normal Toric Varieties -!!! warning - Equality `==` of normal toric varieties currently checks equality of - memory locations. We recommend using `===`, which always checks - equality of memory locations in OSCAR. - -To check that the fans considered as sets of cones are equal, you may use the method below: - -```julia -function slow_equal(tv1::NormalToricVariety, tv2::NormalToricVariety) - tv1 === tv2 && return true - ambient_dim(tv1) == ambient_dim(tv2) || return false - f_vector(tv1) == f_vector(tv2) || return false - return Set(maximal_cones(tv1)) == Set(maximal_cones(tv2)) -end -``` - -Polyhedral fans can be stored in Polymake using either rays or -hyperplanes. In the former case, cones are stored as sets of indices of -rays, corresponding to polyhedral hulls, while in the latter case, cones -are stored as sets of indices of hyperplanes, corresponding to -intersections of hyperplanes. Converting between these two formats can -be expensive. In the case where the polyhedral fans of two normal toric -varieties are both stored using rays, the above method `slow_equal` has -computational complexity $O(n \log n)$, where $n$ is the number of -cones. +Equality `==` of normal toric varieties checks equality of the +corresponding polyhedral fans as sets of cones. +This computes the rays of both of the toric varieties, which can be +expensive if they are not already computed, meaning if +`"RAYS" in Polymake.list_properties(Oscar.pm_object(polyhedral_fan(X)))` +is false for one of the varieties. +Triple-equality `===` always checks equality of memory locations in OSCAR. ## Constructors diff --git a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl index bcfdbc6165a5..e1e5cfe3681f 100644 --- a/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl +++ b/src/AlgebraicGeometry/ToricVarieties/NormalToricVarieties/constructors.jl @@ -181,16 +181,74 @@ end # Equality ###################### -function Base.:(==)(tv1::NormalToricVariety, tv2::NormalToricVariety) - tv1 === tv2 && return true - error("Equality of normal toric varieties is computationally very demanding. More details in the documentation.") -end +@doc raw""" + (==)(X::NormalToricVariety, Y::NormalToricVariety) -> Bool + +Checks equality of the polyhedral fans as sets of cones. + +# Examples +```jldoctest +julia> H = hirzebruch_surface(NormalToricVariety, 0) +Normal toric variety -function Base.hash(tv::NormalToricVariety, h::UInt) - return hash(objectid(tv), h) +julia> P1 = projective_space(NormalToricVariety, 1) +Normal toric variety + +julia> H == P1 * P1 +true +``` +""" +function Base.:(==)(X::NormalToricVariety, Y::NormalToricVariety) + X === Y && return true + ambient_dim(X) == ambient_dim(Y) || return false + n_rays(X) == n_rays(Y) || return false + + # p is a permutation such that the i-th ray of X is the p(i)-th ray of Y + p = inv(perm(sortperm(rays(X)))) * perm(sortperm(rays(Y))) + + for i in 1:n_rays(X) + rays(X)[i] == rays(Y)[p(i)] || return false + end + @inline rows(Z) = [ + row(maximal_cones(IncidenceMatrix, Z), i) for i in 1:n_maximal_cones(Z) + ] + return Set(map(r -> Set(p.(r)), rows(X))) == Set(rows(Y)) end +@doc raw""" + _id(X::NormalToricVariety) + -> Tuple{Vector{Vector{QQFieldElem}}, Vector{Vector{Int64}}} + +Given a toric variety `X`, returns a pair `Oscar._id(X)` with the +following property: two toric varieties `X` and `Y` have equal +polyhedral fans, taken as sets of cones, if and only if +`Oscar._id(X) == Oscar._id(Y)`. +# Examples +```jldoctest +julia> H = hirzebruch_surface(NormalToricVariety, 0) +Normal toric variety + +julia> P1 = projective_space(NormalToricVariety, 1) +Normal toric variety + +julia> Oscar._id(H) == Oscar._id(P1 * P1) +true +``` +""" +function _id(X::NormalToricVariety) + p = inv(perm(sortperm(rays(X)))) + sorted_rays = Vector.(permuted(collect(rays(X)), p)) + @inline rows(Z) = [ + row(maximal_cones(IncidenceMatrix, Z), i) for i in 1:n_maximal_cones(Z) + ] + sorted_maximal_cones = sort(map(r -> sort(Vector(p.(r))), rows(X))) + return (sorted_rays, sorted_maximal_cones) +end + +function Base.hash(X::NormalToricVariety, h::UInt) + return hash(_id(X), h) +end ###################### # Display diff --git a/test/AlgebraicGeometry/ToricVarieties/normal_toric_varieties.jl b/test/AlgebraicGeometry/ToricVarieties/normal_toric_varieties.jl index a4432e9365f1..f76966c5fa0f 100644 --- a/test/AlgebraicGeometry/ToricVarieties/normal_toric_varieties.jl +++ b/test/AlgebraicGeometry/ToricVarieties/normal_toric_varieties.jl @@ -41,6 +41,43 @@ @testset "Equality of normal toric varieties" begin @test (p2 === f2) == false @test p2 === p2 - @test_throws ErrorException("Equality of normal toric varieties is computationally very demanding. More details in the documentation.") p2 == f2 + @test p2 != f2 + + X = projective_space(NormalToricVariety, 2) + X = domain(blow_up(X, [3, 4])) + X = domain(blow_up(X, [-2, -3])) + Y = weighted_projective_space(NormalToricVariety, [1, 2, 3]) + Y = domain(blow_up(Y, [-1, -1])) + Y = domain(blow_up(Y, [3, 4])) + @test X == Y + + Z = projective_space(NormalToricVariety, 2) + X = domain(blow_up(Z, [1, 1])) + Y = domain(blow_up(Z, [1, 2])) + @test X != Y + + H = hirzebruch_surface(NormalToricVariety, 0) + P1 = projective_space(NormalToricVariety, 1) + ray_generators = [[1, 1], [1, 2]] + max_cones = incidence_matrix([[1, 2]]) + X = normal_toric_variety(max_cones, ray_generators) + @test length(Set([H, P1 * P1, X])) == 2 + + @testset "Speed test hash (at most 0.5 seconds)" begin + success = false + ntv5 = normal_toric_variety(polarize(polyhedron(Polymake.polytope.rand_sphere(5, 60; seed=42)))) + hash(ntv5) + for i in 1:5 + stats = @timed hash(ntv5) + duration = stats.time - stats.gctime + if duration < 0.5 + success = true + break + else + @warn "Hash took $duration > 0.5 seconds (i=$i)" + end + end + @test success == true + end end end