OptimalBranchingCore

Literal, Clause and DNF

Literals, clauses and disjunctive normal form (DNF) are basic concepts in boolean logic, where literals are boolean variables, clauses are boolean expressions, and DNF is a disjunction of one or more conjunctions of literals.

Here is an example, given a truth table as follows:

abcvalue
0001
0011
0100
0110
1000
1011
1100
1110

where $a, b, c$ are boolean variables called literals. The true statements can be represented as a conjunction of literals, for example,

\[\neg a \land \neg b \land \neg c, \neg a \land \neg b \land c, a \land \neg b \land c\]

and these clauses can be combined into a DNF:

\[(\neg a \land \neg b) \lor (a \land \neg b \land c).\]

In OptimalBranchingCore, a clause is represented by the Clause type, and a DNF is represented by the DNF type, based on the BitBasis.jl package.

julia> using OptimalBranchingCore, BitBasis
julia> c1 = Clause(bit"011", bit"000")Clause{BitBasis.DitStr{2, 3, Int64}}: ¬#1 ∧ ¬#2
julia> c2 = Clause(bit"111", bit"101")Clause{BitBasis.DitStr{2, 3, Int64}}: #1 ∧ ¬#2 ∧ #3
julia> dnf = DNF(c1, c2)DNF{BitBasis.DitStr{2, 3, Int64}}: (¬#1 ∧ ¬#2) ∨ (#1 ∧ ¬#2 ∧ #3)

The branch and bound algorithm

The branch and bound algorithm is a method to exactly solve the combinatorial optimization problems.

API

OptimalBranchingCore.apply_branchFunction
apply_branch(problem::AbstractProblem, clause::Clause, vertices::Vector)::Tuple

Create a branch from the given clause applied to the specified vertices.

Arguments

  • problem: The problem instance.
  • clause: The clause that containing the information about how to fix the values of the variables.
  • vertices: A vector of vertices to be considered for the branch.

Returns

  • AbstractProblem: A new instance of AbstractProblem with reduced size.
source
OptimalBranchingCore.branch_and_reduceMethod
branch_and_reduce(problem::AbstractProblem, config::BranchingStrategy; reducer::AbstractReducer=NoReducer(), result_type=Int)

Branch the given problem using the specified solver configuration.

Arguments

  • problem: The problem instance to solve.
  • config: The configuration for the solver, which is a BranchingStrategy instance.

Keyword Arguments

  • reducer::AbstractReducer=NoReducer(): The reducer to reduce the problem size.
  • result_type::Type{TR}: The type of the result that the solver will produce.

Returns

The resulting value, which may have different type depending on the result_type.

source
OptimalBranchingCore.branching_tableFunction
branching_table(problem::AbstractProblem, table_solver::AbstractTableSolver, variables::Vector{Int})

Obtains the branching table for a given problem using a specified table solver.

Arguments

  • problem: The problem instance.
  • table_solver: The table solver, which is a subtype of AbstractTableSolver.
  • variables: A vector of indices of the variables to be considered for the branching table.

Returns

A branching table, which is a BranchingTable object.

source
OptimalBranchingCore.candidate_clausesMethod
candidate_clauses(tbl::BranchingTable{INT}) where {INT}

Generates candidate clauses from a branching table.

Arguments

  • tbl::BranchingTable{INT}: The branching table containing bit strings.

Returns

  • Vector{Clause{INT}}: A vector of Clause objects generated from the branching table.
source
OptimalBranchingCore.complexity_bvMethod
complexity_bv(branching_vector::Vector)::Float64

Calculates the complexity that associated with the provided branching vector by solving the equation:

\[γ^0 = \sum_{δρ \in \text{branching_vector}} γ^{-δρ}\]

Arguments

  • branching_vector: a vector of problem size reductions in the branches.

Returns

  • Float64: The computed γ value.
source
OptimalBranchingCore.covered_byMethod
covered_by(t::BranchingTable, dnf::DNF)

Check if the branching table t is covered by the logic expression dnf. Returns true if there exists at least one bitstring in each group of t that satisfies dnf, false otherwise.

source
OptimalBranchingCore.covered_byMethod
covered_by(a::Integer, clause_or_dnf)

Check if a is covered by the logic expression clause_or_dnf.

Arguments

  • a: A bit string.
  • clause_or_dnf: Logic expression, which can be a Clause object or a DNF object.

Returns

true if a satisfies clause_or_dnf, false otherwise.

source
OptimalBranchingCore.measureFunction
measure(problem::AbstractProblem, measure::AbstractMeasure)::Number

Calculate the size of the problem, reducing which serves as the guiding principle for the branching strategy.

Arguments

  • problem: The problem instance.
  • measure: The measure of the problem size.

Returns

A real number representing the problem size.

source
OptimalBranchingCore.minimize_γMethod
minimize_γ(table::BranchingTable, candidates::Vector{Clause}, Δρ::Vector, solver)

Finds the optimal cover based on the provided vector of problem size reduction. This function implements a cover selection algorithm using an iterative process. It utilizes an integer programming solver to optimize the selection of sub-covers based on their complexity.

Arguments

  • table::BranchingTable: A branching table containing clauses that need to be covered, a table entry is covered by a clause if one of its bit strings satisfies the clause. Please refer to covered_by for more details.
  • candidates::Vector{Clause}: A vector of candidate clauses to form the branching rule (in the form of DNF).
  • Δρ::Vector: A vector of problem size reduction for each candidate clause.
  • solver: The solver to be used. It can be an instance of LPSolver or IPSolver.

Keyword Arguments

  • γ0::Float64: The initial γ value.

Returns

A tuple of two elements: (indices of selected subsets, γ)

source
OptimalBranchingCore.optimal_branching_ruleMethod
optimal_branching_rule(table::BranchingTable, variables::Vector, problem::AbstractProblem, measure::AbstractMeasure, solver::AbstractSetCoverSolver)::OptimalBranchingResult

Generate an optimal branching rule from a given branching table.

Arguments

  • table: A BranchingTable instance containing candidate clauses.
  • variables: A vector of variables to perform the branching.
  • problem: The problem instance being solved.
  • measure: The measure used for evaluating the problem size reduction in the branches.
  • solver: The solver used for the weighted minimum set cover problem, which can be either LPSolver or IPSolver.

Returns

A OptimalBranchingResult object representing the optimal branching rule.

source
OptimalBranchingCore.reduce_problemMethod
reduce_problem(::Type{R}, problem::AbstractProblem, reducer::AbstractReducer) where R

Reduces the problem size directly, e.g. by graph rewriting. It is a crucial step in the reduce and branch strategy.

Arguments

  • R: The element type used for computing the size of solution. The should have an additive commutative monoid structure.
  • problem: The problem instance.
  • reducer: The reducer.

Returns

A tuple of two values:

  • AbstractProblem: A new instance of AbstractProblem with reduced size.
  • Number: The local gain of the reduction, which will be added to the global gain.
source
OptimalBranchingCore.select_variablesFunction
select_variables(problem::AbstractProblem, measure::AbstractMeasure, selector::AbstractSelector)::Vector{Int}

Selects a branching strategy for a AbstractProblem instance.

Arguments

  • problem: The problem instance.
  • measure: The measure of the problem size.
  • selector: The variables selection strategy, which is a subtype of AbstractSelector.

Returns

A vector of indices of the selected variables.

source
OptimalBranchingCore.weighted_minimum_set_coverMethod
weighted_minimum_set_cover(solver, weights::AbstractVector, subsets::Vector{Vector{Int}}, num_items::Int)

Solves the weighted minimum set cover problem.

Arguments

  • solver: The solver to be used. It can be an instance of LPSolver or IPSolver.
  • weights::AbstractVector: The weights of the subsets.
  • subsets::Vector{Vector{Int}}: A vector of subsets.
  • num_items::Int: The number of elements to cover.

Returns

A vector of indices of selected subsets.

source
OptimalBranchingCore.AbstractMeasureType
AbstractMeasure

The base type for the measure of the problem size in terms of computational hardness. Some widely used measures include the number of variables, the vertices with connectivity of at least 3, etc.

source
OptimalBranchingCore.AbstractReducerType
AbstractReducer

An abstract type representing a reducer in the context of branching problems. This serves as a base type for all specific reducer implementations.

source
OptimalBranchingCore.BranchingStrategyType
BranchingStrategy
BranchingStrategy(; kwargs...)

A struct representing the configuration for a solver, including the reducer and branching strategy.

Fields

  • table_solver::AbstractTableSolver: The solver to resolve the relevant bit strings and generate a branching table.
  • set_cover_solver::AbstractSetCoverSolver = IPSolver(): The solver to solve the set covering problem.
  • selector::AbstractSelector: The selector to select the next branching variable or decision.
  • m::AbstractMeasure: The measure to evaluate the performance of the branching strategy.
source
OptimalBranchingCore.BranchingTableType
BranchingTable{INT <: Integer}

A list of groupped bitstrings, which is used for designing the branching rule. A valid branching rule, which is represented by a logic expression in Disjunctive Normal Form (DNF), should cover at least one bitstring from each group, where by cover, we mean there exists at least one bitstring in the group that satisfies the logic expression. Please check covered_by for more details.

Fields

  • bit_length::Int: The length of the bit string.
  • table::Vector{Vector{INT}}: The list of groupped bitstrings used for branching, where each group is a vector of bitstrings. The bitstrings uses INT type to store the bitstring.
source
OptimalBranchingCore.CandidateClauseType
CandidateClause{INT <: Integer}

A candidate clause is a clause containing the formation related to how it can cover the items in the branching table.

Fields

  • covered_items::Set{Int}: The items in the branching table that are covered by the clause.
  • clause::Clause{INT}: The clause itself.
source
OptimalBranchingCore.ClauseType
Clause{INT <: Integer}

A Clause is conjunction of literals, which is specified by a pair of bit strings. The type parameter INT is the integer type for storing the bit strings.

Fields

  • mask: A bit string that indicates the variables involved in the clause.
  • val: A bit string that indicates the positive literals in the clause.

Examples

To check if a bit string satisfies a clause, use OptimalBranchingCore.covered_by.

julia> using OptimalBranchingCore

julia> clause = Clause(0b1110, 0b1010)
Clause{UInt8}: #2 ∧ ¬#3 ∧ #4

julia> OptimalBranchingCore.covered_by(0b1110, clause)
false

julia> OptimalBranchingCore.covered_by(0b1010, clause)
true
source
OptimalBranchingCore.DNFType
DNF{INT}

A data structure representing a logic expression in Disjunctive Normal Form (DNF), which is a disjunction of one or more conjunctions of literals. In OptimalBranchingCore, a DNF is used to represent the branching rule.

Fields

  • clauses::Vector{Clause{INT}}: A vector of Clause objects.
source
OptimalBranchingCore.IPSolverType
IPSolver <: AbstractSetCoverSolver
IPSolver(; optimizer = HiGHS.Optimizer, max_itr::Int = 5, verbose::Bool = false)

An integer programming solver for set covering problems.

Fields

  • optimizer: The optimizer to be used.
  • max_itr::Int: The maximum number of iterations to be performed.
  • verbose::Bool: Whether to print the solver's output.
source
OptimalBranchingCore.LPSolverType
LPSolver <: AbstractSetCoverSolver
LPSolver(; optimizer = HiGHS.Optimizer, max_itr::Int = 5, verbose::Bool = false)

A linear programming solver for set covering problems.

Fields

  • optimizer: The optimizer to be used.
  • max_itr::Int: The maximum number of iterations to be performed.
  • verbose::Bool: Whether to print the solver's output.
source
OptimalBranchingCore.MaxSizeType
MaxSize

A struct representing the maximum size of a result. (actually a tropical int)

Fields

  • size::Int: The maximum size value.

Constructors

  • MaxSize(size::Int): Creates a MaxSize instance with the specified size.
source
OptimalBranchingCore.MaxSizeBranchCountType
struct MaxSizeBranchCount

Reture both the max size of the results and number of branches.

Fields

  • size::Int: The max size of the results.
  • count::Int: The number of branches of that size.

Constructors

  • MaxSizeBranchCount(size::Int): Creates a MaxSizeBranchCount with the given size and initializes the count to 1.
  • MaxSizeBranchCount(size::Int, count::Int): Creates a MaxSizeBranchCount with the specified size and count.
source
OptimalBranchingCore.OptimalBranchingResultType
OptimalBranchingResult{INT <: Integer}

The result type for the optimal branching rule.

Fields

  • selected_ids::Vector{Int}: The indices of the selected rows in the branching table.
  • optimal_rule::DNF{INT}: The optimal branching rule.
  • branching_vector::Vector{T<:Real}: The branching vector that records the size reduction in each subproblem.
  • γ::Float64: The optimal γ value (the complexity of the branching rule).
source