The Bounded Linear Minimization Oracle (BLMO)
The Bounded Linear Minimization Oracle (BLMO) is an oracle solving the problem
\[\begin{aligned} v \in \arg\min & \; \langle x, d \rangle \\ \text{s.t} & \; x\in C \\ & \; x_I \in \mathbb{Z}^{|I|} \cap [l,u] %\text{ where } x \in X \subset \mathbb{R}, \, x_I \in \mathbb{Z}^{|I|} \end{aligned}\]
where the direction $d$ is usually the gradient evaluated at a certain point. The bounds are specified at the node level and correspond to bounds obtained by branching.
General BLMO Interface
In the following, the functions for the general BLMO interface are listed. Functions without signature needs to be implemented by a new BLMO type. Functions with signature are optional and are usually for statistics and additional safety checks.
Boscia.BLMOStatus
— TypeEnum encoding the status of the Bounded Linear Minimization Oracle. Currently available: OPTIMAL
, INFEASIBLE
and UNBOUNDED
.
Boscia.BoundedLinearMinimizationOracle
— TypeSupertype for the Bounded Linear Minimization Oracles (BLMO).
Boscia.add_bound_constraint!
— FunctionAdd bound constraint.
Boscia.build_LMO_correct
— Methodbuild_LMO_correct(blmo::BoundedLinearMinimizationOracle, node_bounds)
Check if the bounds were set correctly in build_LMO. Safety check only.
Boscia.build_global_bounds
— FunctionRead global bounds from the problem.
Boscia.check_feasibility
— Methodcheck_feasibility(blmo::BoundedLinearMinimizationOracle)
Check if problem is bounded and feasible, i.e. no contradicting constraints.
Boscia.check_infeasible_vertex
— Methodcheck_infeasible_vertex(blmo::BoundedLinearMinimizationOracle, tree)
Deal with infeasible vertex if necessary, e.g. check what caused it etc.
Boscia.delete_bounds!
— FunctionDelete bounds.
Boscia.find_best_solution
— Methodfind_best_solution(f::Function, blmo::BoundedLinearMinimizationOracle, vars, domain_oracle)
Find best solution from the solving process.
Boscia.free_model
— Methodfree_model(blmo::BoundedLinearMinimizationOracle)
Free model data from previous solve (if necessary).
Boscia.get_BLMO_solve_data
— Methodget_BLMO_solve_data(blmo::BoundedLinearMinimizationOracle)
Get solve time, number of nodes and number of iterations, if applicable.
Boscia.get_bound
— FunctionRead bound value for c_idx.
Boscia.get_int_var
— FunctionGet the index of the integer variable the bound is working on.
Boscia.get_integer_variables
— FunctionGet list of integer variables.
Boscia.get_list_of_variables
— FunctionGet list of variables indices. If the problem has n variables, they are expected to contiguous and ordered from 1 to n.
Boscia.get_lower_bound_list
— FunctionGet the list of lower bounds.
Boscia.get_tol
— Methodget_tol(blmo::BoundedLinearMinimizationOracle)
Get solving tolerance for the BLMO.
Boscia.get_upper_bound_list
— FunctionGet the list of upper bounds.
Boscia.get_variables_pointers
— Methodget_variables_pointers(blmo::BoundedLinearMinimizationOracle, tree)
List of all variable pointers. Depends on how you save your variables internally. In the easy case, this is simply collect(1:N)
. Is used in find_best_solution
.
Boscia.has_integer_constraint
— FunctionHas variable an integer constraint?
Boscia.indicator_present
— Methodindicator_present(blmo::BoundedLinearMinimizationOracle)
Are indicator constraints present?
Boscia.is_bound_in
— FunctionTo check if there is bound for the variable in the global or node bounds.
Boscia.is_constraint_on_int_var
— FunctionCheck if the subject of the bound cidx is an integer variable (recorded in intvars).
Boscia.is_indicator_feasible
— Methodis_indicator_feasible(blmo::BoundedLinearMinimizationOracle, v; atol=1e-6, rtol=1e-6)
Is a given point v indicator feasible, i.e. meets the indicator constraints? If applicable.
Boscia.is_inface_feasible
— MethodIs a given point a on the minimal face containing the given x?
Boscia.is_linear_feasible
— FunctionIs a given point v linear feasible for the model? That means does v satisfy all bounds and other linear constraints?
Boscia.is_valid_split
— Methodis_valid_split(tree::Bonobo.BnBTree, blmo::BoundedLinearMinimizationOracle, vidx::Int)
Check whether a split is valid, i.e. the upper and lower on variable vidx are not the same.
Boscia.set_bound!
— FunctionChange the value of the bound c_idx.
FrankWolfe.compute_extreme_point
— FunctionImplement FrankWolfe.compute_extreme_point
Given a direction d solves the problem min_x d^T x
where x has to be an integer feasible point
FrankWolfe.compute_inface_extreme_point
— MethodImplement FrankWolfe.compute_inface_extreme_point
Given a direction d and feasible point x solves the problem min_a d^T a where a has to be an integer feasible point and on the minimal face containing x
FrankWolfe.dicg_maximum_step
— MethodImplement FrankWolfe.dicg_maximum_step
Given a direction d and feasible point x solves the problem argmax_γ (x - γ * d) ∈ P where P is feasible set
FrankWolfe.is_decomposition_invariant_oracle
— MethodImplement FrankWolfe.is_decomposition_invariant_oracle
Check if necessary DICG-specific orcales are implemented.
MathOptInterface (MOI) BLMO
With this BLMO type, any (MIP) solver that provides an interface to MathOptInterface
and JuMP
can be used in Boscia. Note that we only require the feasible region, i.e. no objective has to be set.
Boscia.MathOptBLMO
— MethodMathOptBLMO(lmo::FrankWolfe.MathOptLMO)
Build an instance of MathOptBLMO
from a FrankWolfe.MathOptLMO
.
Base.convert
— MethodConvert object of Type FrankWolfe.MathOptLMO
into Boscia.MathOptBLMO
and viceversa.
Bonobo.get_branching_variable
— MethodBonobo.get_branching_variable(tree::Bonobo.BnBTree, branching::PartialStrongBranching{MathOptBLMO{OT}}, node::Bonobo.AbstractNode,) where {OT<:MOI.AbstractOptimizer}
Behavior for strong branching. Note that in constrast to the ManagedBLMO
type, we filter out the integer and binary constraints as solving general MIP in strong branching would be very expensive.
Boscia.add_bound_constraint!
— Methodadd_bound_constraint!(blmo::MathOptBLMO, key, value, sense::Symbol)
Add bound constraint.
Boscia.build_LMO_correct
— Methodbuild_LMO_correct(blmo, node_bounds)
Check if the bounds were set correctly in build_LMO. Safety check only.
Boscia.build_global_bounds
— Methodbuild_global_bounds(blmo::MathOptBLMO, integer_variables)
Read global bounds from the problem
Boscia.check_feasibility
— Methodcheck_feasibility(blmo::MathOptBLMO)
Check if problem is bounded and feasible, i.e. no contradicting constraints.
Boscia.check_infeasible_vertex
— Method check_infeasible_vertex(blmo::MathOptBLMO, tree)
Deal with infeasible vertex if necessary, e.g. check what caused it etc.
Boscia.delete_bounds!
— Methoddelete_bounds!(blmo::MathOptBLMO, cons_delete)
Delete bounds.
Boscia.explicit_bounds_binary_var
— Methodexplicit_bounds_binary_var(blmo::MathOptBLMO, global_bounds::IntegerBounds)
Add explicit bounds for binary variables.
Boscia.find_best_solution
— Methodfind_best_solution(f::Function, blmo::MathOptBLMO, vars, domain_oracle)
Find best solution from the solving process.
Boscia.find_best_solution
— Method function find_best_solution(f::Function, o::MOI.AbstractOptimizer, vars::Vector{MOI.VariableIndex}, domain_oracle,)
Finds the best solution in the Optimizer's solution storage, based on the objective function f
. Returns the solution vector and the corresponding best value.
Boscia.free_model
— Method free_model(blmo::MathOptBLMO)
Free model data from previous solve (if necessary).
Boscia.get_BLMO_solve_data
— Methodget_BLMO_solve_data(blmo::MathOptBLMO)
Get solve time, number of nodes and number of simplex iterations.
Boscia.get_binary_variables
— Methodget_binary_variables(blmo::MathOptBLMO)
Get list of binary variables.
Boscia.get_bound
— Methodget_bound(blmo::MathOptBLMO, c_idx, sense::Symbol)
Read bound value for c_idx.
Boscia.get_int_var
— Method get_int_var(blmo::MathOptBLMO, c_idx)
Get the index of the integer variable the bound is working on.
Boscia.get_integer_variables
— Method get_integer_variables(blmo::MathOptBLMO)
Get list of integer variables.
Boscia.get_list_of_variables
— Methodget_list_of_variables(blmo::MathOptBLMO)
Get list of variables indices and the total number of variables. If the problem has n variables, they are expected to contiguous and ordered from 1 to n.
Boscia.get_lower_bound_list
— Methodget_lower_bound_list(blmo::MathOptBLMO)
Get the list of lower bounds.
Boscia.get_tol
— Method get_tol(blmo::MathOptBLMO)
Get solving tolerance for the BLMO.
Boscia.get_upper_bound_list
— Methodget_upper_bound_list(blmo::MathOptBLMO)
Get the list of upper bounds.
Boscia.get_variables_pointers
— Method get_variables_pointers(blmo::MathOptBLMO, tree)
List of all variable pointers. Depends on how you save your variables internally. Is used in find_best_solution
.
Boscia.has_binary_constraint
— Methodhas_binary_constraint(blmo::MathOptBLMO, idx::Int)
Has variable a binary constraint?
Boscia.has_integer_constraint
— Methodhas_integer_constraint(blmo::MathOptBLMO, idx::Int)
Does the variable have an integer constraint?
Boscia.indicator_present
— Methodindicator_present(blmo::MathOptBLMO)
Are indicator constraints present?
Boscia.is_bound_in
— Methodis_bound_in(blmo::MathOptBLMO, c_idx, bounds)
To check if there is bound for the variable in the global or node bounds.
Boscia.is_constraint_on_int_var
— Methodis_constraint_on_int_var(blmo::MathOptBLMO, c_idx, int_vars)
Check if the subject of the bound cidx is an integer variable (recorded in intvars).
Boscia.is_indicator_feasible
— Methodis_indicator_feasible(blmo::MathOptBLMO, v; atol=1e-6, rtol=1e-6)
Is a given point v indicator feasible, i.e. meets the indicator constraints? If applicable.
Boscia.is_linear_feasible
— Methodis_linear_feasible(blmo::MathOptBLMO, v::AbstractVector)
Is a given point v linear feasible for the model?
Boscia.is_valid_split
— Methodis_valid_split(tree::Bonobo.BnBTree, blmo::MathOptBLMO, vidx::Int)
Check whether a split is valid, i.e. the upper and lower on variable vidx are not the same.
Boscia.set_bound!
— Methodset_bound!(blmo::MathOptBLMO, c_idx, value, sense::Symbol)
Change the value of the bound c_idx.
Boscia.solve
— MethodThe solve
function receiving a FrankWolfe.MathOptLMO
. Converts the lmo into an instance of Boscia.MathOptBLMO
and calls the main solve
function.
FrankWolfe.compute_extreme_point
— Methodcompute_extreme_point(blmo::MathOptBLMO, d; kwargs...)
Is implemented in the FrankWolfe package in file "moi_oracle.jl".
Managed BLMO
Sometimes the linear problem over the feasible region can be computed via a combinatorial algorithm that is more efficient than formulating the problem as a MIP. If one does not want to implement the BLMO interface from scratch which requires multiple methods to be provided, we provide the ManagedBLMO
. It handles the bound management, so that the user has to implement only a few methods.
Boscia.ManagedBoundedLMO
— TypeManagedBoundedLMO{SBLMO<:SimpleBoundableLMO} <: BoundedLinearMinimizationOracle
A Bounded Linear Minimization Oracle that manages the bounds.
simple_lmo
an LMO of type Simple Boundable LMO.lower_bounds
list of lower bounds for the integer variables recorded inint_vars
. If there is no specific lower bound, set corresponding entry to-Inf
.upper_bounds
list of upper bounds for the integer variables recorded inint_vars
. If there is no specific upper bound, set corresponding entry toInf
.n
total number of variables.int_vars
list of indices of the integer variables.solving_time
the time to evaluatecompute_extreme_point
.
Boscia.SimpleBoundableLMO
— TypeSimpleBoundableLinearMinimizationOracle
A "simple" BLMO that computes the extreme point given a linear objective and the node specific bounds on the integer variables. Can be stateless since all of the bound management is done by the ManagedBoundedLMO
.
Boscia.bounded_compute_extreme_point
— Functionbounded_compute_extreme_point
Computes the extreme point given an direction d
, the current lower and upper bounds on the integer variables, and the set of indices of integer variables.
Boscia.is_simple_linear_feasible
— Functionis_simple_linear_feasible
Checks whether a given point v
is satisfying the constraints on the problem. Note that the bounds on the integer variables are being checked by the ManagedBoundedLMO and do not have to be check here.
Polytopes
Here are some preimplemented polytopes. We have the hypercube, the unit simplex and the probability simplex.
Boscia.CubeSimpleBLMO
— TypeCubeSimpleBLMO{T}(lower_bounds, upper_bounds)
Hypercube with lower and upper bounds implementing the SimpleBoundableLMO
interface.
Boscia.ProbabilitySimplexSimpleBLMO
— TypeProbablitySimplexSimpleBLMO(N)
The scaled probability simplex with ∑ x = N
.
Boscia.UnitSimplexSimpleBLMO
— TypeUnitSimplexSimpleBLMO(N)
The scaled unit simplex with ∑ x ≤ N
.
Boscia.bounded_compute_extreme_point
— Method bounded_compute_extreme_point(sblmo::CubeSimpleBLMO, d, lb, ub, int_vars; kwargs...)
If the entry is positve, choose the lower bound. Else, choose the upper bound.
Boscia.bounded_compute_extreme_point
— Methodbounded_compute_extreme_point(sblmo::ProbabilitySimplexSimpleBLMO, d, lb, ub, int_vars; kwargs...)
Assign the largest possible values to the entries corresponding to the smallest entries of d.
Boscia.bounded_compute_extreme_point
— Methodbounded_compute_extreme_point(sblmo::UnitSimplexSimpleBLMO, d, lb, ub, int_vars; kwargs...)
For all positive entries of d, assign the corresponding lower bound. For non-positive entries, assign largest possible value in increasing order.
Boscia.bounded_compute_inface_extreme_point
— MethodIf the entry in x is at the boundary, choose the corresponding bound. Otherwise, if the entry in direction is positve, choose the lower bound. Else, choose the upper bound.
Boscia.bounded_compute_inface_extreme_point
— MethodFix the corresponding entries to the boudary based on the given x. Assign the largest possible values to the unfixed entries corresponding to the smallest entries of d.
Boscia.bounded_compute_inface_extreme_point
— MethodFor boundary entries of x, assign the corresponding boudary. For all positive entries of d, assign the corresponding lower bound. For non-positive entries, assign largest possible value in increasing order.
Boscia.bounded_dicg_maximum_step
— MethodCompute the maximum step size for each entry and return the minium of all the possible step sizes.
Boscia.bounded_dicg_maximum_step
— MethodCompute the maximum step size for each entry and return the minium of all the possible step sizes.
Boscia.bounded_dicg_maximum_step
— MethodCompute the maximum step size for each entry and the sum of entries should satisfy inequality constraint. Return the minium of all the possible step sizes.
Boscia.rounding_hyperplane_heuristic
— Method rounding_hyperplane_heuristic(tree::Bonobo.BnBTree, tlmo::TimeTrackingLMO{ManagedBoundedLMO{ProbabilitySimplexSimpleBLMO}}, x)
Hyperplane-aware rounding for the probability simplex.
Boscia.rounding_hyperplane_heuristic
— Methodrounding_hyperplane_heuristic(tree::Bonobo.BnBTree, tlmo::TimeTrackingLMO{ManagedBoundedLMO{UnitSimplexSimpleBLMO}}, x)
Hyperplane-aware rounding for the unit simplex.
Time Tracking BLMO Wrapper
This wrapper keeps track of the statistics like solving time of the BLMO, the number of calls etc.
Boscia.TimeTrackingLMO
— TypeTimeTrackingLMO{BLMO<:BoundedLinearMinimizationOracle} <: FrankWolfe.LinearMinimizationOracle
A wrapper for the BLMO tracking the solving time, number of calls etc. Is created in Boscia itself.
Boscia.TimeTrackingLMO
— MethodTimeTrackingLMO(blmo::BoundedLinearMinimizationOracle, int_vars)
Constructor with just the blmo.
Boscia.TimeTrackingLMO
— MethodTimeTrackingLMO(blmo::BoundedLinearMinimizationOracle)
Constructor with just the blmo.
Boscia.reset!
— Methodreset!(tlmo::TimeTrackingLMO)
If we want to reset the info between nodes in the Branch-and-Bound tree.
FrankWolfe.compute_extreme_point
— MethodFrankWolfe.compute_extreme_point(tlmo::TimeTrackingLMO, d; kwargs...)
Compute the extreme point and collect statistics.
Build LMO
Given the global bounds on the integer variables and the bounds at the node level, this builds the BLMO instance for the specific node. This way, the BLMO can be stored in the tree as opposed to every node having a copy of it.
Boscia.build_LMO
— MethodBuild node LMO from global LMO
Four action can be taken:
- KEEP constraint is as saved in the global bounds
- CHANGE lower/upper bound is changed to the node specific one
- DELETE custom bound from the previous node that is invalid at current node and has to be deleted
- ADD bound has to be added for this node because it does not exist in the global bounds (e.g. variable bound is a half open interval globally)
Integer Bounds
The data structure that records the bounds on the integer/binary variables.
Boscia.IntegerBounds
— TypeIntegerBounds
Keeps track of the bounds of the integer (binary) variables.
lower_bounds
dictionary of Float64, index is the key.upper_bounds
dictionary of Float64, index is the key.