Skip to content

Commit

Permalink
Test functions for regular solids (#3512)
Browse files Browse the repository at this point in the history
* add functions is_platonic_solid, is_archimedean_solid, is_johnson_solid and is_vertex_transitive

---------

Co-authored-by: Alexej Jordan <[email protected]>
Co-authored-by: Lars Kastner <[email protected]>
  • Loading branch information
3 people authored Apr 18, 2024
1 parent 1303662 commit e482be6
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 7 deletions.
3 changes: 3 additions & 0 deletions docs/src/PolyhedralGeometry/Polyhedra/auxiliary.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ is_normal(P::Polyhedron{QQFieldElem})
is_simple(P::Polyhedron)
is_smooth(P::Polyhedron{QQFieldElem})
is_very_ample(P::Polyhedron{QQFieldElem})
is_archimedean_solid(P::Polyhedron)
is_johnson_solid(P::Polyhedron)
is_platonic_solid(P::Polyhedron)
lattice_points(P::Polyhedron{QQFieldElem})
lattice_volume(P::Polyhedron{QQFieldElem})
normalized_volume(P::Polyhedron)
Expand Down
163 changes: 163 additions & 0 deletions src/PolyhedralGeometry/Polyhedron/properties.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,169 @@ false
"""
is_fulldimensional(P::Polyhedron) = pm_object(P).FULL_DIM::Bool

@doc raw"""
is_johnson_solid(P::Polyhedron)
Check whether `P` is a Johnson solid, i.e., a $3$-dimensional polytope with regular faces that is not vertex transitive.
See also [`johnson_solid`](@ref).
!!! note
This will only recognize algebraically precise solids, i.e. no solids with approximate coordinates.
# Examples
```
julia> J = johnson_solid(37)
Polytope in ambient dimension 3 with EmbeddedAbsSimpleNumFieldElem type coefficients
julia> is_johnson_solid(J)
true
```
"""
is_johnson_solid(P::Polyhedron) = _is_3d_pol_reg_facets(P) && !is_vertex_transitive(P)

@doc raw"""
is_archimedean_solid(P::Polyhedron)
Check whether `P` is an Archimedean solid, i.e., a $3$-dimensional vertex
transitive polytope with regular facets, but not a prism or antiprism.
See also [`archimedean_solid`](@ref).
!!! note
This will only recognize algebraically precise solids, i.e. no solids with approximate coordinates.
# Examples
```jldoctest
julia> TO = archimedean_solid("truncated_octahedron")
Polytope in ambient dimension 3
julia> is_archimedean_solid(TO)
true
julia> T = tetrahedron()
Polytope in ambient dimension 3
julia> is_archimedean_solid(T)
false
```
"""
is_archimedean_solid(P::Polyhedron) = _is_3d_pol_reg_facets(P) && !_has_equal_facets(P) && is_vertex_transitive(P) && !_is_prismic_or_antiprismic(P)

@doc raw"""
is_platonic_solid(P::Polyhedron)
Check whether `P` is a Platonic solid.
See also [`platonic_solid`](@ref).
!!! note
This will only recognize algebraically precise solids, i.e. no solids with approximate coordinates.
# Examples
```jldoctest
julia> is_platonic_solid(cube(3))
true
```
"""
is_platonic_solid(P::Polyhedron) = _is_3d_pol_reg_facets(P) && _has_equal_facets(P) && is_vertex_transitive(P)

function _is_3d_pol_reg_facets(P::Polyhedron)
# dimension
dim(P) == 3 || return false

# constant edge length
pedges = faces(P, 1)

edgelength = let v = vertices(pedges[1])
_squared_distance(v[1], v[2])
end

for edge in pedges[2:end]
v = vertices(edge)
_squared_distance(v[1], v[2]) == edgelength || return false
end

pfacets = facets(Polyhedron, P)

# facet vertices on circle
for facet in pfacets
n = n_vertices(facet)
if n >= 4
fverts = vertices(facet)
m = sum(fverts)//n
dist = _squared_distance(fverts[1], m)

for v in fverts[2:end]
_squared_distance(v, m) == dist || return false
end
end
end
return true
end

function _squared_distance(p::PointVector, q::PointVector)
d = p - q
return dot(d, d)
end

function _has_equal_facets(P::Polyhedron)
nv = facet_sizes(P)
return @static VERSION >= v"1.8" ? allequal(nv) : length(unique(nv)) == 1
end

@doc raw"""
is_vertex_transitive(P::Polyhedron)
Check whether `P` is vertex transitive.
# Examples
```jldoctest
julia> is_vertex_transitive(cube(3))
true
julia> is_vertex_transitive(pyramid(cube(2)))
false
```
"""
is_vertex_transitive(P::Polyhedron) = is_transitive(automorphism_group(P; action = :on_vertices))

function _is_prismic_or_antiprismic(P::Polyhedron)
nvf = facet_sizes(P)
# nvfs contains vector entries [i, j] where j is the amount of i-gonal facets of P
nvfs = [[i, sum(nvf .== i)] for i in unique(nvf)]
# an (anti-)prism can only have 2 different types of facets
# n-gons and either squares/triangles (for prisms/antiprisms respectively)
# if n == 1 this function will not be called either way
length(nvfs) == 2 || return false
# there are exactly 2 n-gons, and only n-gons can occur exactly twice
a = findfirst(x -> x[2] == 2, nvfs)
isnothing(a) && return false
# with length(nvfs) == 2, b is the other index than a
b = 3 - a
m = nvfs[b][1]
# the facets that are not n-gons have to be squares or triangles
m == 3 || m == 4 || return false
n = nvfs[a][1]
# P has to have exactly n vertices and
# the amount of squares needs to be n or the amount of triangles needs to be 2n
2n == n_vertices(P) && (5 - m)*n == nvfs[b][2] || return false
dg = dualgraph(P)
ngon_is = findall(x -> x == n, nvf)
has_edge(dg, ngon_is...) && return false
rem_vertex!(dg, ngon_is[2])
rem_vertex!(dg, ngon_is[1])
degs = [length(neighbors(dg, i)) for i in 1:n_vertices(dg)]
degs == fill(2, n_vertices(dg)) && is_connected(dg) || return false
dg = dualgraph(P)
if m == 3
bigdg = Polymake.graph.Graph(ADJACENCY = dg.pm_graph)
return bigdg.BIPARTITE
else
return true
end
end

@doc raw"""
f_vector(P::Polyhedron)
Expand Down
18 changes: 12 additions & 6 deletions src/PolyhedralGeometry/Polyhedron/standard_constructions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ Construct the `i`-th proper Johnson solid.
A Johnson solid is a 3-polytope whose facets are regular polygons, of various gonalities.
It is proper if it is not an Archimedean solid. Up to scaling there are exactly 92 proper Johnson solids.
See also [`is_johnson_solid`](@ref).
"""
function johnson_solid(index::Int)
if index in _johnson_indexes_from_oscar
Expand Down Expand Up @@ -717,8 +719,10 @@ cross_polytope(d::Int64) = cross_polytope(QQFieldElem, d)
Construct a Platonic solid with the name given by String `s` from the list
below.
See also [`is_platonic_solid`](@ref).
# Arguments
- `s::String`: The name of the desired Archimedean solid.
- `s::String`: The name of the desired Platonic solid.
Possible values:
- "tetrahedron" : Tetrahedron.
Regular polytope with four triangular facets.
Expand Down Expand Up @@ -746,8 +750,10 @@ platonic_solid(s::String) = polyhedron(Polymake.polytope.platonic_solid(s))
archimedean_solid(s)
Construct an Archimedean solid with the name given by String `s` from the list
below. The polytopes are realized with floating point numbers and thus not
exact; Vertex-facet-incidences are correct in all cases.
below. Some of these polytopes are realized with floating point numbers and
thus not exact; Vertex-facet-incidences are correct in all cases.
See also [`is_archimedean_solid`](@ref).
# Arguments
- `s::String`: The name of the desired Archimedean solid.
Expand Down Expand Up @@ -806,9 +812,9 @@ archimedean_solid(s::String) = polyhedron(Polymake.polytope.archimedean_solid(s)
@doc raw"""
catalan_solid(s::String)
Construct a Catalan solid with the name `s` from the list
below. The polytopes are realized with floating point coordinates and thus are not
exact. However, vertex-facet-incidences are correct in all cases.
Construct a Catalan solid with the name `s` from the list below. Some of these
polytopes are realized with floating point coordinates and thus are not exact.
However, vertex-facet-incidences are correct in all cases.
# Arguments
- `s::String`: The name of the desired Archimedean solid.
Expand Down
4 changes: 4 additions & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ export is_algebraically_independent_with_relations
export is_almost_simple, has_is_almost_simple, set_is_almost_simple
export is_alternating
export is_ample
export is_archimedean_solid
export is_ball
export is_basepoint_free
export is_bicoset
Expand Down Expand Up @@ -791,6 +792,7 @@ export is_isomorphic_to_symmetric_group, has_is_isomorphic_to_symmetric_group, s
export is_isomorphic_with_map
export is_isomorphic_with_permutation
export is_isomorphism
export is_johnson_solid
export is_k_separation
export is_lattice_polytope
export is_left
Expand All @@ -815,6 +817,7 @@ export is_orbifold
export is_perfect, has_is_perfect, set_is_perfect
export is_pgroup, has_is_pgroup, set_is_pgroup
export is_pgroup_with_prime
export is_platonic_solid
export is_pointed
export is_positively_graded
export is_primary
Expand Down Expand Up @@ -864,6 +867,7 @@ export is_unit
export is_unital
export is_vertical_k_separation
export is_very_ample
export is_vertex_transitive
export is_weakly_connected
export is_welldefined
export is_z_graded
Expand Down
42 changes: 41 additions & 1 deletion test/PolyhedralGeometry/polyhedron.jl
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,51 @@

end

@testset "Johnson solids" begin
@testset "Regular solids" begin

for i in Oscar._johnson_indexes_from_oscar
j = johnson_solid(i)
@test j isa Polyhedron{<:EmbeddedNumFieldElem}
@test Polymake.polytope.isomorphic(Oscar.pm_object(j), Polymake.polytope.johnson_solid(i))
end

let p = platonic_solid("dodecahedron")
@test is_platonic_solid(p)
@test !is_archimedean_solid(p)
@test !is_johnson_solid(p)
@test is_vertex_transitive(p)
end

let a = archimedean_solid("rhombicuboctahedron")
@test !is_platonic_solid(a)
@test is_archimedean_solid(a)
@test !is_johnson_solid(a)
@test is_vertex_transitive(a)
@test !Oscar._is_prismic_or_antiprismic(a)
end

let j = johnson_solid(69)
@test !is_platonic_solid(j)
@test !is_archimedean_solid(j)
@test is_johnson_solid(j)
@test !is_vertex_transitive(j)
end

K = algebraic_closure(QQ)
e = one(K)
s, c = sinpi(2*e/7), cospi(2*e/7)
mat_rot = matrix([ c -s ; s c ])
mat_sigma1 = matrix(K, [ -1 0 ; 0 1 ])
G_mat = matrix_group(mat_rot, mat_sigma1)
p = K.([0,1]) # coordinates of vertex 1, expressed over K
orb = orbit(G_mat, *, p)
pts = collect(orb)
len = sqrt(dot(pts[1]-pts[2],pts[1]-pts[2])) / 2
ngon = convex_hull(pts)
prism = polyhedron(Polymake.polytope.prism(Oscar.pm_object(ngon),len))
@test Oscar._is_prismic_or_antiprismic(prism)
@test !is_archimedean_solid(prism)
@test !is_johnson_solid(prism)


end

0 comments on commit e482be6

Please sign in to comment.