diff --git a/src/PeriodicGraphs.jl b/src/PeriodicGraphs.jl index 2750204..b63e25e 100644 --- a/src/PeriodicGraphs.jl +++ b/src/PeriodicGraphs.jl @@ -984,5 +984,49 @@ function periodiccellgraph(g::PeriodicGraph) return ret end +""" + dimensionality(g::PeriodicGraph) + +Determines the actual dimension of each connected component of `g`. +Return a dictionary where each entry n => [l1, l2, ...] means that li is a +list of vertices that form a connected component of dimension n. +""" +dimensionality(g::PeriodicGraph{0}) = Dict{Int,Vector{Vector{Int}}}(0 => [collect(vertices(g))]) +function dimensionality(g::PeriodicGraph{N}) where N + ret = Dict{Int,Vector{Vector{Int}}}() + n = nv(g) + visited = falses(n) + nullofs = zero(SVector{N,Int}) + for i in vertices(g) + visited[i] && continue + recordedperiodicities = Set{SVector{N,Int}}() + component = Dict{Int,SVector{N,Int}}(i => nullofs) + seen = Set{PeriodicVertex{N}}([PeriodicVertex{N}(i)]) + Q = PeriodicVertex[(PeriodicVertex{N}(i))] + for src in Q + @assert !visited[src.v] + for dst in outneighbors(g, src.v) + dst = PeriodicVertex{N}(dst.v, dst.ofs .+ src.ofs) + dst ∈ seen && continue + push!(seen, dst) + lastperiodicity = get!(component, dst.v, dst.ofs) + thisperiodicity = dst.ofs .- lastperiodicity + if iszero(thisperiodicity) # First time we encounter dst.v + push!(Q, dst) + else + push!(recordedperiodicities, thisperiodicity) + end + end + end + newcomponent = collect(keys(component)) + visited[newcomponent] .= true + dim = rank(reduce(hcat, recordedperiodicities; init=reshape(nullofs, 3, 1))) + x = get!(ret, dim, []) + push!(x, newcomponent) + end + return ret +end + +include("precompile.jl") end diff --git a/src/precompile.jl b/src/precompile.jl new file mode 100644 index 0000000..f462296 --- /dev/null +++ b/src/precompile.jl @@ -0,0 +1,43 @@ +function _precompile_() + ccall(:jl_generating_output, Cint, ()) == 1 || return nothing + + Base.precompile(Tuple{Type{PeriodicGraph},String}) + + for i in 1:3 + Base.precompile(Tuple{Type{PeriodicEdge{i}},Int,Int,NTuple{i, Int}}) + Base.precompile(Tuple{Type{PeriodicVertex{i}},Int}) + + Base.precompile(Tuple{Type{PeriodicEdge},Int,Int,NTuple{i, Int}}) + Base.precompile(Tuple{Type{PeriodicEdge},Int,Int,SVector{i, Int}}) + Base.precompile(Tuple{Type{PeriodicEdge},Int,PeriodicVertex{i}}) + + Base.precompile(Tuple{Type{PeriodicGraph{i}}}) + Base.precompile(Tuple{Type{PeriodicGraph{i}},Vector{PeriodicEdge{i}}}) + Base.precompile(Tuple{Type{PeriodicGraph{i}},String}) + + Base.precompile(Tuple{typeof(==),PeriodicVertex{i},PeriodicVertex{i}}) + Base.precompile(Tuple{typeof(<),PeriodicVertex{i},PeriodicVertex{i}}) + Base.precompile(Tuple{typeof(==),PeriodicEdge{i},PeriodicEdge{i}}) + Base.precompile(Tuple{typeof(<),PeriodicEdge{i},PeriodicEdge{i}}) + + Base.precompile(Tuple{typeof(LightGraphs._neighborhood),PeriodicGraph{i},Int,Int,LightGraphs.DefaultDistance,Function}) + Base.precompile(Tuple{typeof(add_edge!),PeriodicGraph{i},PeriodicEdge{i}}) + Base.precompile(Tuple{typeof(add_vertices!),PeriodicGraph{i},Int}) + Base.precompile(Tuple{typeof(rem_edge!),PeriodicGraph{i},PeriodicEdge{i}}) + Base.precompile(Tuple{typeof(rem_vertex!),PeriodicGraph{i},Int}) + Base.precompile(Tuple{typeof(connected_components),PeriodicGraph{i}}) + Base.precompile(Tuple{typeof(coordination_sequence),PeriodicGraph{i},Int,Int}) + Base.precompile(Tuple{typeof(dimensionality),PeriodicGraph{i}}) + Base.precompile(Tuple{typeof(has_edge),PeriodicGraph{i},Int,Int}) + Base.precompile(Tuple{typeof(induced_subgraph),PeriodicGraph{i},Vector{Int}}) + Base.precompile(Tuple{typeof(isempty),PeriodicGraphs.PeriodicEdgeIter{i}}) + Base.precompile(Tuple{typeof(neighborhood),PeriodicGraph{i},Int,Int}) + Base.precompile(Tuple{typeof(offset_representatives!),PeriodicGraph{i},Vector{Int}}) + Base.precompile(Tuple{typeof(reverse),PeriodicEdge{i}}) + Base.precompile(Tuple{typeof(string),PeriodicEdge{i}}) + Base.precompile(Tuple{typeof(string),PeriodicGraph{i}}) + Base.precompile(Tuple{typeof(string),PeriodicVertex{i}}) + Base.precompile(Tuple{typeof(swap_axes!),PeriodicGraph{i},Vector{Int}}) + Base.precompile(Tuple{typeof(vertex_permutation),PeriodicGraph{i},Vector{Any}}) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 855f0e9..3900515 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ using Test using LightGraphs using PeriodicGraphs -import PeriodicGraphs: ofs, vertex_permutation, LoopException +import PeriodicGraphs: ofs, vertex_permutation, dimensionality, LoopException import StaticArrays: SVector # using Aqua @@ -355,3 +355,35 @@ end @test rem_vertex!(gg, 2) @test g == gg end + +@testset "Dimensionality" begin + @test dimensionality(PeriodicGraph{0}(5)) == Dict(0 => [collect(1:5)]) + g::PeriodicGraph3D = PeriodicGraph3D([PeriodicEdge3D(1, 3, (0, 0, 1)), + PeriodicEdge3D(2, 3, (0, 1, 0)), + PeriodicEdge3D(2, 3, (0, -1, 0)), + PeriodicEdge3D(4, 4, (1, 1, 0))]) + function equivalent_dict(d1, d2) + keys(d1) == keys(d2) || return false + for k in keys(d1) + s1 = Set(Set(x) for x in d1[k]) + s2 = Set(Set(x) for x in d2[k]) + s1 == s2 || return false + end + return true + end + @test equivalent_dict(dimensionality(g), Dict(1 => connected_components(g))) + @test add_edge!(g, PeriodicEdge(3, 2, (0,0,0))) + @test equivalent_dict(dimensionality(g), Dict(1 => connected_components(g))) + @test add_edge!(g, PeriodicEdge(2, 2, (0,0,1))) + @test equivalent_dict(dimensionality(g), Dict(1 => [[4]], 2 => [[1,2,3]])) + @test rem_edge!(g, PeriodicEdge(1, 3, (0,0,1))) + @test equivalent_dict(dimensionality(g), Dict(0 => [[1]], 1 => [[4]], 2 => [[2,3]])) + @test rem_edge!(g, PeriodicEdge(3, 2, (0,0,0))) + @test equivalent_dict(dimensionality(g), Dict(0 => [[1]], 1 => [[4]], 2 => [[2,3]])) + @test rem_edge!(g, PeriodicEdge(2, 3, (0,1,0))) + @test equivalent_dict(dimensionality(g), Dict(0 => [[1]], 1 => [[2,3],[4]])) + @test add_edge!(g, PeriodicEdge(2, 3, (1,0,-1))) + @test equivalent_dict(dimensionality(g), Dict(0 => [[1]], 1 => [[4]], 2 => [[2,3]])) + @test add_edge!(g, PeriodicEdge(2, 3, (2,0,-1))) + @test equivalent_dict(dimensionality(g), Dict(0 => [[1]], 1 => [[4]], 3 => [[2,3]])) +end