-
Notifications
You must be signed in to change notification settings - Fork 103
Prufer coding for trees #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
5ee514c
add is_tree func + exports
SimonCoste 1567bfc
add doc for exported functions
SimonCoste 3bee0c1
add test structure (wip)
SimonCoste 8cf308a
treesize must be >2
SimonCoste 8cb4069
fixed types
SimonCoste 887f8e0
prufer tests
SimonCoste 1078143
renaming prufer to trees
SimonCoste 56c00f6
added docs
SimonCoste 98fdf29
@traitfn for is_tree
SimonCoste 6f1eb44
Update src/trees/prufer.jl
SimonCoste 1cf22a8
better input types
SimonCoste a8fa527
Support for empty sequence / 1-edge tree
SimonCoste 18765b5
update doc for the 1-edge tree
SimonCoste 3c88fd3
formatting
SimonCoste b048c2f
fix bug for 2-nodes trees
SimonCoste 9e44c46
strengthen return types
SimonCoste f1628bb
convention for naming vertices
SimonCoste e81f6b7
Initialize empty graphs with zero edges
SimonCoste fe6303e
various small improvements
SimonCoste 27f497f
added edge tests (forest, empty trees, self-loops)
SimonCoste fc93e6f
uniform tree generator
SimonCoste 5d1a1fb
tests for uniform trees
SimonCoste 4a2d916
revert some silly code
SimonCoste be8be2c
fixed typos in docs
SimonCoste d2e6f56
removed trailing space
SimonCoste e59a993
determine return type from arg in uniform tree
SimonCoste c635042
removed seed from arg
SimonCoste 426d438
added missing arg in rng_from_rng_or_seed
SimonCoste File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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,19 @@ | ||
# Trees | ||
|
||
_Graphs.jl_ algorithms related to [trees](https://en.wikipedia.org/wiki/Tree_(graph_theory)). | ||
|
||
## Index | ||
|
||
```@index | ||
Pages = ["trees.md"] | ||
``` | ||
|
||
## Full docs | ||
|
||
```@autodocs | ||
Modules = [Graphs] | ||
Pages = [ | ||
"trees/prufer.jl", | ||
] | ||
|
||
``` |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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,96 @@ | ||
""" | ||
is_tree(g) | ||
|
||
Returns true if g is a tree: that is, a simple, connected undirected graph, with nv-1 edges (nv = number of vertices). Trees are the minimal connected graphs; equivalently they have no cycles. | ||
|
||
This function does not apply to directed graphs. Directed trees are sometimes called [polytrees](https://en.wikipedia.org/wiki/Polytree)). | ||
|
||
""" | ||
function is_tree end | ||
|
||
@traitfn function is_tree(g::::(!IsDirected)) | ||
return ne(g) == nv(g) - 1 && is_connected(g) | ||
end | ||
|
||
function _is_prufer(c::AbstractVector{T}) where {T<:Integer} | ||
return isempty(c) || (minimum(c) >= 1 && maximum(c) <= length(c) + 2) | ||
end | ||
|
||
function _degree_from_prufer(c::Vector{T})::Vector{T} where {T<:Integer} | ||
""" | ||
Degree sequence from Prüfer code. | ||
Returns d such that d[i] = 1 + number of occurences of i in c | ||
""" | ||
n = length(c) + 2 | ||
d = ones(T, n) | ||
for value in c | ||
d[value] += 1 | ||
end | ||
return d | ||
end | ||
|
||
""" | ||
prufer_decode(code) | ||
|
||
Returns the unique tree associated with the given (Prüfer) code. | ||
Each tree of size n is associated with a Prüfer sequence (a[1], ..., a[n-2]) with 1 ⩽ a[i] ⩽ n. The sequence is constructed recursively by the leaf removal algoritm. At step k, the leaf with smallest index is removed and its unique neighbor is added to the Prüfer sequence, giving a[k]. The decoding algorithm goes backward. The tree associated with the empty sequence is the 2-vertices tree with one edge. | ||
Ref: [Prüfer sequence on Wikipedia](https://en.wikipedia.org/wiki/Pr%C3%BCfer_sequence) | ||
|
||
""" | ||
function prufer_decode(code::AbstractVector{T})::SimpleGraph{T} where {T<:Integer} | ||
!_is_prufer(code) && throw( | ||
ArgumentError("The code must have one dimension and must be a Prüfer sequence. "), | ||
) | ||
isempty(code) && return path_graph(T(2)) # the empty Prüfer sequence codes for the one-edge tree | ||
|
||
n = length(code) + 2 | ||
d = _degree_from_prufer(code) | ||
L = BinaryMinHeap{T}(findall(==(1), d)) | ||
g = Graph{T}(n, 0) | ||
SimonCoste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for i in 1:(n - 2) | ||
l = pop!(L) # extract leaf with priority rule (max) | ||
d[l] -= 1 # update degree sequence | ||
add_edge!(g, l, code[i]) # add edge | ||
d[code[i]] -= 1 # update degree sequence | ||
d[code[i]] == 1 && push!(L, code[i]) # add new leaf if any | ||
end | ||
|
||
add_edge!(g, pop!(L), pop!(L)) # add last leaf | ||
|
||
return g | ||
end | ||
|
||
""" | ||
prufer_encode(g::SimpleGraph) | ||
|
||
Given a tree (a connected minimal undirected graph) of size n⩾3, returns the unique Prüfer sequence associated with this tree. | ||
|
||
Each tree of size n ⩾ 2 is associated with a Prüfer sequence (a[1], ..., a[n-2]) with 1 ⩽ a[i] ⩽ n. The sequence is constructed recursively by the leaf removal algoritm. At step k, the leaf with smallest index is removed and its unique neighbor is added to the Prüfer sequence, giving a[k]. The Prüfer sequence of the tree with only one edge is the empty sequence. | ||
|
||
Ref: [Prüfer sequence on Wikipedia](https://en.wikipedia.org/wiki/Pr%C3%BCfer_sequence) | ||
|
||
""" | ||
function prufer_encode(G::SimpleGraph{T})::Vector{T} where {T<:Integer} | ||
(nv(G) < 2 || !is_tree(G)) && | ||
throw(ArgumentError("The graph must be a tree with n ⩾ 2 vertices. ")) | ||
|
||
n = nv(G) | ||
n == 2 && return Vector{T}() # empty Prüfer sequence | ||
g = copy(G) | ||
code = zeros(T, n - 2) | ||
d = degree(g) | ||
L = BinaryMinHeap(findall(==(1), d)) | ||
|
||
for i in 1:(n - 2) | ||
u = pop!(L) | ||
v = neighbors(g, u)[1] | ||
rem_edge!(g, u, v) | ||
d[u] -= 1 | ||
d[v] -= 1 | ||
d[v] == 1 && push!(L, v) | ||
code[i] = v | ||
end | ||
|
||
return code | ||
end |
This file contains hidden or 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 hidden or 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 hidden or 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,69 @@ | ||
function _harmonize_type(g::AbstractGraph{T}, c::Vector{S}) where {T,S<:Integer} | ||
return convert(Vector{T}, c) | ||
end | ||
|
||
@testset "Tree utilities" begin | ||
t1 = Graph(6) | ||
for e in [(1, 4), (2, 4), (3, 4), (4, 5), (5, 6)] | ||
add_edge!(t1, e...) | ||
end | ||
code = [4, 4, 4, 5] | ||
|
||
t2 = path_graph(2) | ||
t3 = star_graph(10) | ||
t4 = binary_tree(3) | ||
|
||
f1 = Graph(8, 0) #forest with 4 tree components | ||
for e in [(1, 2), (2, 3), (4, 5), (6, 7)] | ||
add_edge!(f1, e...) | ||
end | ||
|
||
g1 = cycle_graph(8) | ||
g2 = complete_graph(4) | ||
g3 = Graph(1, 0) | ||
g4 = Graph(2, 0) | ||
g5 = Graph(1, 0) | ||
add_edge!(g5, 1, 1) # loop | ||
|
||
d1 = cycle_digraph(5) | ||
d2 = path_digraph(5) | ||
d3 = DiGraph(2) | ||
add_edge!(d3, 1, 2) | ||
add_edge!(d3, 2, 1) | ||
|
||
@testset "tree_check" begin | ||
for t in testgraphs(t1, t2, t3, t4, g3) | ||
@test is_tree(t) | ||
end | ||
for g in testgraphs(g1, g2, g4, g5, f1) | ||
@test !is_tree(g) | ||
end | ||
for g in testgraphs(d1, d2, d3) | ||
@test_throws MethodError is_tree(g) | ||
end | ||
end | ||
|
||
@testset "encode/decode" begin | ||
@test prufer_decode(code) == t1 | ||
@test prufer_encode(t1) == code | ||
for g in testgraphs(t2, t3, t4) | ||
ret_code = prufer_encode(g) | ||
@test prufer_decode(ret_code) == g | ||
end | ||
end | ||
|
||
@testset "errors" begin | ||
b1 = [5, 8, 10, 1, 2] | ||
b2 = [0.5, 1.1] | ||
@test_throws ArgumentError prufer_decode(b1) | ||
@test_throws MethodError prufer_decode(b2) | ||
|
||
for g in testgraphs(g1, g2, g3, g4, g5, f1) | ||
@test_throws ArgumentError prufer_encode(g) | ||
end | ||
|
||
for g in testgraphs(d1, d2, d3) | ||
@test_throws MethodError prufer_encode(g) | ||
end | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.