📄 bnb.m
字号:
% *******************************
%% INVARIANT PROBLEM DATA
% *******************************
c = p.corig;
Q = p.Q;
f = p.f;
integer_variables = p.integer_variables;
solved_nodes = 0;
gap = inf;
node = 1;
if p.options.bnb.presolve
savec = p.c;
saveQ = p.Q;
p.Q = p.Q*0;
n = length(p.c);
saveBinary = p.binary_variables;
saveInteger = p.integer_variables;
p.binary_variables = [];
p.integer_variables = [];;
for i = 1:length(c)
p.c = eyev(n,i);
output = feval(lowersolver,p);
if output.problem == 0
p.lb(i) = max(p.lb(i),output.Primal(i));
end
p.c = -eyev(n,i);
output = feval(lowersolver,p);
if output.problem == 0
p.ub(i) = min(p.ub(i),output.Primal(i));
end
p.lb(saveBinary) = ceil(p.lb(saveBinary)-1e-3);
p.ub(saveBinary) = floor(p.ub(saveBinary)+1e-3);
end
p.binary_variables = saveBinary;
p.integer_variables = saveInteger;
p.Q = saveQ;
p.c = savec;
end
% ************************************************
% Some hacks to speed up solver calls
% Only track solver-time if user wants profile
% ************************************************
p.getsolvertime = p.options.bnb.profile;
% *******************************
%% DISPLAY HEADER
% *******************************
originalDiscrete = [p.integer_variables(:);p.binary_variables(:)];
originalBinary = p.binary_variables(:);
if nnz(Q)==0 & (nnz(p.c-fix(p.c))==0) & isequal(p.K.m,0)
can_use_ceil_lower = all(ismember(find(p.c),originalDiscrete));
else
can_use_ceil_lower = 0;
end
if p.options.bnb.verbose
pc = p.problemclass;
non_convex_obj = pc.objective.quadratic.nonconvex | pc.objective.polynomial;
possiblynonconvex = non_convex_obj;
if ~isequal(p.solver.lower.version,'')
p.solver.lower.tag = [p.solver.lower.tag '-' p.solver.lower.version];
end
disp('* Starting YALMIP integer branch & bound.');
disp(['* Lower solver : ' p.solver.lower.tag]);
disp(['* Upper solver : ' p.options.bnb.uppersolver]);
disp(['* Max iterations : ' num2str(p.options.bnb.maxiter)]);
if possiblynonconvex & p.options.warning
disp(' ');
disp('Warning : The relaxed problem may be nonconvex. This means ');
disp('that the branching process not is guaranteed to find a');
disp('globally optimal solution, since the lower bound can be');
disp('invalid. Hence, do not trust the bound or the gap...')
end
end
if p.options.bnb.verbose; disp(' Node Upper Gap(%) Lower Open');end;
if nnz(Q)==0 & nnz(c)==1 & isequal(p.K.m,0)
p.simplecost = 1;
else
p.simplecost = 0;
end
poriginal = p;
p.cuts = [];
%% MAIN LOOP
p.options.rounding = [1 1 1 1];
if p.options.bnb.nodefix & (p.K.s(1)>0)
top=1+p.K.f+p.K.l+sum(p.K.q);
for i=1:length(p.K.s)
n=p.K.s(i);
for j=1:size(p.F_struc,2)-1;
X=full(reshape(p.F_struc(top:top+n^2-1,j+1),p.K.s(i),p.K.s(i)));
X=(X+X')/2;
v=real(eig(X+sqrt(eps)*eye(length(X))));
if all(v>=0)
sdpmonotinicity(i,j)=-1;
elseif all(v<=0)
sdpmonotinicity(i,j)=1;
else
sdpmonotinicity(i,j)=nan;
end
end
top=top+n^2;
end
else
sdpmonotinicity=[];
end
% Try to find sum(d_i) = 1
sosgroups = {};
sosvariables = [];
if p.K.f > 0 & ~isempty(p.binary_variables)
nbin = length(p.binary_variables);
Aeq = -p.F_struc(1:p.K.f,2:end);
beq = p.F_struc(1:p.K.f,1);
notbinary_var_index = setdiff(1:length(p.lb),p.binary_variables);
only_binary = ~any(Aeq(:,notbinary_var_index),2);
Aeq_bin = Aeq(find(only_binary),p.binary_variables);
beq_bin = beq(find(only_binary),:);
% Detect groups with constraints sum(d_i) == 1
sosgroups = {};
for i = 1:size(Aeq_bin,1)
if beq_bin(i) == 1
[ix,jx,sx] = find(Aeq_bin(i,:));
if all(sx == 1)
sosgroups{end+1} = p.binary_variables(jx);
sosvariables = [sosvariables p.binary_variables(jx)];
end
end
end
end
%p = presolve_bounds_from_equalities(p);
pid = 0;
while ~isempty(node) & (solved_nodes < p.options.bnb.maxiter) & (isinf(lower) | gap>p.options.bnb.gaptol)
% ********************************************
% Adjust variable bound based on upper bound
% ********************************************
% This code typically never runs but can be turned on
% using options.bnb.nodetight and bnb.nodefix.
if ~isinf(upper) & ~isnan(lower)
[p,poriginal,stack] = pruneglobally(p,poriginal,upper,lower,stack,x);
[p,poriginal,stack] = fixvariables(p,poriginal,upper,lower,stack,x_min,sdpmonotinicity);
end
% ********************************************
% BINARY VARIABLES ARE FIXED ALONG THE PROCESS
% ********************************************
binary_variables = p.binary_variables;
% ********************************************
% ASSUME THAT WE WON'T FATHOME
% ********************************************
keep_digging = 1;
message = '';
% *************************************
% SOLVE NODE PROBLEM
% *************************************
if any(p.ub<p.lb - 1e-12)
x = zeros(length(p.c),1);
output.Primal = x;
output.problem=1;
else
p.x_min = x_min;
relaxed_p = p;
relaxed_p.integer_variables = [];
relaxed_p.binary_variables = [];
relaxed_p.ub(p.ub<p.lb) = relaxed_p.lb(p.ub<p.lb);
output = bnb_solvelower(lowersolver,relaxed_p,upper+abs(upper)*1e-2+1e-4,lower);
if p.options.bnb.profile
profile.local_solver_time = profile.local_solver_time + output.solvertime;
end
x = setnonlinearvariables(p,output.Primal);
% **************************************
% Hmm, don't remember why this fix...
% **************************************
if (p.K.l>0) & ~any(p.variabletype) & any(p.F_struc(p.K.f+1:p.K.f+p.K.l,:)*[1;x]<-1e-5)
output.problem = 1;
elseif output.problem == 5 & ~checkfeasiblefast(p,x,p.options.bnb.feastol)
output.problem = 1;
end
end
solved_nodes = solved_nodes+1;
% **************************************
% THIS WILL BE INTIAL GUESS FOR CHILDREN
% **************************************
p.x0 = x;
% *************************************
% ANY INTEGERS? ROUND?
% *************************************
non_integer_binary = abs(x(binary_variables)-round(x(binary_variables)))>p.options.bnb.inttol;
non_integer_integer = abs(x(integer_variables)-round(x(integer_variables)))>p.options.bnb.inttol;
if p.options.bnb.round
x(binary_variables(~non_integer_binary)) = round(x(binary_variables(~non_integer_binary)));
x(integer_variables(~non_integer_integer)) = round(x(integer_variables(~non_integer_integer)));
end
non_integer_binary = find(non_integer_binary);
non_integer_integer = find(non_integer_integer);
% *************************************
% NODE HEURISTICS (NOTHING CODED)
% *************************************
if output.problem==0 | output.problem==3 | output.problem==4
cost = computecost(f,c,Q,x,p);%f+c'*x+x'*Q*x;
if isnan(lower)
lower = cost;
end
if cost <= upper & ~(isempty(non_integer_binary) & isempty(non_integer_integer))
[upper1,x_min1] = feval(uppersolver,poriginal,output);
if upper1 < upper
x_min = x_min1;
upper = upper1;
[stack,stacklower] = prune(stack,upper,p.options,solved_nodes,p);
lower = min(lower,stacklower);
[p,poriginal,stack] = pruneglobally(p,poriginal,upper,lower,stack,x_min);
[p,poriginal,stack] = fixvariables(p,poriginal,upper,lower,stack,x_min,sdpmonotinicity);
end
end
end
p = adaptivestrategy(p,upper,solved_nodes);
% *************************************
% CHECK FATHOMING POSSIBILITIES
% *************************************
feasible = 1;
switch output.problem
case 0
if can_use_ceil_lower
lower = ceil(lower);
end
case {1,12}
keep_digging = 0;
cost = inf;
feasible = 0;
case 2
cost = -inf;
otherwise
% This part has to be much more robust
cost = f+c'*x+x'*Q*x;
end
% **************************************
% YAHOO! INTEGER SOLUTION FOUND
% **************************************
if isempty(non_integer_binary) & isempty(non_integer_integer)
if (cost<upper) & feasible
x_min = x;
upper = cost;
[stack,lower] = prune(stack,upper,p.options,solved_nodes,p);
end
p = adaptivestrategy(p,upper,solved_nodes);
keep_digging = 0;
end
% **************************************
% Stop digging if it won't give sufficient improvement anyway
% **************************************
if cost>upper*(1-1e-6)
keep_digging = 0;
end
% **********************************
% CONTINUE SPLITTING?
% **********************************
if keep_digging & (cost<upper)
% **********************************
% BRANCH VARIABLE
% **********************************
[index,whatsplit] = branchvariable(x,integer_variables,binary_variables,p.options,x_min,[],p);
% **********************************
% CREATE NEW PROBLEMS
% **********************************
p0_feasible = 1;
p1_feasible = 1;
switch whatsplit
case 'binary'
[p0,p1,index] = binarysplit(p,x,index,cost,[],sosgroups,sosvariables);
case 'integer'
[p0,p1] = integersplit(p,x,index,cost,x_min);
otherwise
end
% **********************************
% Only save varying data in the tree
% **********************************
% if pid >= 280
% 1
% end
node1.lb = p1.lb;
node1.ub = p1.ub;
node1.depth = p1.depth;
node1.lower = p1.lower;
node1.x0 = p1.x0;
node1.binary_variables = p1.binary_variables;
node1.pid = pid;pid = pid + 1;
node0.lb = p0.lb;
node0.ub = p0.ub;
node0.depth = p0.depth;
node0.lower = p0.lower;
node0.x0 = p0.x0;
node0.binary_variables = p0.binary_variables;
node0.pid = pid;pid = pid + 1;
if p1_feasible
stack = push(stack,node1);
end
if p0_feasible
stack = push(stack,node0);
end
end
% Lowest cost in any open node
if ~isempty(stack)
lower = min([stack.lower]);
if can_use_ceil_lower
lower = ceil(lower);
end
end
% **********************************
% Get a new node to solve
% **********************************
[node,stack] = pull(stack,p.options.bnb.method,x_min,upper);
if ~isempty(node)
p.lb = node.lb;
p.ub = node.ub;
p.depth = node.depth;
p.lower = node.lower;
p.x0 = node.x0;
p.binary_variables = node.binary_variables;
p.pid = node.pid;
end
gap = abs((upper-lower)/(1e-3+abs(upper)+abs(lower)));
if isnan(gap)
gap = inf;
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -