Skip to content

Commit

Permalink
Expose seed kwarg in flow cutter (#9)
Browse files Browse the repository at this point in the history
* Update flow cutter arguments

* Bump version number
  • Loading branch information
brenjohn authored Apr 14, 2021
1 parent b20559c commit a030c09
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "QXGraphDecompositions"
uuid = "b15f1ded-d19a-4aca-a940-1991fcfc7750"
authors = ["QuantEx team"]
version = "0.1.4"
version = "0.1.5"

[deps]
FlowCutterPACE17_jll = "008204e2-cd5c-5c6d-9360-d31f32b5f6c2"
Expand Down
32 changes: 18 additions & 14 deletions src/treewidth.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export quickbb
# **************************************************************************************** #

"""
flow_cutter(G::lg.AbstractGraph; time::Integer=10)
flow_cutter(G::lg.AbstractGraph, time::Integer=10; seed::Integer=-1)
Use the flow cutter algorithm to find a tree decomposition for the graph `G` with minimal
treewidth.
Run the flow cutter algorithm for `time` seconds to find a tree decomposition for the graph
`G` with minimal treewidth.
The tree decomposition is returned in a dictionary with the following key value pairs:
- `:treewidth` => The treewidth of the tree decomposotion,
Expand All @@ -27,9 +27,8 @@ The tree decomposition is returned in a dictionary with the following key value
- `:b_n` => The n-th bag of the decomposition where n is an integer from 1 to the number of
bags in the decomposition. A bag is an array of vertices of `G`.
- `:edges` => An array of integer pairs indicating the edges of the tree decomposition.
If a tree decomposition is not found within the allocated time then an empty dictionary is
returned.
- `:comments` => An array of comments created by flow cutter regarding heuristics used and if
it had enough time to find a decomposition.
The flow cutter algorithm and how it is used to find tree decompositions is described by
Michael Hamann and Ben Strasser in the following papers:
Expand All @@ -38,9 +37,10 @@ Graph Bisection with Pareto Optimization - https://dl.acm.org/doi/10.1145/317304
Computing Tree Decompositions with FlowCutter - https://arxiv.org/abs/1709.08949
# Keywords
- `time::Integer=10`: The number of seconds to run flow cutter for.
- `seed::Integer=-1`: The seed used by flow cutter. Most be a non negative integer,
otherwise the seed is set by flow cutter.
"""
function flow_cutter(G::lg.AbstractGraph; time::Integer=10)
function flow_cutter(G::lg.AbstractGraph, time::Integer=10; seed::Integer=-1)
# Create a temporary directory with the input and output files for flow cutter.
out = Pipe()
flow_cutter_dir = dirname(FlowCutterPACE17_jll.flow_cutter_pace17_path)
Expand All @@ -51,28 +51,32 @@ function flow_cutter(G::lg.AbstractGraph; time::Integer=10)

# Call and run flow cutter for the specified duration of time.
flow_cutter_pace17() do exe
flow_cutter_cmd = [exe, graph_file]
flow_cutter_cmd = [exe]
seed >= 0 && append!(flow_cutter_cmd, ["-s", string(0)])
flow_cutter_cmd = Cmd(flow_cutter_cmd)
flow_cutter_proc = run(pipeline(flow_cutter_cmd, td_file); wait=false)
flow_cutter_proc = run(pipeline(graph_file, flow_cutter_cmd, td_file); wait=false)
while !process_running(flow_cutter_proc) # Wait until process has started.
sleep(1)
end
# TODO: Would be preferable to measure the execution time of the flow cutter
# process here.
sleep(time)
kill(flow_cutter_proc)
end
flow_cutter_output = readlines(td_file)

# Read the output of flow cutter into a dictionary to be returned to the user.
td = Dict{Symbol, Any}()
length(flow_cutter_output) > 0 && (td[:edges] = Tuple{Int, Int}[])
td[:edges] = Tuple{Int, Int}[]
td[:comments] = String[]
for line in flow_cutter_output
words = split(line)
if words[1] == "c"
continue
push!(td[:comments], line)
elseif words[1] == "s"
td[:num_bags] = parse(Int, words[3])
td[:treewidth] = parse(Int, words[4]) - 1
td[:num_vertices] = parse(Int, words[4])
td[:num_vertices] = parse(Int, words[5])
elseif words[1] == "b"
td[Symbol("b_"*words[2])] = parse.(Int, words[3:end])
else
Expand All @@ -83,7 +87,7 @@ function flow_cutter(G::lg.AbstractGraph; time::Integer=10)
end
end

flow_cutter(G::LabeledGraph; kwargs...) = flow_cutter(G.graph; kwargs...)
flow_cutter(G::LabeledGraph, time::Integer=10; kwargs...) = flow_cutter(G.graph, time; kwargs...)



Expand Down
2 changes: 1 addition & 1 deletion test/elimination_order_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

# Get a tree decomposition of the square lattice graph and test converting it to an
# elimination order.
tree_decomp = flow_cutter(G; time=15)
tree_decomp = flow_cutter(G, 15)
td_order = order_from_tree_decomposition(tree_decomp)
@test find_treewidth_from_order(G, td_order) == tree_decomp[:treewidth]
@test length(td_order) == N*N
Expand Down
9 changes: 3 additions & 6 deletions test/flow_cutter_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

# Check if the treewidth and number of bags is correct and check that the tree
# decomposition has no edges.
tree_decomp = flow_cutter(G; time=20)
tree_decomp = flow_cutter(G, 30; seed=42)
@test tree_decomp[:treewidth] == 9
@test tree_decomp[:num_bags] == 1
@test length(tree_decomp[:edges]) == 0
Expand All @@ -32,12 +32,9 @@
end

# Check if the treewidth, number of bags and number of edges is correct.
tree_decomp = flow_cutter(G; time=20)
tree_decomp = flow_cutter(G, 30)
@test tree_decomp[:treewidth] == N-1
@test tree_decomp[:num_bags] == 2
@test tree_decomp[:num_vertices] == N + n
@test length(tree_decomp[:edges]) == 1

# Check that an empty dictionary is returned if no tree decomposition is found.
tree_decomp = flow_cutter(G; time=0)
@test isempty(tree_decomp)
end

0 comments on commit a030c09

Please sign in to comment.