Skip to content

Commit

Permalink
Renaming the Branching method to MinhThiTrick (#48)
Browse files Browse the repository at this point in the history
* update

* update

* update deps

* rename Branching to MinhThi Trick

* fix ci
  • Loading branch information
GiggleLiu authored Jun 24, 2024
1 parent 72fb1a6 commit fea6117
Show file tree
Hide file tree
Showing 19 changed files with 195 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ jobs:
run: julia -e 'using Pkg;
Pkg.add("PlutoUI");
Pkg.activate("notebooks");
Pkg.instantiate();
Pkg.develop(path=".");
Pkg.instantiate();
using PlutoSliderServer;
for file in readdir("notebooks")
endswith(file, ".jl") && PlutoSliderServer.export_notebook(joinpath("notebooks", file))
Expand Down
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ LuxorGraphPlot = "1f49bdf2-22a7-4bc4-978b-948dc219fbbc"

[compat]
Graphs = "1.6"
LuxorGraphPlot = "0.3"
LuxorGraphPlot = "0.5"
julia = "1"

[extras]
GenericTensorNetworks = "3521c873-ad32-4bb4-b63d-f4f178f42b49"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Random", "GenericTensorNetworks"]
test = ["Test", "Random", "GenericTensorNetworks", "LinearAlgebra"]
11 changes: 11 additions & 0 deletions notebooks/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,14 @@ PlutoSliderServer = "2fc8631c-6f24-4c5b-bca7-cbb509c42db4"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
UnitDiskMapping = "1b61a8d9-79ed-4491-8266-ef37f39e1727"

[compat]
GenericTensorNetworks = "2"
Graphs = "1.6"
LinearAlgebra = "1"
LuxorGraphPlot = "0.5"
PlutoSliderServer = "0.3"
PlutoUI = "0.7"
Revise = "3"
UnitDiskMapping = "0.3"
julia = "1"
39 changes: 21 additions & 18 deletions notebooks/tutorial.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### A Pluto.jl notebook ###
# v0.19.32
# v0.19.42

using Markdown
using InteractiveUtils
Expand Down Expand Up @@ -46,7 +46,7 @@ using UnitDiskMapping, Graphs, GenericTensorNetworks, LinearAlgebra
# ╔═╡ 98459516-4833-4e4a-916f-d5ea3e657ceb
# Visualization setup.
# To make the plots dark-mode friendly, we use white-background color.
using UnitDiskMapping.LuxorGraphPlot.Luxor, LuxorGraphPlot; GraphDisplayConfig.unit[] = 25; GraphDisplayConfig.background_color[]="white";
using UnitDiskMapping.LuxorGraphPlot.Luxor, LuxorGraphPlot

# ╔═╡ eac6ceda-f5d4-11ec-23db-b7b4d00eaddf
md"# Unit Disk Mapping"
Expand All @@ -64,16 +64,16 @@ md"Let the source graph be the Petersen graph."
graph = smallgraph(:petersen)

# ╔═╡ 0302be92-076a-4ebe-8d6d-4b352a77cfce
LuxorGraphPlot.show_graph(graph; unit=50)
LuxorGraphPlot.show_graph(graph)

# ╔═╡ 417b18f6-6a8f-45fb-b979-6ec9d12c6246
md"We can use the `map_graph` function to map the unweighted MIS problem on the Petersen graph to one on a defected King's graph."

# ╔═╡ c7315578-8bb0-40a0-a2a3-685a80674c9c
unweighted_res = map_graph(graph; vertex_order=Branching());
unweighted_res = map_graph(graph; vertex_order=MinhThiTrick());

# ╔═╡ 3f605eac-f587-40b2-8fac-8223777d3fad
md"Here, the keyword argument `vertex_order` can be a vector of vertices in a specified order, or the method to compute the path decomposition that generates an order. The `Branching()` method is an exact path decomposition solver, which is suited for small graphs (where number of vertices <= 50). The `Greedy()` method finds the vertex order much faster and works in all cases, but may not be optimal.
md"Here, the keyword argument `vertex_order` can be a vector of vertices in a specified order, or the method to compute the path decomposition that generates an order. The `MinhThiTrick()` method is an exact path decomposition solver, which is suited for small graphs (where number of vertices <= 50). The `Greedy()` method finds the vertex order much faster and works in all cases, but may not be optimal.
A good vertex order can reduce the depth of the mapped graph."

# ╔═╡ e5382b61-6387-49b5-bae8-0389fbc92153
Expand All @@ -86,9 +86,11 @@ fieldnames(unweighted_res |> typeof)
md"The field `grid_graph` is the mapped grid graph."

# ╔═╡ 520fbc23-927c-4328-8dc6-5b98853fb90d
# `unit` is the number of pixels per unit distance
LuxorGraphPlot.show_graph(unweighted_res.grid_graph)

# ╔═╡ af162d39-2da9-4a06-9cde-8306e811ba7a
unweighted_res.grid_graph.size

# ╔═╡ 96ca41c0-ac77-404c-ada3-0cdc4a426e44
md"The field `lines` is a vector of copy gadgets arranged in a `⊢` shape. These copy gadgets form a *crossing lattice*, in which two copy lines cross each other whenever their corresponding vertices in the source graph are connected by an edge.
```
Expand Down Expand Up @@ -121,7 +123,7 @@ unweighted_res.mis_overhead
md"We can solve the mapped graph with [`GenericTensorNetworks`](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/)."

# ╔═╡ f084b98b-097d-4b33-a0d3-0d0a981f735e
res = solve(IndependentSet(SimpleGraph(unweighted_res.grid_graph)), SingleConfigMax())[]
res = solve(GenericTensorNetwork(IndependentSet(SimpleGraph(unweighted_res.grid_graph))), SingleConfigMax())[]

# ╔═╡ 86457b4e-b83e-4bf5-9d82-b5e14c055b4b
md"You might want to read [the documentation page of `GenericTensorNetworks`](https://queracomputing.github.io/GenericTensorNetworks.jl/dev/) for a detailed explanation on this function. Here, we just visually check the solution configuration."
Expand All @@ -148,7 +150,7 @@ md"## Generic Weighted Mapping"
md"A Maximum Weight Independent Set (MWIS) problem on a general graph can be mapped to one on the defected King's graph. The first step is to do the same mapping as above but adding a new positional argument `Weighted()` as the first argument of `map_graph`. Let us still use the Petersen graph as an example."

# ╔═╡ 2fa704ee-d5c1-4205-9a6a-34ba0195fecf
weighted_res = map_graph(Weighted(), graph; vertex_order=Branching());
weighted_res = map_graph(Weighted(), graph; vertex_order=MinhThiTrick());

# ╔═╡ 27acc8be-2db8-4322-85b4-230fdddac043
md"The return value is similar to that for the unweighted mapping generated above, except each node in the mapped graph can have a weight 1, 2 or 3. Note here, we haven't added the weights in the original graph."
Expand Down Expand Up @@ -180,7 +182,7 @@ md"Now that we have both the graph and the weights, let us solve the mapped prob
wmap_config = let
graph, _ = graph_and_weights(weighted_res.grid_graph)
collect(Int,
solve(IndependentSet(graph; weights=mapped_weights), SingleConfigMax())[].c.data
solve(GenericTensorNetwork(IndependentSet(graph, mapped_weights)), SingleConfigMax())[].c.data
)
end

Expand All @@ -197,7 +199,7 @@ map_config_back(weighted_res, wmap_config)
# ╔═╡ beb7c0e5-6221-4f20-9166-2bd56902be1b
# Directly solving the source graph
collect(Int,
solve(IndependentSet(graph; weights=source_weights), SingleConfigMax())[].c.data
solve(GenericTensorNetwork(IndependentSet(graph, source_weights)), SingleConfigMax())[].c.data
)

# ╔═╡ cf7e88cb-432e-4e3a-ae8b-8fa12689e485
Expand Down Expand Up @@ -248,7 +250,7 @@ show_grayscale(qubo.grid_graph)
md"By solving this maximum independent set problem, we will get the following configuration."

# ╔═╡ ef149d9a-6aa9-4f34-b936-201b9d77543c
qubo_mapped_solution = collect(Int, solve(IndependentSet(qubo_graph; weights=qubo_weights), SingleConfigMax())[].c.data)
qubo_mapped_solution = collect(Int, solve(GenericTensorNetwork(IndependentSet(qubo_graph, qubo_weights)), SingleConfigMax())[].c.data)

# ╔═╡ 4ea4f26e-746d-488e-9968-9fc584c04bcf
show_config(qubo.grid_graph, qubo_mapped_solution)
Expand All @@ -264,8 +266,8 @@ map_config_back(qubo, collect(Int, qubo_mapped_solution))
md"This solution is consistent with the exact solution:"

# ╔═╡ 7dd900fc-9531-4bd6-8b6d-3aac3d5a2386
# Directly solving the source graph
collect(Int, solve(SpinGlass(J, h), SingleConfigMax())[].c.data)
# Directly solving the source graph, due to the convention issue, we flip the signs of `J` and `h`
collect(Int, solve(GenericTensorNetwork(spin_glass_from_matrix(-J, -h)), SingleConfigMax())[].c.data)

# ╔═╡ 13f952ce-642a-4396-b574-00ea6584008c
md"### QUBO problem on a square lattice"
Expand Down Expand Up @@ -308,7 +310,7 @@ md"Let us solve the independent set problem on the mapped graph."
square_graph, square_weights = UnitDiskMapping.graph_and_weights(qubo_square.grid_graph);

# ╔═╡ 5c25abb7-e3ee-4104-9a82-eb4aa4e773d2
config_square = collect(Int, solve(IndependentSet(square_graph; weights=square_weights), SingleConfigMax())[].c.data);
config_square = collect(Int, solve(GenericTensorNetwork(IndependentSet(square_graph, square_weights)), SingleConfigMax())[].c.data);

# ╔═╡ 4cec7232-8fbc-4ac1-96bb-6c7fea5fe117
md"We will get the following configuration."
Expand Down Expand Up @@ -343,7 +345,7 @@ let
for (i,j,h) in square_onsite
hs[i+(j-1)*n] = h
end
collect(Int, solve(SpinGlass(g2; J=Js, h=hs), SingleConfigMax())[].c.data)
collect(Int, solve(GenericTensorNetwork(SpinGlass(g2, -Js, -hs)), SingleConfigMax())[].c.data)
end

# ╔═╡ 9db831d6-7f10-47be-93d3-ebc892c4b3f2
Expand Down Expand Up @@ -386,7 +388,7 @@ md"To solve this factoring problem, one can use the following statement:"

# ╔═╡ e5da7214-0e69-4b5a-a65e-ed92d0616c71
multiplier_output = UnitDiskMapping.solve_factoring(mres, 6) do g, ws
collect(Int, solve(IndependentSet(g; weights=ws), SingleConfigMax())[].c.data)
collect(Int, solve(GenericTensorNetwork(IndependentSet(g, ws)), SingleConfigMax())[].c.data)
end

# ╔═╡ 9dc01591-5c37-4d83-b640-83280513941e
Expand Down Expand Up @@ -416,7 +418,7 @@ md"2. Then, we solve this new grid graph."
# ╔═╡ 57f7e085-9589-4a6c-ac14-488ea9924692
config_factoring6 = let
mg, mw = graph_and_weights(mapped_grid_graph)
solve(IndependentSet(mg; weights=mw), SingleConfigMax())[].c.data
solve(GenericTensorNetwork(IndependentSet(mg, mw)), SingleConfigMax())[].c.data
end;

# ╔═╡ 4c7d72f1-688a-4a70-8ce6-a4801127bb9a
Expand All @@ -440,7 +442,7 @@ md"## Logic Gates"
md"Let us define a helper function for visualization."

# ╔═╡ c17bca17-a00a-4118-a212-d21da09af9b5
parallel_show(gate) = leftright(show_pins(Gate(gate); format=:png, unit=80), show_grayscale(gate_gadget(Gate(gate))[1]; format=:png, unit=80, wmax=2));
parallel_show(gate) = leftright(show_pins(Gate(gate)), show_grayscale(gate_gadget(Gate(gate))[1], wmax=2));

# ╔═╡ 6aee2288-1934-4fc5-9a9c-f45b7ce4e767
md"1. NOT gate: ``y_1 =\neg x_1``"
Expand Down Expand Up @@ -485,6 +487,7 @@ md"Since most logic gates have 3 pins, it is natural to embed a circuit to a 3D
# ╠═ae5c8359-6bdb-4a2a-8b54-cd2c7d2af4bd
# ╟─56bdcaa6-c8b9-47de-95d4-6e95204af0f2
# ╠═520fbc23-927c-4328-8dc6-5b98853fb90d
# ╠═af162d39-2da9-4a06-9cde-8306e811ba7a
# ╟─96ca41c0-ac77-404c-ada3-0cdc4a426e44
# ╠═5dfa8a74-26a5-45c4-a80c-47ba4a6a4ae9
# ╟─a64c2094-9a51-4c45-b9d1-41693c89a212
Expand Down
18 changes: 9 additions & 9 deletions notebooks/unweighted.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
### A Pluto.jl notebook ###
# v0.19.32
# v0.19.42

using Markdown
using InteractiveUtils
Expand Down Expand Up @@ -82,11 +82,11 @@ show_graph(g5)
# ╔═╡ 625bdcf4-e37e-4bb8-bd1a-907cdcc5fe24
md"""
#### Step 2: Map the source graph to an unweighted King's subgraph (KSG)
The vertex order is optimized with the Branching path decomposition algorithm
The vertex order is optimized with the Branching path decomposition algorithm (MinhThi's Trick)
"""

# ╔═╡ f9e57a6b-1186-407e-a8b1-cb8f31a17bd2
g5res = UnitDiskMapping.map_graph(g5; vertex_order=Branching())
g5res = UnitDiskMapping.map_graph(g5; vertex_order=MinhThiTrick())

# ╔═╡ e64e7ca4-b297-4c74-8699-bec4b4fbb843
md"Visualize the mapped KSG graph in terminal"
Expand All @@ -107,7 +107,7 @@ md"#### Step 3: Solve the MIS size of the mapped graph"
md"The independent set size can be obtained by solving the `SizeMax()` property using the [generic tensor network](https://github.com/QuEraComputing/GenericTensorNetworks.jl) method."

# ╔═╡ 67fd2dd2-5add-4402-9618-c9b7c7bfe95b
missize_g5_ksg = solve(IndependentSet(SimpleGraph(g5res.grid_graph)), SizeMax())[]
missize_g5_ksg = solve(GenericTensorNetwork(IndependentSet(SimpleGraph(g5res.grid_graph))), SizeMax())[]

# ╔═╡ aaee9dbc-5b9c-41b1-b0d4-35d2cac7c773
md"The predicted MIS size for the source graph is:"
Expand All @@ -121,7 +121,7 @@ One of the best solutions can be obtained by solving the `SingleConfigMax()` pro
"""

# ╔═╡ 0142f661-0855-45b4-852a-78f560e98c67
mis_g5_ksg = solve(IndependentSet(SimpleGraph(g5res.grid_graph)), SingleConfigMax())[].c.data
mis_g5_ksg = solve(GenericTensorNetwork(IndependentSet(SimpleGraph(g5res.grid_graph))), SingleConfigMax())[].c.data

# ╔═╡ fa046f3c-fd7d-4e91-b3f5-fc4591d3cae2
md"Plot the solution"
Expand Down Expand Up @@ -153,7 +153,7 @@ UnitDiskMapping.is_independent_set(g5, mis_g5)
count(isone, mis_g5)

# ╔═╡ 5621bb2a-b1c6-4f0d-921e-980b2ce849d5
solve(IndependentSet(g5), SizeMax())[].n
solve(GenericTensorNetwork(IndependentSet(g5)), SizeMax())[].n

# ╔═╡ 1fe6c679-2962-4c1b-8b12-4ceb77ed9e0f
md"""
Expand All @@ -178,13 +178,13 @@ petersen_res = UnitDiskMapping.map_graph(petersen)
md"The MIS size of the petersen graph is 4."

# ╔═╡ bf97a268-cd96-4dbc-83c6-10eb1b03ddcc
missize_petersen = solve(IndependentSet(petersen), SizeMax())[]
missize_petersen = solve(GenericTensorNetwork(IndependentSet(petersen)), SizeMax())[]

# ╔═╡ 2589f112-5de5-4c98-bcd1-138b6143cd30
md" The MIS size of the mapped KSG graph is much larger"

# ╔═╡ 1b946455-b152-4d6f-9968-7dc6e22d171a
missize_petersen_ksg = solve(IndependentSet(SimpleGraph(petersen_res.grid_graph)), SizeMax())[]
missize_petersen_ksg = solve(GenericTensorNetwork(IndependentSet(SimpleGraph(petersen_res.grid_graph))), SizeMax())[]

# ╔═╡ 4e7f7d9e-fae4-46d2-b95d-110d36b691d9
md"The difference in the MIS size is:"
Expand All @@ -196,7 +196,7 @@ petersen_res.mis_overhead
md"Find an MIS of the mapped KSG and map it back an MIS on the source graph."

# ╔═╡ 0d08cb1a-f7f3-4d63-bd70-78103db086b3
mis_petersen_ksg = solve(IndependentSet(SimpleGraph(petersen_res.grid_graph)), SingleConfigMax())[].c.data
mis_petersen_ksg = solve(GenericTensorNetwork(IndependentSet(SimpleGraph(petersen_res.grid_graph))), SingleConfigMax())[].c.data

# ╔═╡ c27d8aed-c81f-4eb7-85bf-a4ed88c2537f
mis_petersen = UnitDiskMapping.map_config_back(petersen_res, collect(mis_petersen_ksg))
Expand Down
4 changes: 3 additions & 1 deletion src/UnitDiskMapping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export @gg
export is_independent_set, unitdisk_graph

# path decomposition
export pathwidth, PathDecompositionMethod, Branching, Greedy
export pathwidth, PathDecompositionMethod, MinhThiTrick, Greedy

@deprecate Branching MinhThiTrick

include("utils.jl")
include("Core.jl")
Expand Down
16 changes: 8 additions & 8 deletions src/mapping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,16 +318,16 @@ function crossat(ug::MappingGrid, v, w)
end

"""
embed_graph([mode,] g::SimpleGraph; vertex_order=Branching())
embed_graph([mode,] g::SimpleGraph; vertex_order=MinhThiTrick())
Embed graph `g` into a unit disk grid, where the optional argument `mode` can be `Weighted()` or `UnWeighted`.
The `vertex_order` can be a vector or one of the following inputs
* `Greedy()` fast but non-optimal.
* `Branching()` slow but optimal.
* `MinhThiTrick()` slow but optimal.
"""
embed_graph(g::SimpleGraph; vertex_order=Branching()) = embed_graph(UnWeighted(), g; vertex_order)
function embed_graph(mode, g::SimpleGraph; vertex_order=Branching())
embed_graph(g::SimpleGraph; vertex_order=MinhThiTrick()) = embed_graph(UnWeighted(), g; vertex_order)
function embed_graph(mode, g::SimpleGraph; vertex_order=MinhThiTrick())
if vertex_order isa AbstractVector
L = PathDecomposition.Layout(g, collect(vertex_order[end:-1:1]))
else
Expand Down Expand Up @@ -368,7 +368,7 @@ struct MappingResult{NT}
end

"""
map_graph([mode=Weighted(),] g::SimpleGraph; vertex_order=Branching(), ruleset=[...])
map_graph([mode=UnWeighted(),] g::SimpleGraph; vertex_order=MinhThiTrick(), ruleset=[...])
Map a graph to a unit disk grid graph that being "equivalent" to the original graph, and return a `MappingResult` instance.
Here "equivalent" means a maximum independent set in the grid graph can be mapped back to
Expand All @@ -385,13 +385,13 @@ Keyword Arguments
Different vertex orders have different path width, i.e. different depth of mapped grid graph.
It can be a vector or one of the following inputs
* `Greedy()` fast but not optimal.
* `Branching()` slow but optimal.
* `MinhThiTrick()` slow but optimal.
* `ruleset` specifies and extra set of optimization patterns (not the crossing patterns).
"""
function map_graph(g::SimpleGraph; vertex_order=Branching(), ruleset=default_simplifier_ruleset(UnWeighted()))
function map_graph(g::SimpleGraph; vertex_order=MinhThiTrick(), ruleset=default_simplifier_ruleset(UnWeighted()))
map_graph(UnWeighted(), g; ruleset=ruleset, vertex_order=vertex_order)
end
function map_graph(mode, g::SimpleGraph; vertex_order=Branching(), ruleset=default_simplifier_ruleset(mode))
function map_graph(mode, g::SimpleGraph; vertex_order=MinhThiTrick(), ruleset=default_simplifier_ruleset(mode))
ug = embed_graph(mode, g; vertex_order=vertex_order)
mis_overhead0 = mis_overhead_copylines(ug)
ug, tape = apply_crossing_gadgets!(mode, ug)
Expand Down
23 changes: 19 additions & 4 deletions src/pathdecomposition/pathdecomposition.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module PathDecomposition
using Graphs

export pathwidth, PathDecompositionMethod, Branching, Greedy
export pathwidth, PathDecompositionMethod, MinhThiTrick, Greedy

struct Layout{T}
vertices::Vector{T}
Expand Down Expand Up @@ -78,7 +78,22 @@ Graphs.vertices(layout::Layout) = layout.vertices
##### Interfaces #####
abstract type PathDecompositionMethod end

struct Branching <: PathDecompositionMethod end
"""
MinhThiTrick <: PathDecompositionMethod
A path decomposition method based on the Branching method.
In memory of Minh-Thi Nguyen, one of the main developers of this method.
She left us in a truck accident at her 24 years old.
- https://www.cbsnews.com/boston/news/cyclist-killed-minh-thi-nguyen-cambridge-bike-safety/
"""
struct MinhThiTrick <: PathDecompositionMethod end

"""
Greedy <: PathDecompositionMethod
A path decomposition method based on the Greedy method.
"""
Base.@kwdef struct Greedy <: PathDecompositionMethod
nrepeat::Int = 10
end
Expand All @@ -90,9 +105,9 @@ Compute the optimal path decomposition of graph `g`, returns a `Layout` instance
`method` can be
* Greedy(; nrepeat=10)
* Branching
* MinhThiTrick
"""
function pathwidth(g::AbstractGraph, ::Branching)
function pathwidth(g::AbstractGraph, ::MinhThiTrick)
return branch_and_bound(g)
end

Expand Down
Loading

0 comments on commit fea6117

Please sign in to comment.