-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- this commits a large number of updates to the BandGraphs code; many aspects have received corrections, very aggressive performance optimizations (e.g., work-arrays), as well as algorithmic improvements (e.g., checks of articulation point), and many changes to the types - we also now do a much more general job of splitting degeneracies: in particular, we now allow splitting of degeneracies connected to non-nondegenerate nonmaximal irreps; this required multiset permutations. - the main point of entry at the moment is in `test/scan-separable-irreps.jl`
- Loading branch information
Showing
17 changed files
with
1,181 additions
and
325 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Copy of https://github.com/JuliaGraphs/Graphs.jl/pull/407, until it is merged: | ||
# implements `count_connected_components(g, [label, search_queue])`. | ||
# TODO: remove when merged | ||
|
||
""" | ||
_connected_components!(label, g, [search_queue]) | ||
Fill `label` with the `id` of the connected component in the undirected graph | ||
`g` to which it belongs. Return a vector representing the component assigned | ||
to each vertex. The component value is the smallest vertex ID in the component. | ||
A `search_queue`, an empty `Vector{eltype(edgetype(g))}`, can be provided to reduce | ||
allocations if `_connected_components!` is intended to be called multiple times sequentially. | ||
If not provided, it is automatically instantiated. | ||
### Performance | ||
This algorithm is linear in the number of edges of the graph. | ||
""" | ||
function _connected_components!( | ||
label::AbstractVector, g::AbstractGraph{T}, search_queue::Vector{T}=Vector{T}() | ||
) where {T} | ||
isempty(search_queue) || error("provided `search_queue` is not empty") | ||
for u in vertices(g) | ||
label[u] != zero(T) && continue | ||
label[u] = u | ||
push!(search_queue, u) | ||
while !isempty(search_queue) | ||
src = popfirst!(search_queue) | ||
for vertex in all_neighbors(g, src) | ||
if label[vertex] == zero(T) | ||
push!(search_queue, vertex) | ||
label[vertex] = u | ||
end | ||
end | ||
end | ||
end | ||
return label | ||
end | ||
|
||
""" | ||
count_connected_components( g, [label, search_queue]; reset_label::Bool=false) | ||
Return the number of connected components in `g`. | ||
Equivalent to `length(connected_components(g))` but uses fewer allocations by not | ||
materializing the component vectors explicitly. Additionally, mutated work-arrays `label` | ||
and `search_queue` can be provided to reduce allocations further (see | ||
[`_connected_components!`](@ref)). | ||
## Keyword arguments | ||
- `reset_label :: Bool` (default, `false`): if `true`, `label` is reset to zero before | ||
returning. | ||
## Example | ||
``` | ||
julia> using Graphs | ||
julia> g = Graph(Edge.([1=>2, 2=>3, 3=>1, 4=>5, 5=>6, 6=>4, 7=>8])); | ||
length> connected_components(g) | ||
3-element Vector{Vector{Int64}}: | ||
[1, 2, 3] | ||
[4, 5, 6] | ||
[7, 8] | ||
julia> count_connected_components(g) | ||
3 | ||
``` | ||
""" | ||
function count_connected_components( | ||
g::AbstractGraph{T}, | ||
label::AbstractVector=zeros(T, nv(g)), | ||
search_queue::Vector{T}=Vector{T}(); | ||
reset_label::Bool=false | ||
) where T | ||
_connected_components!(label, g, search_queue) | ||
c = count_unique(label) | ||
reset_label && fill!(label, zero(eltype(label))) | ||
return c | ||
end | ||
|
||
function count_unique(label::Vector{T}) where T | ||
seen = Set{T}() | ||
c = 0 | ||
for l in label | ||
if l ∉ seen | ||
push!(seen, l) | ||
c += 1 | ||
end | ||
end | ||
return c | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
struct MultiSetPermutationIndices | ||
f::Vector{Int} | ||
end | ||
Base.eltype(::Type{MultiSetPermutationIndices}) = Vector{Int} | ||
Base.length(c::MultiSetPermutationIndices) = _multinomial(c.f) | ||
|
||
# copy of https://github.com/JuliaMath/Combinatorics.jl/pull/170, until merged & public | ||
function _multinomial(k) | ||
s = zero(eltype(k)) | ||
result = one(eltype(k)) | ||
@inbounds for i in k | ||
s += i | ||
result *= binomial(s, i) | ||
end | ||
result | ||
end | ||
|
||
""" | ||
multiset_permutation_indices(f :: Vector{Int}) | ||
Return a generator over all index permutations corresponding to the multiset permutations of | ||
a multiset `f = {f[1]*1, f[2]*2, ...}`, i.e. of a multiset with `f[1]` elements of `1`, | ||
`f[2]` elements of `2`, and so on. | ||
More explicitly, `f[i]` indicates the frequency of the `i`th element of the underlying | ||
multiset `a`, which can consequently be considered as the sequence or sorted multiset: | ||
`a` = [`1` repeated `f[1]` times, `2` repeated `f[2]` times, ...]. | ||
The indices `inds` of the unique permutations of `a` are returned. Compared to the iterator | ||
`multiset_permutations(a, length(a))` - which returns the actual permutations, not the | ||
indices - from Combinatorics.jl, this means that | ||
`a[ind] == collect(multiset_permutations(a), length(a))[ind]` for all `ind in inds`. | ||
## Implementation | ||
The implementation here is adapted from the implementation in `multiset_permutations` in | ||
Combinatorics.jl, which in turn appears to follow the Algorithm L (p. 319) from Knuth's | ||
TAOCP Vol. 4A. | ||
""" | ||
multiset_permutation_indices(f::Vector{Int}) = MultiSetPermutationIndices(f) | ||
|
||
function canonical_multiset(f::Vector{Int}) | ||
# build the sequence `a` = [`1` repeated `f[1]` times, `2` repeated `f[2]` times, ...]. | ||
a = Vector{Int}(undef, sum(f)) | ||
N = 1 | ||
for (i, n) in enumerate(f) | ||
@inbounds a[N:N+n-1] .= i | ||
N += n | ||
end | ||
return a | ||
end | ||
|
||
function Base.iterate( | ||
p::MultiSetPermutationIndices, | ||
composite_state = (a=canonical_multiset(p.f); (a, collect(eachindex(a)))) | ||
) | ||
state, indices = composite_state | ||
if !isempty(state) && state[1] > length(state) | ||
return nothing | ||
end | ||
return nextpermutation!(state, indices) | ||
end | ||
|
||
function nextpermutation!(state::Vector{Int}, indices::Vector{Int}) | ||
i = length(state) - 1 | ||
indices_next = copy(indices) | ||
while i ≥ 1 && state[i] ≥ state[i+1] | ||
i -= 1 | ||
end | ||
if i > 0 | ||
j = length(state) | ||
while j > i && state[i] ≥ state[j] | ||
j -= 1 | ||
end | ||
state[i], state[j] = state[j], state[i] | ||
indices_next[i], indices_next[j] = indices_next[j], indices_next[i] | ||
reverse!(state, i+1) | ||
reverse!(indices_next, i+1) | ||
else | ||
state[1] = length(state) + 1 # finished; no more permutations | ||
end | ||
|
||
return indices, (state, indices_next) | ||
end |
Oops, something went wrong.