From 523546d005855c0e0053515639656136e632e782 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Tue, 26 Nov 2024 12:22:51 +0400 Subject: [PATCH 1/5] update --- Project.toml | 1 + src/UnitDiskMapping.jl | 3 +++ src/reduceto.jl | 26 ++++++++++++++++++++++++++ test/reduceto.jl | 29 +++++++++++++++++++++++++++++ test/runtests.jl | 4 ++++ 5 files changed, 63 insertions(+) create mode 100644 src/reduceto.jl create mode 100644 test/reduceto.jl diff --git a/Project.toml b/Project.toml index dfe5a96..fd35266 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.4.0" [deps] Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" LuxorGraphPlot = "1f49bdf2-22a7-4bc4-978b-948dc219fbbc" +ProblemReductions = "899c297d-f7d2-4ebf-8815-a35996def416" [compat] Graphs = "1.6" diff --git a/src/UnitDiskMapping.jl b/src/UnitDiskMapping.jl index 628f921..ba1d0fe 100644 --- a/src/UnitDiskMapping.jl +++ b/src/UnitDiskMapping.jl @@ -5,6 +5,8 @@ module UnitDiskMapping using Graphs using LuxorGraphPlot using LuxorGraphPlot.Luxor.Colors +import ProblemReductions: reduceto, target_problem, extract_multiple_solutions +import ProblemReductions # Basic types export UnWeighted, Weighted @@ -53,6 +55,7 @@ include("mapping.jl") include("weighted.jl") include("simplifiers.jl") include("extracting_results.jl") +include("reduceto.jl") include("visualize.jl") end diff --git a/src/reduceto.jl b/src/reduceto.jl new file mode 100644 index 0000000..722d236 --- /dev/null +++ b/src/reduceto.jl @@ -0,0 +1,26 @@ +struct IndependentSetKSGResult{NT, VT} <: ProblemReductions.AbstractReductionResult + mapres::MappingResult{NT} + weights::VT +end + +# unweighted reduction +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph) + return IndependentSetKSGResult(map_graph(UnWeighted(), problem.graph), problem.weights) +end + +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) + return IndependentSetKSGResult(map_graph(Weighted(), problem.graph), problem.weights) +end + +function ProblemReductions.target_problem(res::IndependentSetKSGResult{<:UnWeightedNode}) + return ProblemReductions.IndependentSet(SimpleGraph(res.mapres.grid_graph)) +end +function ProblemReductions.target_problem(res::IndependentSetKSGResult{<:WeightedNode}) + graph, _ = graph_and_weights(res.mapres.grid_graph) + weights = UnitDiskMapping.map_weights(res.mapres, res.weights) + return ProblemReductions.IndependentSet(graph, weights) +end + +function ProblemReductions.extract_solution(res::IndependentSetKSGResult, sol) + return map_config_back(res.mapres, sol) +end \ No newline at end of file diff --git a/test/reduceto.jl b/test/reduceto.jl new file mode 100644 index 0000000..d425d54 --- /dev/null +++ b/test/reduceto.jl @@ -0,0 +1,29 @@ +using Test, UnitDiskMapping, ProblemReductions, Graphs + +@testset "rules" begin + graph = complete_graph(3) # triangle + is = IndependentSet(graph) + wis = IndependentSet(graph, rand(nv(graph)) .* 0.2) + for (source, target_type) in [ + # please add more tests here + is => IndependentSet{ProblemReductions.GridGraph, Int, UnitWeight}, + wis => IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}}, + ] + @info "Testing reduction from $(typeof(source)) to $(target_type)" + # directly solve + best_source = findbest(source, BruteForce()) + + # reduce and solve + result = reduceto(target_type, source) + target = target_problem(result) + best_target = findbest(target, BruteForce()) + + # extract the solution + best_source_extracted_single = unique( extract_solution.(Ref(result), best_target) ) + best_source_extracted_multiple = extract_multiple_solutions(result, best_target) + + # check if the solutions are the same + @test best_source_extracted_single ⊆ best_source + @test Set(best_source_extracted_multiple) == Set(best_source) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index b983e25..4caa69e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -48,3 +48,7 @@ end @testset "visualize" begin include("visualize.jl") end + +@testset "reduceto" begin + include("reduceto.jl") +end \ No newline at end of file From c59325efb2081b9cc1fe5a921fe2c9c4460b1b7f Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Tue, 26 Nov 2024 14:38:15 +0400 Subject: [PATCH 2/5] port to ProblemReductions.jl, tests all pass --- src/Core.jl | 6 +++++ src/multiplier.jl | 4 ++-- src/reduceto.jl | 56 +++++++++++++++++++++++++++++++++++++---------- test/Core.jl | 15 +++++++++++++ test/reduceto.jl | 27 +++++++++++++---------- test/runtests.jl | 4 ++++ 6 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 test/Core.jl diff --git a/src/Core.jl b/src/Core.jl index 90b8c8d..18049f2 100644 --- a/src/Core.jl +++ b/src/Core.jl @@ -76,6 +76,12 @@ function Graphs.SimpleGraph(grid::GridGraph{Node{ONE}}) return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius) end coordinates(grid::GridGraph) = getfield.(grid.nodes, :loc) +function Graphs.neighbors(g::GridGraph, i::Int) + [j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j]) <= g.radius] +end +distance(n1::Node, n2::Node) = sqrt(sum(abs2, n1.loc .- n2.loc)) +Graphs.nv(g::GridGraph) = length(g.nodes) +Graphs.vertices(g::GridGraph) = 1:nv(g) # printing function for Grid graphs function print_grid(io::IO, grid::GridGraph{Node{WT}}; show_weight=false) where WT diff --git a/src/multiplier.jl b/src/multiplier.jl index dfd60d1..f1814f7 100644 --- a/src/multiplier.jl +++ b/src/multiplier.jl @@ -97,7 +97,7 @@ function map_factoring(M::Int, N::Int) [pinloc(1, j, 1) for j=1:N]..., [pinloc(i, N, 4) for i=1:M]..., ] - return FactoringResult(gg, pp, pq, pm, p0) + return FactoringResult(gg, pq, pp, pm, p0) end struct FactoringResult{NT} @@ -131,7 +131,7 @@ function solve_factoring(missolver, mres::FactoringResult, target::Int) return map_config_back(mres, cfg) end -function set_target(g::SimpleGraph, pins::AbstractVector, target::Int) +function set_target(g, pins::AbstractVector, target::Int) vs = collect(vertices(g)) for (i, p) in enumerate(pins) bitval = (target >> (i-1)) & 1 diff --git a/src/reduceto.jl b/src/reduceto.jl index 722d236..115af33 100644 --- a/src/reduceto.jl +++ b/src/reduceto.jl @@ -1,26 +1,60 @@ -struct IndependentSetKSGResult{NT, VT} <: ProblemReductions.AbstractReductionResult +struct IndependentSetToKSG{NT, VT} <: ProblemReductions.AbstractReductionResult mapres::MappingResult{NT} weights::VT end +function _to_gridgraph(g::UnitDiskMapping.GridGraph) + return ProblemReductions.GridGraph(g.size, getfield.(g.nodes, :loc), g.radius) +end +function _extract_weights(g::UnitDiskMapping.GridGraph{<:WeightedNode}) + getfield.(g.nodes, :weight) +end -# unweighted reduction +###### unweighted reduction function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph) - return IndependentSetKSGResult(map_graph(UnWeighted(), problem.graph), problem.weights) + return IndependentSetToKSG(map_graph(UnWeighted(), problem.graph), problem.weights) end -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) - return IndependentSetKSGResult(map_graph(Weighted(), problem.graph), problem.weights) +function ProblemReductions.target_problem(res::IndependentSetToKSG{<:UnWeightedNode}) + return ProblemReductions.IndependentSet(_to_gridgraph(res.mapres.grid_graph)) +end +function ProblemReductions.extract_solution(res::IndependentSetToKSG, sol) + return map_config_back(res.mapres, sol) end -function ProblemReductions.target_problem(res::IndependentSetKSGResult{<:UnWeightedNode}) - return ProblemReductions.IndependentSet(SimpleGraph(res.mapres.grid_graph)) +###### Weighted reduction +# TODO: rescale the weights to avoid errors +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) + return IndependentSetToKSG(map_graph(Weighted(), problem.graph), problem.weights) end -function ProblemReductions.target_problem(res::IndependentSetKSGResult{<:WeightedNode}) - graph, _ = graph_and_weights(res.mapres.grid_graph) +function ProblemReductions.target_problem(res::IndependentSetToKSG{<:WeightedNode}) + graph = _to_gridgraph(res.mapres.grid_graph) weights = UnitDiskMapping.map_weights(res.mapres, res.weights) return ProblemReductions.IndependentSet(graph, weights) end -function ProblemReductions.extract_solution(res::IndependentSetKSGResult, sol) - return map_config_back(res.mapres, sol) +###### Factoring ###### +struct FactoringToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult + mapres::FactoringResult{NT} + raw_graph::ProblemReductions.GridGraph + raw_weight::Vector{Int} + vmap::Vector{Int} + problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}} +end +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.Factoring) + mres = map_factoring(problem.m, problem.n) + g = _to_gridgraph(mres.grid_graph) + ws = getfield.(mres.grid_graph.nodes, :weight) + mg, vmap = set_target(g, [mres.pins_zeros..., mres.pins_output...], problem.input << length(mres.pins_zeros)) + return FactoringToIndependentSet(mres, g, ws, vmap, ProblemReductions.IndependentSet(mg, ws[vmap])) +end + +function ProblemReductions.target_problem(res::FactoringToIndependentSet) + return res.problem +end + +function ProblemReductions.extract_solution(res::FactoringToIndependentSet, sol) + cfg = zeros(Int, nv(res.raw_graph)) + cfg[res.vmap] .= sol + i1, i2 = map_config_back(res.mapres, cfg) + return vcat([i1>>(k-1) & 1 for k=1:length(res.mapres.pins_input1)], [i2>>(k-1) & 1 for k=1:length(res.mapres.pins_input2)]) end \ No newline at end of file diff --git a/test/Core.jl b/test/Core.jl new file mode 100644 index 0000000..b1376a3 --- /dev/null +++ b/test/Core.jl @@ -0,0 +1,15 @@ +using Test, UnitDiskMapping, Graphs + +@testset "GridGraph" begin + grid = GridGraph((5, 5), [Node(2, 3), Node(2, 4), Node(5, 5)], 1.2) + g = SimpleGraph(grid) + @test ne(g) == 1 + @test vertices(grid) == vertices(g) + @test neighbors(grid, 2) == neighbors(g, 2) + + grid = GridGraph((5, 5), [Node(2, 3), Node(2, 4), Node(5, 5)], 4.0) + g = SimpleGraph(grid) + @test ne(g) == 3 + @test vertices(grid) == vertices(g) + @test neighbors(grid, 2) == neighbors(g, 2) +end \ No newline at end of file diff --git a/test/reduceto.jl b/test/reduceto.jl index d425d54..9ed393d 100644 --- a/test/reduceto.jl +++ b/test/reduceto.jl @@ -1,26 +1,31 @@ -using Test, UnitDiskMapping, ProblemReductions, Graphs +using Test, UnitDiskMapping, Graphs, GenericTensorNetworks +import ProblemReductions @testset "rules" begin graph = complete_graph(3) # triangle - is = IndependentSet(graph) - wis = IndependentSet(graph, rand(nv(graph)) .* 0.2) + fact = ProblemReductions.Factoring(2, 1, 2) + is = ProblemReductions.IndependentSet(graph) + wis = ProblemReductions.IndependentSet(graph, rand(nv(graph)) .* 0.2) for (source, target_type) in [ # please add more tests here - is => IndependentSet{ProblemReductions.GridGraph, Int, UnitWeight}, - wis => IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}}, + is => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, ProblemReductions.UnitWeight}, + wis => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Float64, Vector{Float64}}, + fact => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}}, ] @info "Testing reduction from $(typeof(source)) to $(target_type)" # directly solve - best_source = findbest(source, BruteForce()) + best_source = ProblemReductions.findbest(source, ProblemReductions.BruteForce()) # reduce and solve - result = reduceto(target_type, source) - target = target_problem(result) - best_target = findbest(target, BruteForce()) + result = ProblemReductions.reduceto(target_type, source) + target = ProblemReductions.target_problem(result) + @test target isa target_type + #best_target = findbest(target, BruteForce()) + best_target = GenericTensorNetworks.solve(GenericTensorNetwork(GenericTensorNetworks.IndependentSet(SimpleGraph(target.graph), collect(target.weights))), ConfigsMax())[].c.data # extract the solution - best_source_extracted_single = unique( extract_solution.(Ref(result), best_target) ) - best_source_extracted_multiple = extract_multiple_solutions(result, best_target) + best_source_extracted_single = unique( ProblemReductions.extract_solution.(Ref(result), best_target) ) + best_source_extracted_multiple = ProblemReductions.extract_multiple_solutions(result, best_target) # check if the solutions are the same @test best_source_extracted_single ⊆ best_source diff --git a/test/runtests.jl b/test/runtests.jl index 4caa69e..2350059 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,10 @@ using UnitDiskMapping using Test +@testset "Core" begin + include("Core.jl") +end + @testset "utils" begin include("utils.jl") end From 6db3c3cf5f0bf0ad10245654f0c731f6975aad0c Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 27 Nov 2024 04:18:45 +0100 Subject: [PATCH 3/5] port ProblemReductions --- src/dragondrop.jl | 6 ++--- src/reduceto.jl | 60 +++++++++++++++++++++++++++++++++++++++++------ test/reduceto.jl | 16 +++++++++---- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/dragondrop.jl b/src/dragondrop.jl index 8d9e2ec..66fd3bc 100644 --- a/src/dragondrop.jl +++ b/src/dragondrop.jl @@ -335,7 +335,7 @@ function post_process_grid(grid::Matrix{SimpleCell{T}}, h0, h1) where T return gg, pins end -struct QUBOResult{NT} +struct QUBOResult{NT} <: ProblemReductions.AbstractReductionResult grid_graph::GridGraph{NT} pins::Vector{Int} mis_overhead::Int @@ -353,13 +353,13 @@ function map_config_back(res::WMISResult, cfg) return cfg[res.pins] end -struct RestrictedQUBOResult{NT} +struct RestrictedQUBOResult{NT} <: ProblemReductions.AbstractReductionResult grid_graph::GridGraph{NT} end function map_config_back(res::RestrictedQUBOResult, cfg) end -struct SquareQUBOResult{NT} +struct SquareQUBOResult{NT} <: ProblemReductions.AbstractReductionResult grid_graph::GridGraph{NT} pins::Vector{Int} mis_overhead::Float64 diff --git a/src/reduceto.jl b/src/reduceto.jl index 115af33..5cd1079 100644 --- a/src/reduceto.jl +++ b/src/reduceto.jl @@ -3,14 +3,14 @@ struct IndependentSetToKSG{NT, VT} <: ProblemReductions.AbstractReductionResult weights::VT end function _to_gridgraph(g::UnitDiskMapping.GridGraph) - return ProblemReductions.GridGraph(g.size, getfield.(g.nodes, :loc), g.radius) + return ProblemReductions.GridGraph(getfield.(g.nodes, :loc), g.radius) end function _extract_weights(g::UnitDiskMapping.GridGraph{<:WeightedNode}) getfield.(g.nodes, :weight) end ###### unweighted reduction -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph) return IndependentSetToKSG(map_graph(UnWeighted(), problem.graph), problem.weights) end @@ -23,7 +23,7 @@ end ###### Weighted reduction # TODO: rescale the weights to avoid errors -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) return IndependentSetToKSG(map_graph(Weighted(), problem.graph), problem.weights) end function ProblemReductions.target_problem(res::IndependentSetToKSG{<:WeightedNode}) @@ -35,12 +35,12 @@ end ###### Factoring ###### struct FactoringToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult mapres::FactoringResult{NT} - raw_graph::ProblemReductions.GridGraph + raw_graph::ProblemReductions.GridGraph{2} raw_weight::Vector{Int} vmap::Vector{Int} - problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}} + problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}} end -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph, T, Vector{T}}} where T, problem::ProblemReductions.Factoring) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.Factoring) mres = map_factoring(problem.m, problem.n) g = _to_gridgraph(mres.grid_graph) ws = getfield.(mres.grid_graph.nodes, :weight) @@ -57,4 +57,50 @@ function ProblemReductions.extract_solution(res::FactoringToIndependentSet, sol) cfg[res.vmap] .= sol i1, i2 = map_config_back(res.mapres, cfg) return vcat([i1>>(k-1) & 1 for k=1:length(res.mapres.pins_input1)], [i2>>(k-1) & 1 for k=1:length(res.mapres.pins_input2)]) -end \ No newline at end of file +end + +###### Spinglass problem to MIS on KSG ###### +# NOTE: I am not sure about the correctness of this reduction. If you encounter a bug, please question this function! +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{<:SimpleGraph}) + n = length(problem.h) + M = similar(problem.h, n, n) + for (e, j) in zip(edges(problem.graph), problem.J) + M[e.src, e.dst] = M[e.dst, e.src] = j + end + return map_qubo(M, -problem.h) +end + +function ProblemReductions.target_problem(res::QUBOResult) + grid = _to_gridgraph(res.grid_graph) + ws = getfield.(res.grid_graph.nodes, :weight) + return ProblemReductions.IndependentSet(grid, ws) +end + +function ProblemReductions.extract_solution(res::QUBOResult, sol) + res = map_config_back(res, sol) + return 1 .- 2 .* Int.(res) +end + +###### Spinglass problem on grid to MIS on KSG ###### +# NOTE: the restricted layout is not implemented, since it is not often used +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{ProblemReductions.GridGraph{2}}) + g = problem.graph + @assert 1 <= g.radius < sqrt(2) "Only support nearest neighbor interaction" + coupling = [(g.locations[e.src]..., g.locations[e.dst]..., J) for (e, J) in zip(edges(g), problem.J)] + onsite = [(i, j, -h) for ((i, j), h) in zip(g.locations, problem.h)] + # a vector coupling of `(i, j, i', j', J)`, s.t. (i', j') == (i, j+1) or (i', j') = (i+1, j). + # a vector of onsite term `(i, j, h)`. + return map_qubo_square(coupling, onsite) +end + +function ProblemReductions.target_problem(res::SquareQUBOResult) + grid = _to_gridgraph(res.grid_graph) + ws = getfield.(res.grid_graph.nodes, :weight) + return ProblemReductions.IndependentSet(grid, ws) +end + +function ProblemReductions.extract_solution(res::SquareQUBOResult, sol) + res = map_config_back(res, sol) + return 1 .- 2 .* Int.(res) +end + \ No newline at end of file diff --git a/test/reduceto.jl b/test/reduceto.jl index 9ed393d..2f267be 100644 --- a/test/reduceto.jl +++ b/test/reduceto.jl @@ -6,11 +6,19 @@ import ProblemReductions fact = ProblemReductions.Factoring(2, 1, 2) is = ProblemReductions.IndependentSet(graph) wis = ProblemReductions.IndependentSet(graph, rand(nv(graph)) .* 0.2) + sg = ProblemReductions.SpinGlass(graph, [0.2, 0.4, -0.6], [0.1, 0.1, 0.1]) + sg2 = ProblemReductions.SpinGlass(graph, [0.1, 0.1, -0.1], [0.1, 0.1, 0.1]) + grid = ProblemReductions.GridGraph(ones(Bool, 2, 2), 1.2) + sg_square = ProblemReductions.SpinGlass(grid, [0.1, 0.3, -0.1, 0.4], [0.1, 0.1, 0.1, 0.2]) + sg_square2 = ProblemReductions.SpinGlass(grid, [0.1, -0.3, 0.1, 0.4], [0.1, 0.1, 0.1, 0.2]) for (source, target_type) in [ - # please add more tests here - is => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, ProblemReductions.UnitWeight}, - wis => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Float64, Vector{Float64}}, - fact => ProblemReductions.IndependentSet{ProblemReductions.GridGraph, Int, Vector{Int}}, + sg_square => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}, + sg_square2 => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}, + sg => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}, + sg2 => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}, + is => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, ProblemReductions.UnitWeight}, + wis => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}, + fact => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}}, ] @info "Testing reduction from $(typeof(source)) to $(target_type)" # directly solve From c1a8c68e0b5c53ec43eeefc6d6d7031f410c79e6 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 27 Nov 2024 10:32:54 +0100 Subject: [PATCH 4/5] make problemreductions an extension --- Project.toml | 8 +++- .../ProblemReductionsExt.jl | 48 ++++++++++++------- src/UnitDiskMapping.jl | 3 -- src/dragondrop.jl | 6 +-- 4 files changed, 40 insertions(+), 25 deletions(-) rename src/reduceto.jl => ext/ProblemReductionsExt.jl (76%) diff --git a/Project.toml b/Project.toml index fd35266..c7690b6 100644 --- a/Project.toml +++ b/Project.toml @@ -6,11 +6,17 @@ version = "0.4.0" [deps] Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" LuxorGraphPlot = "1f49bdf2-22a7-4bc4-978b-948dc219fbbc" + +[weakdeps] ProblemReductions = "899c297d-f7d2-4ebf-8815-a35996def416" +[extensions] +ProblemReductionsExt = "ProblemReductions" + [compat] Graphs = "1.6" LuxorGraphPlot = "0.5" +ProblemReductions = "0.1.1" julia = "1" [extras] @@ -20,4 +26,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "Random", "GenericTensorNetworks", "LinearAlgebra"] +test = ["Test", "Random", "GenericTensorNetworks", "LinearAlgebra", "ProblemReductions"] diff --git a/src/reduceto.jl b/ext/ProblemReductionsExt.jl similarity index 76% rename from src/reduceto.jl rename to ext/ProblemReductionsExt.jl index 5cd1079..a730bf1 100644 --- a/src/reduceto.jl +++ b/ext/ProblemReductionsExt.jl @@ -1,7 +1,9 @@ -struct IndependentSetToKSG{NT, VT} <: ProblemReductions.AbstractReductionResult - mapres::MappingResult{NT} - weights::VT -end +module ProblemReductionsExt + +using UnitDiskMapping, UnitDiskMapping.Graphs +import ProblemReductions: reduceto, target_problem, extract_multiple_solutions +import ProblemReductions + function _to_gridgraph(g::UnitDiskMapping.GridGraph) return ProblemReductions.GridGraph(getfield.(g.nodes, :loc), g.radius) end @@ -10,6 +12,10 @@ function _extract_weights(g::UnitDiskMapping.GridGraph{<:WeightedNode}) end ###### unweighted reduction +struct IndependentSetToKSG{NT, VT} <: ProblemReductions.AbstractReductionResult + mapres::MappingResult{NT} + weights::VT +end function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph) return IndependentSetToKSG(map_graph(UnWeighted(), problem.graph), problem.weights) end @@ -44,7 +50,7 @@ function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{Prob mres = map_factoring(problem.m, problem.n) g = _to_gridgraph(mres.grid_graph) ws = getfield.(mres.grid_graph.nodes, :weight) - mg, vmap = set_target(g, [mres.pins_zeros..., mres.pins_output...], problem.input << length(mres.pins_zeros)) + mg, vmap = UnitDiskMapping.set_target(g, [mres.pins_zeros..., mres.pins_output...], problem.input << length(mres.pins_zeros)) return FactoringToIndependentSet(mres, g, ws, vmap, ProblemReductions.IndependentSet(mg, ws[vmap])) end @@ -61,28 +67,34 @@ end ###### Spinglass problem to MIS on KSG ###### # NOTE: I am not sure about the correctness of this reduction. If you encounter a bug, please question this function! +struct SpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult + mapres::QUBOResult{NT} +end function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{<:SimpleGraph}) n = length(problem.h) M = similar(problem.h, n, n) for (e, j) in zip(edges(problem.graph), problem.J) M[e.src, e.dst] = M[e.dst, e.src] = j end - return map_qubo(M, -problem.h) + return SpinGlassToIndependentSet(map_qubo(M, -problem.h)) end -function ProblemReductions.target_problem(res::QUBOResult) - grid = _to_gridgraph(res.grid_graph) - ws = getfield.(res.grid_graph.nodes, :weight) +function ProblemReductions.target_problem(res::SpinGlassToIndependentSet) + grid = _to_gridgraph(res.mapres.grid_graph) + ws = getfield.(res.mapres.grid_graph.nodes, :weight) return ProblemReductions.IndependentSet(grid, ws) end -function ProblemReductions.extract_solution(res::QUBOResult, sol) - res = map_config_back(res, sol) +function ProblemReductions.extract_solution(res::SpinGlassToIndependentSet, sol) + res = map_config_back(res.mapres, sol) return 1 .- 2 .* Int.(res) end ###### Spinglass problem on grid to MIS on KSG ###### # NOTE: the restricted layout is not implemented, since it is not often used +struct SquareSpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult + mapres::SquareQUBOResult{NT} +end function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{ProblemReductions.GridGraph{2}}) g = problem.graph @assert 1 <= g.radius < sqrt(2) "Only support nearest neighbor interaction" @@ -90,17 +102,17 @@ function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{Prob onsite = [(i, j, -h) for ((i, j), h) in zip(g.locations, problem.h)] # a vector coupling of `(i, j, i', j', J)`, s.t. (i', j') == (i, j+1) or (i', j') = (i+1, j). # a vector of onsite term `(i, j, h)`. - return map_qubo_square(coupling, onsite) + return SquareSpinGlassToIndependentSet(map_qubo_square(coupling, onsite)) end -function ProblemReductions.target_problem(res::SquareQUBOResult) - grid = _to_gridgraph(res.grid_graph) - ws = getfield.(res.grid_graph.nodes, :weight) +function ProblemReductions.target_problem(res::SquareSpinGlassToIndependentSet) + grid = _to_gridgraph(res.mapres.grid_graph) + ws = getfield.(res.mapres.grid_graph.nodes, :weight) return ProblemReductions.IndependentSet(grid, ws) end -function ProblemReductions.extract_solution(res::SquareQUBOResult, sol) - res = map_config_back(res, sol) +function ProblemReductions.extract_solution(res::SquareSpinGlassToIndependentSet, sol) + res = map_config_back(res.mapres, sol) return 1 .- 2 .* Int.(res) end - \ No newline at end of file +end \ No newline at end of file diff --git a/src/UnitDiskMapping.jl b/src/UnitDiskMapping.jl index ba1d0fe..628f921 100644 --- a/src/UnitDiskMapping.jl +++ b/src/UnitDiskMapping.jl @@ -5,8 +5,6 @@ module UnitDiskMapping using Graphs using LuxorGraphPlot using LuxorGraphPlot.Luxor.Colors -import ProblemReductions: reduceto, target_problem, extract_multiple_solutions -import ProblemReductions # Basic types export UnWeighted, Weighted @@ -55,7 +53,6 @@ include("mapping.jl") include("weighted.jl") include("simplifiers.jl") include("extracting_results.jl") -include("reduceto.jl") include("visualize.jl") end diff --git a/src/dragondrop.jl b/src/dragondrop.jl index 66fd3bc..8d9e2ec 100644 --- a/src/dragondrop.jl +++ b/src/dragondrop.jl @@ -335,7 +335,7 @@ function post_process_grid(grid::Matrix{SimpleCell{T}}, h0, h1) where T return gg, pins end -struct QUBOResult{NT} <: ProblemReductions.AbstractReductionResult +struct QUBOResult{NT} grid_graph::GridGraph{NT} pins::Vector{Int} mis_overhead::Int @@ -353,13 +353,13 @@ function map_config_back(res::WMISResult, cfg) return cfg[res.pins] end -struct RestrictedQUBOResult{NT} <: ProblemReductions.AbstractReductionResult +struct RestrictedQUBOResult{NT} grid_graph::GridGraph{NT} end function map_config_back(res::RestrictedQUBOResult, cfg) end -struct SquareQUBOResult{NT} <: ProblemReductions.AbstractReductionResult +struct SquareQUBOResult{NT} grid_graph::GridGraph{NT} pins::Vector{Int} mis_overhead::Float64 From 8ae9645e3514c1735deab82c56e3171a48167e21 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Wed, 27 Nov 2024 23:08:02 +0100 Subject: [PATCH 5/5] fix tests --- ext/ProblemReductionsExt.jl | 10 +++++----- test/reduceto.jl | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ext/ProblemReductionsExt.jl b/ext/ProblemReductionsExt.jl index a730bf1..1ebcdeb 100644 --- a/ext/ProblemReductionsExt.jl +++ b/ext/ProblemReductionsExt.jl @@ -29,7 +29,7 @@ end ###### Weighted reduction # TODO: rescale the weights to avoid errors -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph) return IndependentSetToKSG(map_graph(Weighted(), problem.graph), problem.weights) end function ProblemReductions.target_problem(res::IndependentSetToKSG{<:WeightedNode}) @@ -46,7 +46,7 @@ struct FactoringToIndependentSet{NT} <: ProblemReductions.AbstractReductionResul vmap::Vector{Int} problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}} end -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.Factoring) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}}}, problem::ProblemReductions.Factoring) mres = map_factoring(problem.m, problem.n) g = _to_gridgraph(mres.grid_graph) ws = getfield.(mres.grid_graph.nodes, :weight) @@ -70,7 +70,7 @@ end struct SpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult mapres::QUBOResult{NT} end -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{<:SimpleGraph}) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.SpinGlass{<:SimpleGraph}) n = length(problem.h) M = similar(problem.h, n, n) for (e, j) in zip(edges(problem.graph), problem.J) @@ -95,7 +95,7 @@ end struct SquareSpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult mapres::SquareQUBOResult{NT} end -function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, T, Vector{T}}} where T, problem::ProblemReductions.SpinGlass{ProblemReductions.GridGraph{2}}) +function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.SpinGlass{ProblemReductions.GridGraph{2}}) g = problem.graph @assert 1 <= g.radius < sqrt(2) "Only support nearest neighbor interaction" coupling = [(g.locations[e.src]..., g.locations[e.dst]..., J) for (e, J) in zip(edges(g), problem.J)] @@ -115,4 +115,4 @@ function ProblemReductions.extract_solution(res::SquareSpinGlassToIndependentSet res = map_config_back(res.mapres, sol) return 1 .- 2 .* Int.(res) end -end \ No newline at end of file +end diff --git a/test/reduceto.jl b/test/reduceto.jl index 2f267be..319ef6c 100644 --- a/test/reduceto.jl +++ b/test/reduceto.jl @@ -1,6 +1,10 @@ using Test, UnitDiskMapping, Graphs, GenericTensorNetworks import ProblemReductions +@testset "reduction graph" begin + @test ProblemReductions.reduction_graph() isa ProblemReductions.ReductionGraph +end + @testset "rules" begin graph = complete_graph(3) # triangle fact = ProblemReductions.Factoring(2, 1, 2)