Skip to content

Commit 4d04aec

Browse files
authored
New extension: ProblemReductionsExt (#50)
* update * port to ProblemReductions.jl, tests all pass * port ProblemReductions * make problemreductions an extension * fix tests
1 parent 4ef1965 commit 4d04aec

File tree

7 files changed

+203
-3
lines changed

7 files changed

+203
-3
lines changed

Project.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,16 @@ version = "0.4.0"
77
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
88
LuxorGraphPlot = "1f49bdf2-22a7-4bc4-978b-948dc219fbbc"
99

10+
[weakdeps]
11+
ProblemReductions = "899c297d-f7d2-4ebf-8815-a35996def416"
12+
13+
[extensions]
14+
ProblemReductionsExt = "ProblemReductions"
15+
1016
[compat]
1117
Graphs = "1.6"
1218
LuxorGraphPlot = "0.5"
19+
ProblemReductions = "0.1.1"
1320
julia = "1"
1421

1522
[extras]
@@ -19,4 +26,4 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1926
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2027

2128
[targets]
22-
test = ["Test", "Random", "GenericTensorNetworks", "LinearAlgebra"]
29+
test = ["Test", "Random", "GenericTensorNetworks", "LinearAlgebra", "ProblemReductions"]

ext/ProblemReductionsExt.jl

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
module ProblemReductionsExt
2+
3+
using UnitDiskMapping, UnitDiskMapping.Graphs
4+
import ProblemReductions: reduceto, target_problem, extract_multiple_solutions
5+
import ProblemReductions
6+
7+
function _to_gridgraph(g::UnitDiskMapping.GridGraph)
8+
return ProblemReductions.GridGraph(getfield.(g.nodes, :loc), g.radius)
9+
end
10+
function _extract_weights(g::UnitDiskMapping.GridGraph{<:WeightedNode})
11+
getfield.(g.nodes, :weight)
12+
end
13+
14+
###### unweighted reduction
15+
struct IndependentSetToKSG{NT, VT} <: ProblemReductions.AbstractReductionResult
16+
mapres::MappingResult{NT}
17+
weights::VT
18+
end
19+
function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, ProblemReductions.UnitWeight}}, problem::ProblemReductions.IndependentSet{GT, Int, ProblemReductions.UnitWeight} where GT<:SimpleGraph)
20+
return IndependentSetToKSG(map_graph(UnWeighted(), problem.graph), problem.weights)
21+
end
22+
23+
function ProblemReductions.target_problem(res::IndependentSetToKSG{<:UnWeightedNode})
24+
return ProblemReductions.IndependentSet(_to_gridgraph(res.mapres.grid_graph))
25+
end
26+
function ProblemReductions.extract_solution(res::IndependentSetToKSG, sol)
27+
return map_config_back(res.mapres, sol)
28+
end
29+
30+
###### Weighted reduction
31+
# TODO: rescale the weights to avoid errors
32+
function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.IndependentSet{GT} where GT<:SimpleGraph)
33+
return IndependentSetToKSG(map_graph(Weighted(), problem.graph), problem.weights)
34+
end
35+
function ProblemReductions.target_problem(res::IndependentSetToKSG{<:WeightedNode})
36+
graph = _to_gridgraph(res.mapres.grid_graph)
37+
weights = UnitDiskMapping.map_weights(res.mapres, res.weights)
38+
return ProblemReductions.IndependentSet(graph, weights)
39+
end
40+
41+
###### Factoring ######
42+
struct FactoringToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult
43+
mapres::FactoringResult{NT}
44+
raw_graph::ProblemReductions.GridGraph{2}
45+
raw_weight::Vector{Int}
46+
vmap::Vector{Int}
47+
problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}}
48+
end
49+
function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}}}, problem::ProblemReductions.Factoring)
50+
mres = map_factoring(problem.m, problem.n)
51+
g = _to_gridgraph(mres.grid_graph)
52+
ws = getfield.(mres.grid_graph.nodes, :weight)
53+
mg, vmap = UnitDiskMapping.set_target(g, [mres.pins_zeros..., mres.pins_output...], problem.input << length(mres.pins_zeros))
54+
return FactoringToIndependentSet(mres, g, ws, vmap, ProblemReductions.IndependentSet(mg, ws[vmap]))
55+
end
56+
57+
function ProblemReductions.target_problem(res::FactoringToIndependentSet)
58+
return res.problem
59+
end
60+
61+
function ProblemReductions.extract_solution(res::FactoringToIndependentSet, sol)
62+
cfg = zeros(Int, nv(res.raw_graph))
63+
cfg[res.vmap] .= sol
64+
i1, i2 = map_config_back(res.mapres, cfg)
65+
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)])
66+
end
67+
68+
###### Spinglass problem to MIS on KSG ######
69+
# NOTE: I am not sure about the correctness of this reduction. If you encounter a bug, please question this function!
70+
struct SpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult
71+
mapres::QUBOResult{NT}
72+
end
73+
function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.SpinGlass{<:SimpleGraph})
74+
n = length(problem.h)
75+
M = similar(problem.h, n, n)
76+
for (e, j) in zip(edges(problem.graph), problem.J)
77+
M[e.src, e.dst] = M[e.dst, e.src] = j
78+
end
79+
return SpinGlassToIndependentSet(map_qubo(M, -problem.h))
80+
end
81+
82+
function ProblemReductions.target_problem(res::SpinGlassToIndependentSet)
83+
grid = _to_gridgraph(res.mapres.grid_graph)
84+
ws = getfield.(res.mapres.grid_graph.nodes, :weight)
85+
return ProblemReductions.IndependentSet(grid, ws)
86+
end
87+
88+
function ProblemReductions.extract_solution(res::SpinGlassToIndependentSet, sol)
89+
res = map_config_back(res.mapres, sol)
90+
return 1 .- 2 .* Int.(res)
91+
end
92+
93+
###### Spinglass problem on grid to MIS on KSG ######
94+
# NOTE: the restricted layout is not implemented, since it is not often used
95+
struct SquareSpinGlassToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult
96+
mapres::SquareQUBOResult{NT}
97+
end
98+
function ProblemReductions.reduceto(::Type{ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}}}, problem::ProblemReductions.SpinGlass{ProblemReductions.GridGraph{2}})
99+
g = problem.graph
100+
@assert 1 <= g.radius < sqrt(2) "Only support nearest neighbor interaction"
101+
coupling = [(g.locations[e.src]..., g.locations[e.dst]..., J) for (e, J) in zip(edges(g), problem.J)]
102+
onsite = [(i, j, -h) for ((i, j), h) in zip(g.locations, problem.h)]
103+
# a vector coupling of `(i, j, i', j', J)`, s.t. (i', j') == (i, j+1) or (i', j') = (i+1, j).
104+
# a vector of onsite term `(i, j, h)`.
105+
return SquareSpinGlassToIndependentSet(map_qubo_square(coupling, onsite))
106+
end
107+
108+
function ProblemReductions.target_problem(res::SquareSpinGlassToIndependentSet)
109+
grid = _to_gridgraph(res.mapres.grid_graph)
110+
ws = getfield.(res.mapres.grid_graph.nodes, :weight)
111+
return ProblemReductions.IndependentSet(grid, ws)
112+
end
113+
114+
function ProblemReductions.extract_solution(res::SquareSpinGlassToIndependentSet, sol)
115+
res = map_config_back(res.mapres, sol)
116+
return 1 .- 2 .* Int.(res)
117+
end
118+
end

src/Core.jl

+6
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ function Graphs.SimpleGraph(grid::GridGraph{Node{ONE}})
7676
return unit_disk_graph(getfield.(grid.nodes, :loc), grid.radius)
7777
end
7878
coordinates(grid::GridGraph) = getfield.(grid.nodes, :loc)
79+
function Graphs.neighbors(g::GridGraph, i::Int)
80+
[j for j in 1:nv(g) if i != j && distance(g.nodes[i], g.nodes[j]) <= g.radius]
81+
end
82+
distance(n1::Node, n2::Node) = sqrt(sum(abs2, n1.loc .- n2.loc))
83+
Graphs.nv(g::GridGraph) = length(g.nodes)
84+
Graphs.vertices(g::GridGraph) = 1:nv(g)
7985

8086
# printing function for Grid graphs
8187
function print_grid(io::IO, grid::GridGraph{Node{WT}}; show_weight=false) where WT

src/multiplier.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function map_factoring(M::Int, N::Int)
9797
[pinloc(1, j, 1) for j=1:N]...,
9898
[pinloc(i, N, 4) for i=1:M]...,
9999
]
100-
return FactoringResult(gg, pp, pq, pm, p0)
100+
return FactoringResult(gg, pq, pp, pm, p0)
101101
end
102102

103103
struct FactoringResult{NT}
@@ -131,7 +131,7 @@ function solve_factoring(missolver, mres::FactoringResult, target::Int)
131131
return map_config_back(mres, cfg)
132132
end
133133

134-
function set_target(g::SimpleGraph, pins::AbstractVector, target::Int)
134+
function set_target(g, pins::AbstractVector, target::Int)
135135
vs = collect(vertices(g))
136136
for (i, p) in enumerate(pins)
137137
bitval = (target >> (i-1)) & 1

test/Core.jl

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Test, UnitDiskMapping, Graphs
2+
3+
@testset "GridGraph" begin
4+
grid = GridGraph((5, 5), [Node(2, 3), Node(2, 4), Node(5, 5)], 1.2)
5+
g = SimpleGraph(grid)
6+
@test ne(g) == 1
7+
@test vertices(grid) == vertices(g)
8+
@test neighbors(grid, 2) == neighbors(g, 2)
9+
10+
grid = GridGraph((5, 5), [Node(2, 3), Node(2, 4), Node(5, 5)], 4.0)
11+
g = SimpleGraph(grid)
12+
@test ne(g) == 3
13+
@test vertices(grid) == vertices(g)
14+
@test neighbors(grid, 2) == neighbors(g, 2)
15+
end

test/reduceto.jl

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Test, UnitDiskMapping, Graphs, GenericTensorNetworks
2+
import ProblemReductions
3+
4+
@testset "reduction graph" begin
5+
@test ProblemReductions.reduction_graph() isa ProblemReductions.ReductionGraph
6+
end
7+
8+
@testset "rules" begin
9+
graph = complete_graph(3) # triangle
10+
fact = ProblemReductions.Factoring(2, 1, 2)
11+
is = ProblemReductions.IndependentSet(graph)
12+
wis = ProblemReductions.IndependentSet(graph, rand(nv(graph)) .* 0.2)
13+
sg = ProblemReductions.SpinGlass(graph, [0.2, 0.4, -0.6], [0.1, 0.1, 0.1])
14+
sg2 = ProblemReductions.SpinGlass(graph, [0.1, 0.1, -0.1], [0.1, 0.1, 0.1])
15+
grid = ProblemReductions.GridGraph(ones(Bool, 2, 2), 1.2)
16+
sg_square = ProblemReductions.SpinGlass(grid, [0.1, 0.3, -0.1, 0.4], [0.1, 0.1, 0.1, 0.2])
17+
sg_square2 = ProblemReductions.SpinGlass(grid, [0.1, -0.3, 0.1, 0.4], [0.1, 0.1, 0.1, 0.2])
18+
for (source, target_type) in [
19+
sg_square => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}},
20+
sg_square2 => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}},
21+
sg => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}},
22+
sg2 => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}},
23+
is => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, ProblemReductions.UnitWeight},
24+
wis => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Float64, Vector{Float64}},
25+
fact => ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}},
26+
]
27+
@info "Testing reduction from $(typeof(source)) to $(target_type)"
28+
# directly solve
29+
best_source = ProblemReductions.findbest(source, ProblemReductions.BruteForce())
30+
31+
# reduce and solve
32+
result = ProblemReductions.reduceto(target_type, source)
33+
target = ProblemReductions.target_problem(result)
34+
@test target isa target_type
35+
#best_target = findbest(target, BruteForce())
36+
best_target = GenericTensorNetworks.solve(GenericTensorNetwork(GenericTensorNetworks.IndependentSet(SimpleGraph(target.graph), collect(target.weights))), ConfigsMax())[].c.data
37+
38+
# extract the solution
39+
best_source_extracted_single = unique( ProblemReductions.extract_solution.(Ref(result), best_target) )
40+
best_source_extracted_multiple = ProblemReductions.extract_multiple_solutions(result, best_target)
41+
42+
# check if the solutions are the same
43+
@test best_source_extracted_single best_source
44+
@test Set(best_source_extracted_multiple) == Set(best_source)
45+
end
46+
end

test/runtests.jl

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using UnitDiskMapping
22
using Test
33

4+
@testset "Core" begin
5+
include("Core.jl")
6+
end
7+
48
@testset "utils" begin
59
include("utils.jl")
610
end
@@ -48,3 +52,7 @@ end
4852
@testset "visualize" begin
4953
include("visualize.jl")
5054
end
55+
56+
@testset "reduceto" begin
57+
include("reduceto.jl")
58+
end

0 commit comments

Comments
 (0)