Automatic rule discovery
using OptimalBranching.OptimalBranchingMIS.Graphs, OptimalBranching, OptimalBranching.OptimalBranchingMIS
using OptimalBranching.OptimalBranchingCore, OptimalBranching.OptimalBranchingCore.BitBasis
using OptimalBranching.OptimalBranchingCore: IPSolver
using ProblemReductions
WARNING: using ProblemReductions.IPSolver in module Main conflicts with an existing identifier.
This function generates the tree-like N3 neighborhood of g0.
function tree_like_N3_neighborhood(g0::SimpleGraph)
g = copy(g0)
for layer in 1:3
for v in vertices(g)
for _ = 1:(3-degree(g, v))
add_vertex!(g)
add_edge!(g, v, nv(g))
end
end
end
return g
end
function solve_opt_rule(branching_region, graph, vs)
# Use default solver and measure
m = D3Measure()
table_solver = TensorNetworkSolver(; prune_by_env=true)
set_cover_solver = IPSolver()
# Pruning irrelevant entries
ovs = OptimalBranchingMIS.open_vertices(graph, vs)
subg, vmap = induced_subgraph(graph, vs)
@info "solving the branching table..."
tbl = OptimalBranchingMIS.reduced_alpha_configs(table_solver, subg, UnitWeight(nv(subg)), Int[findfirst(==(v), vs) for v in ovs])
@info "the length of the truth_table after pruning irrelevant entries: $(length(tbl.table))"
@info "generating candidate clauses..."
candidate_clauses = OptimalBranchingMIS.OptimalBranchingCore.candidate_clauses(tbl)
@info "the length of the candidate clauses: $(length(candidate_clauses))"
@info "generating the optimal branching rule via set cover..."
problem = MISProblem(graph)
size_reductions = [measure(problem, m) - measure(first(OptimalBranchingCore.apply_branch(problem, candidate, vs)), m) for candidate in candidate_clauses]
result = OptimalBranchingMIS.OptimalBranchingCore.minimize_γ(tbl, candidate_clauses, size_reductions, set_cover_solver)
is_valid, gamma = OptimalBranchingCore.test_rule(tbl, result.optimal_rule, problem, m, vs)
@assert is_valid "The rule is not valid"
@assert gamma ≈ result.γ "The gamma is not correct"
@info "the minimized gamma: $(result.γ)"
@info "the optimal branching rule on R:"
viz_dnf(result.optimal_rule, vs)
return result
end
function viz_dnf(dnf::DNF{INT}, variables::Vector{T}) where {T, INT}
for c in dnf.clauses
println(join([iszero(readbit(c.val, i)) ? "¬$(variables[i])" : "$(variables[i])" for i = 1:bsizeof(INT) if readbit(c.mask, i) == 1], " ∧ "))
end
end
viz_dnf (generic function with 1 method)
Domination rule
Define the branching region R
vs = [1,2,3,4,5]
edges = [(1, 2), (2, 3), (1, 3), (2, 4), (3, 4), (2, 5), (4, 5)]
branching_region = SimpleGraph(Graphs.SimpleEdge.(edges))
graph = tree_like_N3_neighborhood(branching_region)
solve_opt_rule(branching_region, graph, vs)
OptimalBranchingResult{BitBasis.LongLongUInt{1}, Int64}:
optimal_rule: DNF{BitBasis.LongLongUInt{1}}: (¬#2)
branching_vector: [6]
γ: 1.0
PH2 rule
Define the branching region R
vs = [1,2,3,4,5,6,7,8]
edges = [(1, 2), (1, 5), (2, 3), (2, 6), (3, 4), (4, 5), (5, 8), (6, 7), (7, 8)]
branching_region = SimpleGraph(Graphs.SimpleEdge.(edges))
{8, 9} undirected simple Int64 graph
Generate the tree-like N3 neighborhood of R
graph = tree_like_N3_neighborhood(branching_region)
solve_opt_rule(branching_region, graph, vs)
OptimalBranchingResult{BitBasis.LongLongUInt{1}, Int64}:
optimal_rule: DNF{BitBasis.LongLongUInt{1}}: (¬#7) ∨ (¬#1 ∧ #2 ∧ ¬#3 ∧ ¬#4 ∧ #5 ∧ ¬#6 ∧ #7 ∧ ¬#8)
branching_vector: [4, 16]
γ: 1.0839059681358738
Bottleneck case
Define the branching region R
vs = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]
edges = [(1, 2), (1, 3), (1, 4), (2, 5), (2, 6), (3, 7), (3, 8), (4, 9), (4, 10), (5, 11), (5, 12), (6, 13), (6, 14), (7, 15), (7, 16), (8, 17), (8, 18), (9, 19), (9, 20), (10, 21), (10, 22), (11, 14), (12, 13), (15, 18), (16, 17), (19, 22), (20, 21)]
branching_region = SimpleGraph(Graphs.SimpleEdge.(edges))
{22, 27} undirected simple Int64 graph
Generate the tree-like N3 neighborhood of R
graph = tree_like_N3_neighborhood(branching_region)
solve_opt_rule(branching_region, graph, vs)
OptimalBranchingResult{BitBasis.LongLongUInt{1}, Int64}:
optimal_rule: DNF{BitBasis.LongLongUInt{1}}: (¬#1 ∧ #4 ∧ ¬#9 ∧ ¬#10 ∧ ¬#11 ∧ ¬#13 ∧ ¬#15 ∧ ¬#17) ∨ (¬#1 ∧ #2 ∧ #3 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4) ∨ (¬#1 ∧ #4 ∧ ¬#9 ∧ ¬#10 ∧ ¬#12 ∧ ¬#14 ∧ ¬#16 ∧ ¬#18)
branching_vector: [26, 16, 10, 26]
γ: 1.081711631576666
Generating rules for large scale problems
For large scale problems, we can use the greedy merge rule to generate rules, which avoids generating all candidate clauses.
function solve_greedy_rule(branching_region, graph, vs)
# Use default solver and measure
m = D3Measure()
table_solver = TensorNetworkSolver(; prune_by_env=true)
# Pruning irrelevant entries
ovs = OptimalBranchingMIS.open_vertices(graph, vs)
subg, vmap = induced_subgraph(graph, vs)
@info "solving the branching table..."
tbl = OptimalBranchingMIS.reduced_alpha_configs(table_solver, subg, UnitWeight(nv(subg)), Int[findfirst(==(v), vs) for v in ovs])
@info "the length of the truth_table after pruning irrelevant entries: $(length(tbl.table))"
@info "generating the optimal branching rule via greedy merge..."
candidates = OptimalBranchingCore.bit_clauses(tbl)
result = OptimalBranchingMIS.OptimalBranchingCore.greedymerge(candidates, MISProblem(graph), vs, m)
is_valid, gamma = OptimalBranchingCore.test_rule(tbl, result.optimal_rule, MISProblem(graph), m, vs)
@assert is_valid "The rule is not valid"
@assert gamma ≈ result.γ "The gamma is not correct"
@info "the greedily minimized gamma: $(result.γ)"
@info "the branching rule on R:"
viz_dnf(result.optimal_rule, vs)
return result
end
result = solve_greedy_rule(branching_region, graph, vs)
OptimalBranchingResult{BitBasis.LongLongUInt{1}, Float64}:
optimal_rule: DNF{BitBasis.LongLongUInt{1}}: (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #6 ∧ #8 ∧ #10 ∧ ¬#13 ∧ ¬#14 ∧ ¬#17 ∧ ¬#18 ∧ ¬#21 ∧ ¬#22) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #5 ∧ ¬#6 ∧ #8 ∧ #10 ∧ ¬#11 ∧ ¬#12 ∧ #13 ∧ #14 ∧ ¬#17 ∧ ¬#18 ∧ ¬#21 ∧ ¬#22) ∨ (¬#1 ∧ #2 ∧ #3 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8 ∧ #15 ∧ ¬#16 ∧ #17 ∧ ¬#18 ∧ ¬#19 ∧ ¬#21) ∨ (¬#1 ∧ #2 ∧ #3 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8 ∧ #11 ∧ ¬#12 ∧ #13 ∧ ¬#14 ∧ #16 ∧ ¬#17 ∧ ¬#22) ∨ (¬#1 ∧ #2 ∧ #3 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8 ∧ ¬#11 ∧ #12 ∧ ¬#13 ∧ #14 ∧ ¬#15 ∧ #18 ∧ ¬#21) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #6 ∧ #7 ∧ ¬#8 ∧ #9 ∧ ¬#13 ∧ ¬#14 ∧ ¬#15 ∧ ¬#16 ∧ #17 ∧ #18 ∧ ¬#19 ∧ ¬#20) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #5 ∧ ¬#6 ∧ #7 ∧ ¬#8 ∧ #9 ∧ ¬#11 ∧ ¬#12 ∧ #13 ∧ #14 ∧ ¬#15 ∧ ¬#16 ∧ #17 ∧ #18 ∧ ¬#19 ∧ ¬#20) ∨ (¬#1 ∧ #3 ∧ #4 ∧ ¬#7 ∧ ¬#8 ∧ ¬#9 ∧ ¬#10 ∧ #15 ∧ ¬#16 ∧ #17 ∧ ¬#18) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #5 ∧ #7 ∧ ¬#8 ∧ ¬#9 ∧ #10 ∧ ¬#11 ∧ ¬#12 ∧ ¬#15 ∧ ¬#16 ∧ #17 ∧ #18 ∧ #19 ∧ #20 ∧ ¬#21 ∧ ¬#22) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ ¬#5 ∧ #6 ∧ #7 ∧ ¬#8 ∧ ¬#9 ∧ #10 ∧ #11 ∧ #12 ∧ ¬#13 ∧ ¬#14 ∧ ¬#15 ∧ ¬#16 ∧ #17 ∧ #18 ∧ #19 ∧ #20 ∧ ¬#21 ∧ ¬#22) ∨ (¬#1 ∧ #2 ∧ ¬#3 ∧ #4 ∧ ¬#5 ∧ ¬#6 ∧ #7 ∧ ¬#9 ∧ ¬#10 ∧ ¬#15 ∧ ¬#16 ∧ #19 ∧ ¬#20 ∧ #21 ∧ ¬#22) ∨ (¬#1 ∧ #2 ∧ #3 ∧ #4 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8 ∧ ¬#9 ∧ ¬#10 ∧ ¬#11 ∧ #14 ∧ #19 ∧ ¬#20 ∧ #21 ∧ ¬#22) ∨ (¬#1 ∧ ¬#2 ∧ #3 ∧ #4 ∧ #6 ∧ ¬#7 ∧ ¬#8 ∧ ¬#9 ∧ ¬#10 ∧ ¬#13 ∧ ¬#14 ∧ ¬#15 ∧ #16 ∧ ¬#17 ∧ #18 ∧ #19 ∧ ¬#20 ∧ #21 ∧ ¬#22) ∨ (¬#1 ∧ #2 ∧ #4 ∧ ¬#5 ∧ ¬#6 ∧ ¬#9 ∧ ¬#10 ∧ ¬#17 ∧ ¬#19 ∧ #22) ∨ (¬#1 ∧ ¬#2 ∧ #3 ∧ #4 ∧ #5 ∧ ¬#7 ∧ ¬#8 ∧ ¬#9 ∧ ¬#10 ∧ ¬#11 ∧ ¬#12 ∧ ¬#15 ∧ #16 ∧ ¬#17 ∧ #18 ∧ ¬#19 ∧ #20 ∧ ¬#21 ∧ #22) ∨ (¬#1 ∧ #2 ∧ #3 ∧ #4 ∧ ¬#5 ∧ ¬#6 ∧ ¬#7 ∧ ¬#8 ∧ ¬#9 ∧ ¬#10 ∧ #11 ∧ ¬#12 ∧ #13 ∧ ¬#14 ∧ ¬#15 ∧ ¬#16 ∧ #17 ∧ #18 ∧ ¬#19 ∧ #20 ∧ ¬#21 ∧ #22) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #6 ∧ #8 ∧ #9 ∧ ¬#10 ∧ ¬#13 ∧ ¬#14 ∧ ¬#17 ∧ ¬#18 ∧ ¬#19 ∧ ¬#20 ∧ #21 ∧ #22) ∨ (#1 ∧ ¬#2 ∧ ¬#3 ∧ ¬#4 ∧ #5 ∧ ¬#6 ∧ #8 ∧ #9 ∧ ¬#10 ∧ ¬#11 ∧ ¬#12 ∧ #13 ∧ #14 ∧ ¬#17 ∧ ¬#18 ∧ ¬#19 ∧ ¬#20 ∧ #21 ∧ #22)
branching_vector: [28.0, 34.0, 32.0, 32.0, 32.0, 34.0, 40.0, 24.0, 40.0, 46.0, 32.0, 34.0, 40.0, 24.0, 40.0, 46.0, 34.0, 40.0]
γ: 1.090233540640428
This page was generated using Literate.jl.