-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New extension: ProblemReductionsExt (#50)
* update * port to ProblemReductions.jl, tests all pass * port ProblemReductions * make problemreductions an extension * fix tests
- Loading branch information
Showing
7 changed files
with
203 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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 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,118 @@ | ||
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 | ||
function _extract_weights(g::UnitDiskMapping.GridGraph{<:WeightedNode}) | ||
getfield.(g.nodes, :weight) | ||
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 | ||
|
||
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 | ||
|
||
###### Weighted reduction | ||
# TODO: rescale the weights to avoid errors | ||
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}) | ||
graph = _to_gridgraph(res.mapres.grid_graph) | ||
weights = UnitDiskMapping.map_weights(res.mapres, res.weights) | ||
return ProblemReductions.IndependentSet(graph, weights) | ||
end | ||
|
||
###### Factoring ###### | ||
struct FactoringToIndependentSet{NT} <: ProblemReductions.AbstractReductionResult | ||
mapres::FactoringResult{NT} | ||
raw_graph::ProblemReductions.GridGraph{2} | ||
raw_weight::Vector{Int} | ||
vmap::Vector{Int} | ||
problem::ProblemReductions.IndependentSet{ProblemReductions.GridGraph{2}, Int, Vector{Int}} | ||
end | ||
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) | ||
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 | ||
|
||
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 | ||
|
||
###### 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}, 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) | ||
M[e.src, e.dst] = M[e.dst, e.src] = j | ||
end | ||
return SpinGlassToIndependentSet(map_qubo(M, -problem.h)) | ||
end | ||
|
||
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::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}, 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)] | ||
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 SquareSpinGlassToIndependentSet(map_qubo_square(coupling, onsite)) | ||
end | ||
|
||
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::SquareSpinGlassToIndependentSet, sol) | ||
res = map_config_back(res.mapres, sol) | ||
return 1 .- 2 .* Int.(res) | ||
end | ||
end |
This file contains 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 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 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,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 |
This file contains 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,46 @@ | ||
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) | ||
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 [ | ||
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 | ||
best_source = ProblemReductions.findbest(source, ProblemReductions.BruteForce()) | ||
|
||
# reduce and solve | ||
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( 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 | ||
@test Set(best_source_extracted_multiple) == Set(best_source) | ||
end | ||
end |
This file contains 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