📄 bnb.m
字号:
function output = bnb(p)
%BNB General branch-and-bound scheme for conic programs
%
% BNB applies a branch-and-bound scheme to solve mixed integer
% conic programs (LP, QP, SOCP, SDP) and mixed integer geometric programs.
%
% BNB is never called by the user directly, but is called by
% YALMIP from SOLVESDP, by choosing the solver tag 'bnb' in sdpsettings.
%
% BNB is used if no other mixed integer solver is found, and
% is only useful for very small problems, due to its simple
% and naive implementation.
%
% The behaviour of BNB can be altered using the fields
% in the field 'bnb' in SDPSETTINGS
%
% bnb.branchrule Deceides on what variable to branch
% 'max' : Variable furthest away from being integer
% 'min' : Variable closest to be being integer
% 'first' : First variable (lowest variable index in YALMIP)
% 'last' : Last variable (highest variable index in YALMIP)
% 'weight' : See manual
%
% bnb.method Branching strategy
% 'depth' : Depth first
% 'breadth' : Breadth first
% 'best' : Expand branch with lowest lower bound
% 'depthX' : Depth until integer solution found, then X (e.g 'depthbest')
%
% solver Solver for the relaxed problems (standard solver tag, see SDPSETTINGS)
%
% maxiter Maximum number of nodes explored
%
% inttol Tolerance for declaring a variable as integer
%
% feastol Tolerance for declaring constraints as feasible
%
% gaptol Exit when (upper bound-lower bound)/(1e-3+abs(lower bound)) < gaptol
%
% round Round variables smaller than bnb.inttol
%
%
% See also SOLVESDP, BINVAR, INTVAR, BINARY, INTEGER
% Author Johan L鰂berg
% $Id: bnb.m,v 1.54 2008/02/19 15:58:16 joloef Exp $
% ********************************
%% INITIALIZE DIAGNOSTICS IN YALMIP
% ********************************
%
% p.c(23) = 64;
% p.Q=p.Q*0;
% p.monomtable(end+1,7)=1;
% p.monomtable(end+1,8)=1;
% p.lb(end+1)=-inf;
% p.ub(end+1)=inf;
% p.F_struc(end,end+1)=0;
% p.Q(end+1,end+1)=0;
bnbsolvertime = clock;
showprogress('Branch and bound started',p.options.showprogress);
% ********************************
%% We might have a GP : pre-calc
% ********************************
p.nonlinear = find(~(sum(p.monomtable~=0,2)==1 & sum(p.monomtable,2)==1));
p.nonlinear = union(p.nonlinear,p.evalVariables);
% ********************************
% This field is only used in bmibnb, which uses the same sub-functions as
% bnb
% ********************************
p.high_monom_model = [];
% ********************************
%% Define infinite bounds
% ********************************
if isempty(p.ub)
p.ub = repmat(inf,length(p.c),1);
end
if isempty(p.lb)
p.lb = repmat(-inf,length(p.c),1);
end
% ********************************
%% Extract bounds from model
% ********************************
if ~isempty(p.F_struc)
[lb,ub,used_rows_eq,used_rows_lp] = findulb(p.F_struc,p.K);
if ~isempty(used_rows_lp)
used_rows_lp = used_rows_lp(~any(full(p.F_struc(p.K.f + used_rows_lp,1+p.nonlinear)),2));
if ~isempty(used_rows_lp)
lower_defined = find(~isinf(lb));
if ~isempty(lower_defined)
p.lb(lower_defined) = max(p.lb(lower_defined),lb(lower_defined));
end
upper_defined = find(~isinf(ub));
if ~isempty(upper_defined)
p.ub(upper_defined) = min(p.ub(upper_defined),ub(upper_defined));
end
p.F_struc(p.K.f + used_rows_lp,:)=[];
p.K.l = p.K.l - length(used_rows_lp);
end
end
if ~isempty(used_rows_eq)
used_rows_eq = used_rows_eq(~any(full(p.F_struc(used_rows_eq,1+p.nonlinear)),2));
if ~isempty(used_rows_eq)
lower_defined = find(~isinf(lb));
if ~isempty(lower_defined)
p.lb(lower_defined) = max(p.lb(lower_defined),lb(lower_defined));
end
upper_defined = find(~isinf(ub));
if ~isempty(upper_defined)
p.ub(upper_defined) = min(p.ub(upper_defined),ub(upper_defined));
end
p.F_struc(used_rows_eq,:)=[];
p.K.f = p.K.f - length(used_rows_eq);
end
end
%
% [lb,ub,used_rows_eq,used_rows_lp] = findulb(p.F_struc,p.K);
% if ~isempty(used_rows)
% used_rows = used_rows(~any(full(p.F_struc(used_rows,1+p.nonlinear)),2));
% if ~isempty(used_rows)
% lower_defined = find(~isinf(lb));
% if ~isempty(lower_defined)
% p.lb(lower_defined) = max(p.lb(lower_defined),lb(lower_defined));
% end
% upper_defined = find(~isinf(ub));
% if ~isempty(upper_defined)
% p.ub(upper_defined) = min(p.ub(upper_defined),ub(upper_defined));
% end
% p.F_struc(p.K.f+used_rows,:)=[];
% p.K.l = p.K.l - length(used_rows);
% end
% end
end
% ********************************
%% ADD CONSTRAINTS 0<x<1 FOR BINARY
% ********************************
if ~isempty(p.binary_variables)
p.ub(p.binary_variables) = min(p.ub(p.binary_variables),1);
p.lb(p.binary_variables) = max(p.lb(p.binary_variables),0);
godown = find(p.ub(p.binary_variables) < 1);%-p.options.bnb.inttol);
goup = find(p.lb(p.binary_variables) > 0);%p.options.bnb.inttol);
p.ub(p.binary_variables(godown)) = 0;
p.lb(p.binary_variables(goup)) = 1;
end
p.lb(p.integer_variables) = fix(p.lb(p.integer_variables));
p.ub(p.integer_variables) = fix(p.ub(p.integer_variables));
% Could be some nonlinear terms (although these problems are recommended to
% be solved using BMIBNB
p = compile_nonlinear_table(p);
p = updatemonomialbounds(p);
% *******************************
%% PRE-SOLVE (nothing fancy coded)
% *******************************
pss=[];
p = presolve_bounds_from_equalities(p);
if isempty(p.nonlinear)
%if isempty(find(isinf([p.ub;p.lb]))) & p.K.l>0 & isempty(p.nonlinear)
if p.K.f>0
Aeq = -p.F_struc(1:p.K.f,2:end);
beq = p.F_struc(1:p.K.f,1);
A = [Aeq;-Aeq];
b = [beq;-beq];
[p.lb,p.ub,redundant,pss] = tightenbounds(A,b,p.lb,p.ub,p.integer_variables,p.binary_variables,ones(length(p.lb),1));
end
pss=[];
if p.K.l>0
A = -p.F_struc(1+p.K.f:p.K.f+p.K.l,2:end);
b = p.F_struc(1+p.K.f:p.K.f+p.K.l,1);
[p.lb,p.ub,redundant,pss] = tightenbounds(A,b,p.lb,p.ub,p.integer_variables,p.binary_variables,ones(length(p.lb),1));
if length(redundant)>0
pss.AL0A(redundant,:)=[];
pss.AG0A(redundant,:)=[];
p.F_struc(p.K.f+redundant,:)=[];
p.K.l = p.K.l - length(redundant);
end
end
end
% Silly redundancy
p = updatemonomialbounds(p);
p = presolve_bounds_from_equalities(p);
if p.K.l > 0
b = p.F_struc(1+p.K.f:p.K.l+p.K.f,1);
A = -p.F_struc(1+p.K.f:p.K.l+p.K.f,2:end);
redundant = find(((A>0).*A*(p.ub-p.lb) - (b-A*p.lb) <= 0));
if ~isempty(redundant)
p.F_struc(p.K.f + redundant,:) = [];
p.K.l = p.K.l - length(redundant);
end
end
% *******************************
%% PERTURBATION OF LINEAR COST
% *******************************
p.corig = p.c;
if nnz(p.Q)==0 & isequal(p.K.m,0)
g = randn('seed');
randn('state',1253); %For my testing, I keep this the same...
% This perturbation has to be better. Crucial for many real LP problems
p.c = (p.c).*(1+randn(length(p.c),1)*1e-4);
randn('seed',g);
end
% *******************************
%% Display logics
% 0 : Silent
% 1 : Display branching
% 2 : Display node solver prints
% *******************************
switch max(min(p.options.verbose,3),0)
case 0
p.options.bnb.verbose = 0;
case 1
p.options.bnb.verbose = 1;
p.options.verbose = 0;
case 2
p.options.bnb.verbose = 2;
p.options.verbose = 0;
case 3
p.options.bnb.verbose = 2;
p.options.verbose = 1;
otherwise
p.options.bnb.verbose = 0;
p.options.verbose = 0;
end
% *******************************
%% Figure out the weights if any
% *******************************
try % Probably buggy first version...
if ~isempty(p.options.bnb.weight)
weightvar = p.options.bnb.weight;
if isa(weightvar,'sdpvar')
if (prod(size(weightvar)) == 1)
weight = ones(length(p.c),1);
for i = 1:length(p.c)
weight(i,1) = full(getbasematrix(weightvar,p.used_variables(i)));
end
p.weight = weight;
else
error('Weight should be an SDPVAR scalar');
end
else
error('Weight should be an SDPVAR scalar');
end
else
p.weight = ones(length(p.c),1);
% p.weight(p.binary_variables) = (1./(1:length(p.binary_variables)));
end
catch
disp('Something wrong with weights. Please report bug');
p.weight = ones(length(p.c),1);
end
% *******************************
%% START BRANCHING
% *******************************
setuptime = etime(clock,bnbsolvertime);
bnbsolvertime = clock;
[x_min,solved_nodes,lower,upper,profile] = branch_and_bound(p,pss);
bnbsolvertime = etime(clock,bnbsolvertime);
output.solvertime = setuptime + bnbsolvertime;
% **********************************
%% CREATE SOLUTION
% **********************************
output.problem = 0;
if isinf(upper)
output.problem = 1;
end
if isinf(-lower)
output.problem = 2;
end
if solved_nodes == p.options.bnb.maxiter
output.problem = 3;
end
output.solved_nodes = solved_nodes;
output.Primal = x_min;
output.Dual = [];
output.Slack = [];
output.infostr = yalmiperror(output.problem,'BNB');
output.solverinput = 0;
if p.options.savesolveroutput
output.solveroutput.setuptime = setuptime;
output.solveroutput.localsolvertime = profile.local_solver_time;
output.solveroutput.branchingtime = bnbsolvertime;
output.solveroutput.solved_nodes = solved_nodes;
output.solveroutput.lower = lower;
output.solveroutput.upper = upper;
else
output.solveroutput =[];
end
%% --
function [x_min,solved_nodes,lower,upper,profile] = branch_and_bound(p,pss)
% *******************************
%% We don't need this
% *******************************
p.options.savesolverinput = 0;
p.options.savesolveroutput = 0;
p.options.saveduals = 0;
p.options.dimacs = 0;
% *******************************
% Tracking performance etc
% *******************************
profile.local_solver_time = 0;
% *******************************
%% SET-UP ROOT PROBLEM
% *******************************
p.depth = 0;
p.lower = NaN;
% Does the user want to create his own initial guess
if p.options.usex0
[x_min,upper] = initializesolution(p);
if isinf(upper)
% Try to initialize to lowerbound+ upperbound. fmincon really
% doesn't like zero initial guess, despite having bounds available
x_min = zeros(length(p.c),1);
violates_finite_bounds = ((x_min < p.lb) | (x_min < p.ub));
violates_finite_bounds = find(violates_finite_bounds & ~isinf(p.lb) & ~isinf(p.ub));
x_min(violates_finite_bounds) = (p.lb(violates_finite_bounds) + p.ub(violates_finite_bounds))/2;
x_min = setnonlinearvariables(p,x_min);
end
p.x0 = x_min;
else
upper = inf;
x_min = zeros(length(p.c),1);
violates_finite_bounds = ((x_min < p.lb) | (x_min < p.ub));
violates_finite_bounds = find(violates_finite_bounds & ~isinf(p.lb) & ~isinf(p.ub));
x_min(violates_finite_bounds) = (p.lb(violates_finite_bounds) + p.ub(violates_finite_bounds))/2;
x_min = setnonlinearvariables(p,x_min);
p.x0 = x_min;
end
% *******************************
%% Global stuff
% *******************************
lower = NaN;
stack = [];
% *******************************
%% Create function handle to solver
% *******************************
lowersolver = p.solver.lower.call;
uppersolver = p.options.bnb.uppersolver;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -