📄 bmibnb.m
字号:
function output = bmibnb(p)
%BMIBNB Branch-and-bound scheme for bilinear programs
%
% BMIBNB is never called by the user directly, but is called by YALMIP from
% SOLVESDP, by choosing the solver tag 'bmibnb' in sdpsettings
%
% The behaviour of BMIBNB can be altered using the fields in the field
% 'bmibnb' in SDPSETTINGS
%
% bmibnb.lowersolver - Solver for lower bound [solver tag ('')]
% bmibnb.uppersolver - Solver for upper bound [solver tag ('')]
% bmibnb.lpsolver - Solver for LP bound tightening [solver tag ('')]
% bmibnb.branchmethod - Branch strategy ['maxvol' | 'best' ('best')]
% bmibnb.branchrule - Branch position ['omega' | 'bisect' ('omega')]
% bmibnb.lpreduce - Improve variable bounds using LP [ real [0,1] (0 means no reduction, 1 means all variables)
% bmibnb.lowrank - Partition variables into two disjoint sets and branch on smallest [ 0|1 (0)]
% bmibnb.target - Exit if upper bound<target [double (-inf)]
% bmibnb.roottight - Improve variable bounds in root using full problem [ 0|1 (1)]
% bmibnb.vartol - Cut tree when x_U-x_L < vartol on all branching variables
% bmibnb.relgaptol - Tolerance on relative objective error (UPPER-LOWER)/(1+|UPPER|) [real (0.01)]
% bmibnb.absgaptol - Tolerance on objective error (UPPER-LOWER) [real (0.01)]
% bmibnb.pdtol - A number is declared non-negative if larger than...[ double (-1e-6)]
% bmibnb.eqtol - A number is declared zero if abs(x) smaller than...[ double (1e-6)]
% bmibnb.maxiter - Maximum number nodes [int (100)]
% bmibnb.maxtime - Maximum CPU time (sec.) [int (3600)]
% Author Johan L鰂berg
% $Id: bmibnb.m,v 1.76 2008/02/22 15:38:17 joloef Exp $
bnbsolvertime = clock;
showprogress('Branch and bound started',p.options.showprogress);
% *************************************************************************
% INITIALIZE DIAGNOSTICS AND DISPLAY LOGICS (LOWER VERBOSE IN SOLVERS)
% *************************************************************************
switch max(min(p.options.verbose,3),0)
case 0
p.options.bmibnb.verbose = 0;
case 1
p.options.bmibnb.verbose = 1;
p.options.verbose = 0;
case 2
p.options.bmibnb.verbose = 2;
p.options.verbose = 0;
case 3
p.options.bmibnb.verbose = 2;
p.options.verbose = 1;
otherwise
p.options.bmibnb.verbose = 0;
p.options.verbose = 0;
end
if ~isempty(p.F_struc)
if any(isnan(p.F_struc) | isinf(p.F_struc))
output = yalmip_default_output;
output.problem = 1;
output.solved_nodes = 0;
output.Primal = zeros(length(p.c),1);
output.infostr = yalmiperror(output.problem,'BNB');
output.solvertime = 0;
output.lower = inf;
return
end
end
% *************************************************************************
% Assume feasible (this property can be changed in the presolve codes
% *************************************************************************
p.feasible = 1;
% *************************************************************************
% Ensure that bond propagation is performed
% *************************************************************************
p.changedbounds = 1;
% *************************************************************************
% Some preprocessing to put the model in a better format
% *************************************************************************
p = convert_perspective_log(p);
n_in = length(p.c);
% *************************************************************************
% Extract bounds from model using direct information available
% *************************************************************************
p = presolve_bounds_from_domains(p);
p = presolve_bounds_from_modelbounds(p);
p = update_eval_bounds(p);
p = update_monomial_bounds(p);
% *************************************************************************
% Improve the bounds by performing some standard presolve
% *************************************************************************
p = presolve_bounds_from_equalities(p);
p = update_eval_bounds(p);
p = update_monomial_bounds(p);
p = presolve_bounds_from_inequalities(p);
p = update_eval_bounds(p);
p = update_monomial_bounds(p);
% *************************************************************************
% For quadratic nonconvex programming over linear constraints, we
% diagonalize the problem to obtain less number of bilinear terms. Not
% theoretically any reason to do so, but practical performance is better
% *************************************************************************
p = diagonalize_quadratic_program(p);
% *************************************************************************
% Try to generate a feasible solution, by using avialable x0 (if usex0=1),
% or by trying the zero solution, or my trying the (lb+ub)/2 solution. Note
% that we do this here before the problem possibly is bilinearized, thus
% avoiding to introduce possibly complicating bilinear constraints
% *************************************************************************
p = compile_nonlinear_table(p);
[p,x_min,upper] = initializesolution(p);
% *************************************************************************
% If the upper bound solver is capable of solving the original problem,
% without bilinearizing it first, we might try to get a local solution,
% possibly based on our initial crude solution
% *************************************************************************
if solver_can_solve(p.solver.uppersolver,p) & any(p.variabletype>2)
p.high_monom_model = [];
p = build_recursive_scheme(p);
p = compile_nonlinear_table(p);
p = preprocess_bilinear_bounds(p);
p = update_eval_bounds(p);
p = updateboundsfromupper(p,upper);
p = propagatecomplementary(p);
p = updatemonomialbounds(p);
p = presolve_bounds_from_equalities(p);
p = updatemonomialbounds(p);
p = updatemonomialbounds(p);
p = update_eval_bounds(p);
[upper,p.x0,info_text,numglobals] = solve_upper_in_node(p,p,x_min,upper,x_min,p.solver.uppersolver.call,'',0);
if numglobals > 0
x_min = p.x0;
end
end
if isempty(p.x0)
p.x0 = zeros(length(p.c),1);
end
% *************************************************************************
% Sigmonial terms are converted to evaluation based expressions.
% *************************************************************************
p = convert_sigmonial_to_sdpfun(p);
%p = convert_polynomial_to_sdpfun(p);
% *************************************************************************
% The bilinear solver does not support non-quadratic models
% Hence, we need to bilinearize the problem. However, the upper bound
% solver might support general problems, so we should keep a copy of the
% original problem also in that case
% *************************************************************************
[p_bilin,changed] = convert_polynomial_to_quadratic(p);
if (p.solver.uppersolver.constraint.equalities.polynomial & p.solver.uppersolver.objective.polynomial)
p_bilin.originalModel = p;
p = p_bilin;
else
p = p_bilin;
p.originalModel = p_bilin;
end
% *************************************************************************
% Build an evaluation tree for computing nonlinear terms when running the
% upper-bound solver, typically fmincon. Operators have to be applied in
% a particular order, to cope with terms such as sin(cos(x^2)^2)
p.originalModel = build_recursive_scheme(p.originalModel);
% *************************************************************************
% Fast derivbation of upper and lower bounds also requires an evaluation
% order
p = build_recursive_scheme(p);
% *************************************************************************
% Pre-calc lists of linear/bilinear/nonlinear variables (we have bilineared
% the model now, so the old precompiled table could be wrong
% *************************************************************************
p = compile_nonlinear_table(p);
% *************************************************************************
% Select branch variables. We should branch on all variables that are
% involved in bilinear terms.
% *************************************************************************
p.branch_variables = decide_branch_variables(p);
p.branch_variables = setdiff(p.branch_variables,p.evalVariables);
% *************************************************************************
% Tighten bounds (might be useful after bilinearization?)
% *************************************************************************
p = preprocess_bilinear_bounds(p);
p = update_eval_bounds(p);
% *************************************************************************
% Now reduce the branch variables by removing bilinear terms that only have
% been introduced due to a convex quadratic objective
% *************************************************************************
p = reduce_bilinear_branching_variables(p);
% *************************************************************************
% Simple pre-solve loop. The loop is needed for variables defined as w =
% x*y, x = t*u,y=..
% ******************************************[******************************
p = presolveloop(p,upper);
% while goon
% start = [p.lb;p.ub];
% i = i+1;
% p = updateboundsfromupper(p,upper);
% p = propagatecomplementary(p);
% p = updatemonomialbounds(p);
% p = presolve_bounds_from_equalities(p);
% p = updatemonomialbounds(p);
% p = updatemonomialbounds(p);
% p = update_eval_bounds(p);
% i = i+1;
% goon = (norm(start-[p.lb;p.ub],'inf') > 1e-2) & i < 150;
% start = [p.lb;p.ub];
% end
% *************************************************************************
% Try a little more root node tigthening
% *************************************************************************
close = find(abs(p.lb - p.ub) < 1e-12);
p.lb(close) = (p.lb(close)+p.ub(close))/2;
p.ub(close) = p.lb(close);
p = root_node_tighten(p,upper);
p = updatemonomialbounds(p);
p = update_eval_bounds(p);
p = presolve_bounds_from_equalities(p);
p = updatemonomialbounds(p);
output = yalmip_default_output;
% Detect complementary constraints
p.complementary = [];
complementary = find(p.lb==0 & p.ub==0 & p.variabletype'==1);
if ~isempty(complementary)
for i = 1:length(complementary)
index = find(p.bilinears(:,1) == complementary(i));
x = p.bilinears(index,2);
y = p.bilinears(index,3);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -