Skip to content

Commit

Permalink
Export strong_erings and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Liozou committed Jun 10, 2022
1 parent c52431d commit 6f048ee
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 24 deletions.
1 change: 1 addition & 0 deletions docs/src/rings.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,6 @@ PeriodicGraphs.gaussian_elimination!
PeriodicGraphs.retrieve_track!
PeriodicGraphs.rings_around
PeriodicGraphs.EdgeDict
strong_erings
PeriodicGraphs.convert_to_ering!
```
49 changes: 34 additions & 15 deletions src/algorithms/rings.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Ring statistics

export rings, strong_rings, RingAttributions, RingIncluding, normalize_cycle!
export rings, strong_rings, strong_erings, RingAttributions, RingIncluding,
normalize_cycle!

"""
ConstMiniBitSet{T} <: AbstractSet{Int}
Expand Down Expand Up @@ -42,8 +43,6 @@ function Base.iterate(::ConstMiniBitSet{T}, x::T) where T
return (first, x & (~(one(T) << (first % UInt8))))
end
Base.iterate(x::ConstMiniBitSet) = iterate(x, x.x)
Base.isdone(::ConstMiniBitSet{T}, x::T) where {T} = iszero(x)
Base.isdone(x::ConstMiniBitSet) = Base.isdone(x, x.x)

hasonly(x::ConstMiniBitSet{T}, i::Integer) where {T} = iszero(x.x & ~(one(T) << (i % UInt8)))

Expand Down Expand Up @@ -930,7 +929,7 @@ const VertexPair{D} = Tuple{PeriodicVertex{D},PeriodicVertex{D}}
"""
EdgeDict{D}
Internal map from pairs of `PeriodicVertex{D}` to the identifier of the corresponding edge.
Map from pairs of `PeriodicVertex{D}` to the identifier of the corresponding edge.
`kp::EdgeDict{D}` should be queried by either `get!(kp, minmax(v1, v2))` where `v1` and
`v2` are `PeriodicVertex{D}` to obtain the identifier of the edge and store a new
Expand Down Expand Up @@ -1282,6 +1281,22 @@ retrieve_track!(gauss::IterativeGaussianEliminationDecomposition) = retrieve_tra
# return ret
# end

"""
strong_erings([rs::Vector{Vector{Int}},], g::PeriodicGraph{D}, [depth=15,] ringsymms::AbstractSymmetryGroup=NoSymmetryGroup(length(rs))) where D
Compute the list of strong edge rings in `g`, up to length `2*depth+3`.
See [`strong_rings`](@ref) and [`rings`](@ref) for the meaning of the optional arguments.
Return a quadruplet of values:
- the two first values are the list of rings and their symmetry group, identical to the
result of [`strong_rings`](@ref), unless `rs` is provided (see below).
- the third is the list of edge rings: each edge of the periodic graph is mapped to an
integer and each ring is represented by the sorted list of its edges.
- the last is the mapping from edges to integers, given as an [`EdgeDict`](@ref).
If `rs` is provided, the first returned value is the list of indices `keep` of `rs` such
that `rs[keep]` is the list of strong rings.
"""
function strong_erings(rs::Vector{Vector{Int}}, g::PeriodicGraph{D}, depth=15, ringsymms::AbstractSymmetryGroup=NoSymmetryGroup(length(rs))) where D
kp = EdgeDict(g)
ecycles, origin = sort_cycles(g, rs, depth, kp)
Expand Down Expand Up @@ -1346,22 +1361,33 @@ function strong_erings(g::PeriodicGraph, depth=15, symmetries::AbstractSymmetryG
keep, symms, erings, kp = strong_erings(rs, g, depth, symmg)
return rs[keep], symms, erings, kp
end

function strong_erings(g::PeriodicGraph, symmetries::AbstractSymmetryGroup=NoSymmetryGroup(g), dist::DistanceRecord=DistanceRecord(g,15))
strong_erings(g, 15, symmetries, dist)
end

function strong_rings(rs::Vector{Vector{Int}}, g::PeriodicGraph{D}, depth=15, ringsymms::AbstractSymmetryGroup=NoSymmetryGroup(length(rs))) where D
keep, symms = strong_erings(rs, g, depth, ringsymms)
return rs[keep], symms
end

"""
strong_rings(g::PeriodicGraph{D}, [depth::Integer=15,] symmetries::AbstractSymmetryGroup=NoSymmetryGroup(g), dist::DistanceRecord=DistanceRecord(g,depth)) where D
strong_rings([rs::Vector{Vector{Int}},] g::PeriodicGraph{D}, [depth::Integer=15,] symmetries::AbstractSymmetryGroup=NoSymmetryGroup(g), dist::DistanceRecord=DistanceRecord(g,depth)) where D
Compute the list of strong rings in `g`, up to length `2*depth+3`. Return them with their
symmetry group. Each ring is represented by the list of [`hash_position`](@ref) of its
vertices.
The optional first argument `rs` is the list of rings which can be provided if previously
computed.
Compute the list of strong rings in `g`, up to length `2*depth+3`. See [`rings`](@ref) for
the meaning of the other arguments.
See [`rings`](@ref) for the meaning of the other arguments.
A strong ring is a cycle of the graph which cannot be decomposed into a sum of any number
of smaller cycles. By comparison, a ring is a cycle which cannot be decomposed into a sum
of two smaller cycles. In particular, all strong rings are rings.
See also [`strong_erings`](@ref) to obtain the rings as a list of integers representing the
edges of the ring, instead of a list of integers representing its vertices.
"""
function strong_rings(g::PeriodicGraph, depth::Integer=15, symmetries::AbstractSymmetryGroup=NoSymmetryGroup(g), dist::DistanceRecord=DistanceRecord(g,depth))
rs, symmg = rings(g, depth, symmetries, dist)
Expand Down Expand Up @@ -1433,9 +1459,6 @@ Base.@propagate_inbounds function Base.getindex(ras::RingAttributions, i::Intege
RingIncluding(ras, i)
end
Base.size(ras::RingAttributions) = size(ras.attrs)
Base.keys(ras::RingAttributions) = Base.OneTo(length(ras))
Base.firstindex(::RingAttributions) = 1
Base.lastindex(ras::RingAttributions) = length(ras)
Base.IndexStyle(::Type{RingAttributions{D}}) where {D} = Base.IndexLinear()

function Base.show(io::IO, ::MIME"text/plain", ras::RingAttributions)
Expand All @@ -1461,8 +1484,4 @@ function Base.getindex(ri::RingIncluding{D}, j::Integer) where {D}
return OffsetVertexIterator{D}(.-ofs, newring)
end
Base.size(ri::RingIncluding) = size(ri.ras.attrs[ri.i])

Base.keys(ri::RingIncluding) = Base.OneTo(length(ri))
Base.firstindex(::RingIncluding) = 1
Base.lastindex(ri::RingIncluding) = length(ri)
Base.IndexStyle(::Type{RingIncluding{D}}) where {D} = Base.IndexLinear()
7 changes: 0 additions & 7 deletions src/utils/graphsAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,8 @@ function Base.getindex(x::OffsetVertexIterator{D}, i::Int) where D
return PeriodicVertex{D}(neigh.v, neigh.ofs .+ x.ofs)
end
Base.size(x::OffsetVertexIterator) = (length(x.nlist),)
Base.keys(x::OffsetVertexIterator) = Base.OneTo(length(x))
Base.firstindex(::OffsetVertexIterator) = 1
Base.lastindex(x::OffsetVertexIterator) = length(x)
Base.IndexStyle(::Type{OffsetVertexIterator{D}}) where {D} = Base.IndexLinear()

# function Base.iterate(x::OffsetVertexIterator, state=1)
# (state % UInt) - 1 < length(x) ? ((@inbounds x[state]), state+1) : nothing
# end

for (neigh, deg) in ((:neighbors, :degree),
(:inneighbors, :indegree),
(:outneighbors, :outdegree))
Expand Down
33 changes: 31 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ end
gg = g[[3,1]]
@test nv(gg) == 2
@test collect(edges(gg)) == PeriodicEdge3D[(1, 2, (0,0,-1)), (2, 2, (1,0,0))]
@test gg == g[[true, false, true, false]][[2,1]]
@test_throws ArgumentError g[[3,3]]
@test_throws ArgumentError offset_representatives!(gg, [2])
gcopy = PeriodicGraph(g)
Expand Down Expand Up @@ -612,11 +613,14 @@ end

expected_neighbors = PeriodicVertex3D[(1, (7, -9, 1)), (2, (7, -10, 2)),
(2, (7, -9, 2)), (2, (8, -9, 2))]
@test collect(neighbors(g, PeriodicVertex(3, (7, -9, 2)))) == expected_neighbors
ofslist = neighbors(g, PeriodicVertex(3, (7, -9, 2)))
@test ofslist == collect(ofslist) == ofslist[begin:end] == expected_neighbors
@test keys(ofslist) == 1:4
@test neighbors(g, PeriodicVertex3D(2)) == neighbors(g, 2)
@test isempty(outneighbors(g, PeriodicVertex(5, (1, 0, 1))))
@test only(inneighbors(g, PeriodicVertex(1, (4, 5, 6)))) == PeriodicVertex(3, (4, 5, 7))
@test indegree(g, 2) == indegree(g, PeriodicVertex3D(2)) == outdegree(g, PeriodicVertex3D(2)) == degree(g, PeriodicVertex3D(2))
@test string(neighbors(g, PeriodicVertex(3, (7, -9, 2)))) == string(expected_neighbors)
@test string(ofslist) == string(expected_neighbors)
end

@testset "Vertex removal" begin
Expand Down Expand Up @@ -907,6 +911,8 @@ end
@test gausstrack.shortcuts == Int32[4, 7, 1, 2, 6, 3, 8]
@test length(gausstrack.rings) == 9

@test PeriodicGraphs.IterativeGaussianElimination().track == PeriodicGraphs.IterativeGaussianElimination{Nothing}().track == nothing

# keep track of the limitations
@test_throws ErrorException rings(lta, 63)
very_high_degree = PeriodicGraph{0}(128)
Expand Down Expand Up @@ -1014,6 +1020,14 @@ end
@test canonicalize_ri(r1) == canonicalize_ri(r2)
end

# AbstractArray interface
_ri = first(ras_sny2)
@test _ri == ras_sny2[1] == ras_sny2[begin]
@test last(ras_sny2) == ras_sny2[length(ras_sny2)] == ras_sny2[end]

@test _ri[1] == first(_ri) == _ri[begin]
@test _ri[length(_ri)] == last(_ri) == _ri[end]

ras_sny6 = RingAttributions(sny, true, 6)
rasym_sny6 = RingAttributions(sny, symmetries_sny)
@test length(ras_sny6) == length(rasym_sny6)
Expand Down Expand Up @@ -1067,6 +1081,7 @@ end
@test symmweaks(image) == id
end
end

strs, symmstrs = strong_rings(lta, symmetries_lta)
@test length(symmstrs) == length(symmetries_lta)
for str in strs
Expand All @@ -1080,6 +1095,20 @@ end
@test symmstrs(image) == id
end
end

_strs, _symmstrs, estrs, kp = strong_erings(lta, symmetries_lta)
@test _strs == strs
@test unique(symmstrs) == unique(_symmstrs)
@test length.(estrs) == length.(strs)
for (str, estr) in Iterators.take(zip(strs, estrs), 5)
@test issorted(estr)
for e in estr
@test get!(kp, kp[e]) == e
end
lenstr = length(str)
_edgestr = [minmax(str[i], str[mod1(i+1,lenstr)]) for i in 1:lenstr]
@test issetequal(_edgestr, minmax(hash_position.(kp[e], nv(lta))...) for e in estr)
end
end

@testset "Simple symmetries" begin
Expand Down

2 comments on commit 6f048ee

@Liozou
Copy link
Owner Author

@Liozou Liozou commented on 6f048ee Jun 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/62117

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.9.0 -m "<description of version>" 6f048ee6078373059642bdd88029712e9668dd3c
git push origin v0.9.0

Please sign in to comment.