-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Greedy merge and better size reduction #43
base: main
Are you sure you want to change the base?
Changes from 8 commits
e338ca1
82d019d
b385d22
7198b18
129efa5
f2e54d1
7231cea
31f9a7a
771ae11
a0fb2fd
63ae467
88ae792
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
struct GreedyMerge <: AbstractSetCoverSolver end | ||
function optimal_branching_rule(table::BranchingTable, variables::Vector, problem::AbstractProblem, m::AbstractMeasure, solver::GreedyMerge) | ||
candidates = bit_clauses(table) | ||
return greedymerge(candidates, problem, variables, m) | ||
end | ||
|
||
function bit_clauses(tbl::BranchingTable{INT}) where {INT} | ||
n, bss = tbl.bit_length, tbl.table | ||
temp_clauses = [[Clause(bmask(INT, 1:n), bs) for bs in bss1] for bss1 in bss] | ||
return temp_clauses | ||
end | ||
|
||
function greedymerge(cls::Vector{Vector{Clause{INT}}}, problem::AbstractProblem, variables::Vector, m::AbstractMeasure) where {INT} | ||
active_cls = collect(1:length(cls)) | ||
cls = copy(cls) | ||
merging_pairs = [(i, j) for i in active_cls, j in active_cls if i < j] | ||
n = length(variables) | ||
size_reductions = [size_reduction(problem, m, candidate[1], variables) for candidate in cls] | ||
γ = complexity_bv(size_reductions) | ||
while !isempty(merging_pairs) | ||
i, j = popfirst!(merging_pairs) | ||
if i in active_cls && j in active_cls | ||
for ii in 1:length(cls[i]), jj in 1:length(cls[j]) | ||
if bdistance(cls[i][ii], cls[j][jj]) == 1 | ||
cl12 = gather2(n, cls[i][ii], cls[j][jj]) | ||
if cl12.mask == 0 | ||
continue | ||
end | ||
l12 = size_reduction(problem, m, cl12, variables) | ||
if γ^(-size_reductions[i]) + γ^(-size_reductions[j]) >= γ^(-l12) + 1e-8 | ||
push!(cls, [cl12]) | ||
k = length(cls) | ||
deleteat!(active_cls, findfirst(==(i), active_cls)) | ||
deleteat!(active_cls, findfirst(==(j), active_cls)) | ||
for ii in active_cls | ||
push!(merging_pairs, (ii, k)) | ||
end | ||
push!(active_cls, k) | ||
push!(size_reductions, l12) | ||
γ = complexity_bv(size_reductions[active_cls]) | ||
break | ||
end | ||
end | ||
end | ||
end | ||
end | ||
return [cl[1] for cl in cls[active_cls]] | ||
end | ||
|
||
function size_reduction(p::AbstractProblem, m::AbstractMeasure, cl::Clause{INT}, variables::Vector) where {INT} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. repeated function and wrong format |
||
return measure(p, m) - measure(first(apply_branch(p, cl, variables)), m) | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using Test | ||
using OptimalBranchingCore | ||
using OptimalBranchingCore: bit_clauses | ||
using OptimalBranchingCore.BitBasis | ||
using GenericTensorNetworks | ||
|
||
@testset "bit_clauses" begin | ||
tbl = BranchingTable(5, [ | ||
[StaticElementVector(2, [0, 0, 1, 0, 0]), StaticElementVector(2, [0, 1, 0, 0, 0])], | ||
[StaticElementVector(2, [1, 0, 0, 1, 0])], | ||
[StaticElementVector(2, [0, 0, 1, 0, 1])], | ||
]) | ||
|
||
bc = bit_clauses(tbl) | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,37 @@ | ||
""" | ||
mis_size(g::AbstractGraph; bs::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer::AbstractReducer = MISReducer()) | ||
mis_size(g::AbstractGraph; branching_strategy::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer::AbstractReducer = MISReducer()) | ||
|
||
Calculate the size of the Maximum Independent Set (MIS) for a given graph. | ||
|
||
### Arguments | ||
- `g::AbstractGraph`: The graph for which the MIS size is to be calculated. | ||
- `bs::BranchingStrategy`: (optional) The branching strategy to be used. Defaults to a strategy using `table_solver=TensorNetworkSolver`, `selector=MinBoundaryHighDegreeSelector(2, 6, 0)`, and `measure=D3Measure`. | ||
- `branching_strategy::BranchingStrategy`: (optional) The branching strategy to be used. Defaults to a strategy using `table_solver=TensorNetworkSolver`, `selector=MinBoundaryHighDegreeSelector(2, 6, 0)`, and `measure=D3Measure`. | ||
- `reducer::AbstractReducer`: (optional) The reducer to be applied. Defaults to `MISReducer`. | ||
|
||
### Returns | ||
- An integer representing the size of the Maximum Independent Set for the given graph. | ||
""" | ||
function mis_size(g::AbstractGraph; bs::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer=MISReducer()) | ||
function mis_size(g::AbstractGraph; branching_strategy::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure = D3Measure()), reducer = MISReducer()) | ||
p = MISProblem(g) | ||
res = branch_and_reduce(p, bs, reducer, MaxSize) | ||
res = branch_and_reduce(p, branching_strategy, reducer, MaxSize) | ||
return res.size | ||
end | ||
|
||
""" | ||
mis_branch_count(g::AbstractGraph; bs::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer=MISReducer()) | ||
mis_branch_count(g::AbstractGraph; branching_strategy::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer=MISReducer()) | ||
|
||
Calculate the size and the number of branches of the Maximum Independent Set (MIS) for a given graph. | ||
|
||
### Arguments | ||
- `g::AbstractGraph`: The graph for which the MIS size and the number of branches are to be calculated. | ||
- `bs::BranchingStrategy`: (optional) The branching strategy to be used. Defaults to a strategy using `table_solver=TensorNetworkSolver`, `selector=MinBoundaryHighDegreeSelector(2, 6, 0)`, and `measure=D3Measure`. | ||
- `branching_strategy::BranchingStrategy`: (optional) The branching strategy to be used. Defaults to a strategy using `table_solver=TensorNetworkSolver`, `selector=MinBoundaryHighDegreeSelector(2, 6, 0)`, and `measure=D3Measure`. | ||
- `reducer::AbstractReducer`: (optional) The reducer to be applied. Defaults to `MISReducer`. | ||
|
||
### Returns | ||
- A tuple `(size, count)` where `size` is the size of the Maximum Independent Set and `count` is the number of branches. | ||
""" | ||
function mis_branch_count(g::AbstractGraph; branching_strategy::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure=D3Measure()), reducer=MISReducer()) | ||
function mis_branch_count(g::AbstractGraph; branching_strategy::BranchingStrategy = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure = D3Measure()), reducer = MISReducer()) | ||
p = MISProblem(g) | ||
res = branch_and_reduce(p, branching_strategy, reducer, MaxSizeBranchCount) | ||
return (res.size, res.count) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
""" | ||
mutable struct MISProblem <: AbstractProblem | ||
mutable struct MISProblem <: AbstractProblem | ||
|
||
Represents a Maximum Independent Set (MIS) problem. | ||
|
||
|
@@ -12,15 +12,15 @@ Represents a Maximum Independent Set (MIS) problem. | |
|
||
""" | ||
mutable struct MISProblem <: AbstractProblem | ||
g::SimpleGraph | ||
g::SimpleGraph{Int} | ||
end | ||
Base.copy(p::MISProblem) = MISProblem(copy(p.g)) | ||
Base.show(io::IO, p::MISProblem) = print(io, "MISProblem($(nv(p.g)))") | ||
Base.isempty(p::MISProblem) = nv(p.g) == 0 | ||
|
||
""" | ||
TensorNetworkSolver | ||
TensorNetworkSolver(; prune_by_env::Bool = true) | ||
TensorNetworkSolver | ||
TensorNetworkSolver(; prune_by_env::Bool = true) | ||
|
||
A struct representing a solver for tensor network problems. | ||
This struct serves as a specific implementation of the `AbstractTableSolver` type. | ||
|
@@ -30,7 +30,7 @@ This struct serves as a specific implementation of the `AbstractTableSolver` typ | |
end | ||
|
||
""" | ||
NumOfVertices | ||
NumOfVertices | ||
|
||
A struct representing a measure that counts the number of vertices in a graph. | ||
Each vertex is counted as 1. | ||
|
@@ -41,7 +41,7 @@ Each vertex is counted as 1. | |
struct NumOfVertices <: AbstractMeasure end | ||
|
||
""" | ||
measure(p::MISProblem, ::NumOfVertices) | ||
measure(p::MISProblem, ::NumOfVertices) | ||
|
||
Calculates the number of vertices in the given `MISProblem`. | ||
|
||
|
@@ -54,7 +54,7 @@ Calculates the number of vertices in the given `MISProblem`. | |
OptimalBranchingCore.measure(p::MISProblem, ::NumOfVertices) = nv(p.g) | ||
|
||
""" | ||
D3Measure | ||
D3Measure | ||
|
||
A struct representing a measure that calculates the sum of the maximum degree minus 2 for each vertex in the graph. | ||
|
||
|
@@ -64,7 +64,7 @@ A struct representing a measure that calculates the sum of the maximum degree mi | |
struct D3Measure <: AbstractMeasure end | ||
|
||
""" | ||
measure(p::MISProblem, ::D3Measure) | ||
measure(p::MISProblem, ::D3Measure) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The leading spaces should not be removed. Please change it back. |
||
|
||
Calculates the D3 measure for the given `MISProblem`, which is defined as the sum of | ||
the maximum degree of each vertex minus 2, for all vertices in the graph. | ||
|
@@ -83,4 +83,17 @@ function OptimalBranchingCore.measure(p::MISProblem, ::D3Measure) | |
dg = degree(g) | ||
return Int(sum(max(d - 2, 0) for d in dg)) | ||
end | ||
end | ||
end | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, the other measures will fail to work. I think we should keep the previous one as a fallback if no speacial Oh I noticed that this function is defined in another file, please move it here. |
||
function OptimalBranchingCore.size_reduction(p::MISProblem, m::D3Measure, cl::Clause{INT}, variables::Vector) where {INT} | ||
vertices_removed = removed_vertices(variables, p.g, cl) | ||
sum = 0 | ||
for v in vertices_removed | ||
sum += max(degree(p.g, v) - 2, 0) | ||
end | ||
vertices_removed_neighbors = setdiff(mapreduce(v -> neighbors(p.g, v), ∪, vertices_removed), vertices_removed) | ||
for v in vertices_removed_neighbors | ||
sum += max(degree(p.g, v) - 2) - max(degree(p.g, v) - 2 - count(vx -> vx ∈ vertices_removed, neighbors(p.g, v)), 0) | ||
end | ||
return sum | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using OptimalBranchingMIS | ||
using OptimalBranchingMIS.EliminateGraphs.Graphs | ||
using Test | ||
using Random | ||
using OptimalBranchingCore | ||
using OptimalBranchingCore.BitBasis | ||
using GenericTensorNetworks | ||
using OptimalBranchingCore: bit_clauses | ||
Random.seed!(1234) | ||
|
||
# Example from arXiv:2412.07685 Fig. 1 | ||
@testset "GreedyMerge" begin | ||
edges = [(1, 4), (1, 5), (3, 4), (2, 5), (4, 5), (1, 6), (2, 7), (3, 8)] | ||
example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) | ||
p = MISProblem(example_g) | ||
tbl = BranchingTable(5, [ | ||
[StaticElementVector(2, [0, 0, 0, 0, 1]), StaticElementVector(2, [0, 0, 0, 1, 0])], | ||
[StaticElementVector(2, [0, 0, 1, 0, 1])], | ||
[StaticElementVector(2, [0, 1, 0, 1, 0])], | ||
[StaticElementVector(2, [1, 1, 1, 0, 0])], | ||
]) | ||
cls = bit_clauses(tbl) | ||
clsf = OptimalBranchingCore.greedymerge(cls, p, [1, 2, 3, 4, 5], D3Measure()) | ||
@test clsf[1].mask == cls[3][1].mask | ||
@test clsf[1].val == cls[3][1].val | ||
@test clsf[2].mask == cls[4][1].mask | ||
@test clsf[2].val == cls[4][1].val | ||
@test clsf[3].mask == 27 | ||
@test clsf[3].val == 16 | ||
end | ||
|
||
@testset "GreedyMerge" begin | ||
g = random_regular_graph(20, 3) | ||
mis_num, count2 = mis_branch_count(g) | ||
for reducer in [NoReducer(), MISReducer()] | ||
for measure in [D3Measure(), NumOfVertices()] | ||
bs = BranchingStrategy(table_solver = TensorNetworkSolver(), selector = MinBoundaryHighDegreeSelector(2, 6, 0), measure = measure, set_cover_solver = OptimalBranchingCore.GreedyMerge()) | ||
mis1, count1 = mis_branch_count(g; branching_strategy = bs, reducer) | ||
@test mis1 == mis_num | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using OptimalBranchingMIS | ||
using OptimalBranchingMIS.EliminateGraphs.Graphs | ||
using Test | ||
using Random | ||
using OptimalBranchingCore | ||
using OptimalBranchingCore.BitBasis | ||
using GenericTensorNetworks | ||
using OptimalBranchingCore: size_reduction, apply_branch | ||
|
||
@testset "size_reduction" begin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nothing is tested here. Should compare this function with the naive way |
||
g = random_regular_graph(60, 3) | ||
vs = collect(1:20) | ||
cl = Clause(bit"1111111111", bit"1011010111") | ||
p = MISProblem(g) | ||
m = D3Measure() | ||
@test size_reduction(p, m, cl, vs) == measure(p, m) - measure(first(apply_branch(p, cl, vs)), m) | ||
end | ||
|
||
|
||
@testset "covered_by" begin | ||
tbl = BranchingTable(9, [ | ||
[[0,0,0,0,0,1,1,0,0], [0,0,0,0,0,0,1,1,0]], | ||
[[0,0,0,0,1,1,1,0,0]], | ||
[[0,0,1,1,0,0,0,0,1], [0,0,1,1,0,1,0,0,0], [0,0,1,1,0,0,0,1,0]], | ||
[[0,0,1,1,1,0,0,0,1], [0,0,1,1,1,1,0,0,0]], | ||
[[0,1,0,0,0,0,1,1,0]], | ||
[[0,1,0,1,1,0,0,0,1]], | ||
[[0,1,1,0,1,0,0,0,1]], | ||
[[0,1,1,1,0,0,0,0,1], [0,1,1,1,0,0,0,1,0]], | ||
[[0,1,1,1,1,0,0,0,1]], | ||
[[1,0,0,0,0,0,1,1,0]], | ||
[[1,0,0,1,1,0,0,0,1]], | ||
[[1,0,1,0,1,0,0,0,1]], | ||
[[1,0,1,1,0,0,0,0,1], [1,0,1,1,0,0,0,1,0]], | ||
[[1,0,1,1,1,0,0,0,1]], | ||
[[1,1,0,0,0,0,1,1,0]], | ||
[[1,1,0,1,1,0,0,0,1]], | ||
[[1,1,1,0,1,0,0,0,1]], | ||
[[1,1,1,1,0,0,0,0,1], [1,1,1,1,0,0,0,1,0]], | ||
[[1,1,1,1,1,0,0,0,1]] | ||
]) | ||
clauses = OptimalBranchingCore.candidate_clauses(tbl) | ||
Δρ = [count_ones(c.mask) for c in clauses] | ||
result_ip = OptimalBranchingCore.minimize_γ(tbl, clauses, Δρ, IPSolver(max_itr = 10, verbose = false)) | ||
@test OptimalBranchingCore.covered_by(tbl, result_ip.optimal_rule) | ||
|
||
p = MISProblem(random_regular_graph(20, 3)) | ||
cls = OptimalBranchingCore.bit_clauses(tbl) | ||
clsf = OptimalBranchingCore.greedymerge(cls, p, [1, 2, 3, 4, 5], D3Measure()) | ||
@test OptimalBranchingCore.covered_by(tbl, DNF(clsf)) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No... This line is very stupid.