1130 lines
39 KiB
Matlab
Executable File
1130 lines
39 KiB
Matlab
Executable File
function output = mpcvx(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
|
|
%
|
|
% WARNING: THIS IS EXPERIMENTAL CODE
|
|
%
|
|
% bmibnb.lowersolver - Solver for lower bound [standard solver tag ('')]
|
|
% bmibnb.uppersolver - Solver for upper bound [standard solver tag ('')]
|
|
% bmibnb.lpsolver - Solver for LP bound tightening [standard 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 found<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 of solved nodes [int (100)]
|
|
% bmibnb.maxtime - Maximum CPU time (sec.) [int (3600)]
|
|
|
|
% Author Johan Löfberg
|
|
% $Id: callmpcvx.m,v 1.2 2005-05-07 13:53:20 joloef Exp $
|
|
|
|
% ********************************
|
|
% INITIALIZE DIAGNOSTICS IN YALMIP
|
|
% ********************************
|
|
bnbsolvertime = clock;
|
|
showprogress('Branch and bound started',p.options.showprogress);
|
|
|
|
% *******************************
|
|
% Display-logics
|
|
% *******************************
|
|
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
|
|
|
|
% *******************************
|
|
% Actual linear variables
|
|
% *******************************
|
|
p.linears = find(sum(p.monomtable,2)==1);
|
|
|
|
% *******************************
|
|
% PRE-CALCULATE INDICIES
|
|
% FOR BILNEAR VARIABLES
|
|
% *******************************
|
|
nonlinear = find(~(sum(p.monomtable~=0,2)==1 & sum(p.monomtable,2)==1));
|
|
nonlins = [];
|
|
for i = 1:length(nonlinear)
|
|
z = find(p.monomtable(nonlinear(i),:));
|
|
if length(z)==1
|
|
nonlins = [nonlins;nonlinear(i) z z];
|
|
else
|
|
nonlins = [nonlins;nonlinear(i) z(1) z(2)];
|
|
end
|
|
end
|
|
p.nonlins = nonlins;
|
|
|
|
p.options.saveduals = 0;
|
|
|
|
% *******************************
|
|
% Select branch variables
|
|
% *******************************
|
|
p.branch_variables = decide_branch_variables(p);
|
|
|
|
% ********************************
|
|
% Extract bounds from model
|
|
% ********************************
|
|
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
|
|
if ~isempty(p.F_struc)
|
|
[lb,ub,used_rows] = findulb(p.F_struc,p.K);
|
|
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
|
|
% Remove linear bounds
|
|
used_rows = used_rows(find(~any(p.F_struc(p.K.f+used_rows,1+nonlinear),2)));
|
|
not_used_rows = setdiff(1:p.K.l,used_rows);
|
|
for i = 1:length(p.KCut.l)
|
|
p.KCut.l(i) = find(not_used_rows==p.KCut.l(i) );
|
|
end
|
|
if ~isempty(used_rows)
|
|
p.F_struc(p.K.f+used_rows,:)=[];
|
|
p.K.l = p.K.l - length(used_rows);
|
|
end
|
|
end
|
|
end
|
|
p = clean_bounds(p);
|
|
p = updatenonlinearbounds(p);
|
|
feasible = all(p.lb<=p.ub);
|
|
|
|
% ********************************
|
|
% Remove empty linear rows
|
|
% ********************************
|
|
if p.K.l > 0
|
|
empty_rows = find(~any(p.F_struc(p.K.f+1:p.K.f+p.K.l,2:end),2));
|
|
if ~isempty(empty_rows)
|
|
if all(p.F_struc(p.K.f+empty_rows,1)>=0)
|
|
p.F_struc(p.K.f+empty_rows,:)=[];
|
|
p.K.l = p.K.l - length(empty_rows);
|
|
else
|
|
feasible = 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
% ********************************
|
|
% Tighten bounds at root
|
|
% ********************************
|
|
if p.options.bmibnb.roottight & feasible
|
|
lowersolver = eval(['@' p.solver.lowercall]);
|
|
c = p.c;
|
|
Q = p.Q;
|
|
mt = p.monomtable;
|
|
p.monomtable = eye(length(c));
|
|
i = 1;
|
|
while i<=length(p.linears) & feasible
|
|
j = p.linears(i);
|
|
p.c = eyev(length(p.c),j);
|
|
output = feval(lowersolver,p);
|
|
if (output.problem == 0) & (output.Primal(j)>p.lb(j))
|
|
p.lb(j) = output.Primal(j);
|
|
p = updateonenonlinearbound(p,j);
|
|
p = clean_bounds(p);
|
|
end
|
|
if output.problem == 1
|
|
feasible = 0;
|
|
else
|
|
% p = updatenonlinearbounds(p,0,1);
|
|
p.c = -eyev(length(p.c),j);
|
|
output = feval(lowersolver,p);
|
|
if (output.problem == 0) & (output.Primal(j) < p.ub(j))
|
|
p.ub(j) = output.Primal(j);
|
|
p = updateonenonlinearbound(p,j);
|
|
p = clean_bounds(p);
|
|
end
|
|
if output.problem == 1
|
|
feasible = 0;
|
|
end
|
|
i = i+1;
|
|
end
|
|
end
|
|
p.lb(p.lb<-1e10) = -inf;
|
|
p.ub(p.ub>1e10) = inf;
|
|
p.c = c;
|
|
p.Q = Q;
|
|
p.monomtable = mt;
|
|
end
|
|
|
|
if feasible
|
|
|
|
% *******************************
|
|
% Bounded domain?
|
|
% *******************************
|
|
involved = unique(p.nonlins(:,2:3));
|
|
if isinf(p.lb(involved)) | isinf(p.ub(involved))
|
|
error('You have to bound all complicating variables explicitely (I cannot deduce bounds on all variables)')
|
|
output.Primal = [];
|
|
output.problem = -1;
|
|
end
|
|
|
|
% *******************************
|
|
% We don't need to save node data
|
|
% *******************************
|
|
p.options.savesolverinput = 0;
|
|
p.options.savesolveroutput = 0;
|
|
|
|
% *******************************
|
|
% RUN BRANCH & BOUND
|
|
% *******************************
|
|
[x_min,solved_nodes,lower,upper] = branch_and_bound(p);
|
|
|
|
% **********************************
|
|
% 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
|
|
else
|
|
output.problem = 1;
|
|
x_min = repmat(nan,length(p.c),1);
|
|
solved_nodes = 0;
|
|
end
|
|
|
|
output.solved_nodes = solved_nodes;
|
|
output.Primal = x_min;
|
|
output.Dual = [];
|
|
output.Slack = [];
|
|
output.infostr = yalmiperror(output.problem,'BNB');
|
|
output.solverinput = 0;
|
|
output.solveroutput =[];
|
|
output.solvertime = etime(clock,bnbsolvertime);
|
|
|
|
function [x_min,solved_nodes,lower,upper] = branch_and_bound(p)
|
|
|
|
% ***************************************
|
|
% LPs ARE USED IN BOX-REDUCTION
|
|
% (this is essentially a cutting plane pool)
|
|
% ***************************************
|
|
p.lpcuts = p.F_struc(1+p.K.f:1:p.K.l,:);
|
|
|
|
% ***************************************
|
|
% Create function handles to solvers
|
|
% ***************************************
|
|
try
|
|
lowersolver = eval(['@' p.solver.lowercall]); % Local LMI solver
|
|
uppersolver = eval(['@' p.solver.uppercall]); % Local BMI solver
|
|
lpsolver = eval(['@' p.solver.lpcall]); % LP solver
|
|
catch
|
|
disp(' ');
|
|
disp('The internal branch & bound solver requires MATLAB 6')
|
|
disp('I am too lazy too do the changes to make it compatible')
|
|
disp('with MATLAB 5. If you really need it, contact me...');
|
|
disp(' ');
|
|
error(lasterr);
|
|
end
|
|
|
|
% ************************************************
|
|
% GLOBAL PROBLEM DATA
|
|
% ************************************************
|
|
c = p.c;
|
|
Q = p.Q;
|
|
f = p.f;
|
|
K = p.K;
|
|
p.options.saveduals = 0;
|
|
options = p.options;
|
|
|
|
% ************************************************
|
|
% ORIGINAL PROBLEM (used in LOCAL BMI solver)
|
|
% ************************************************
|
|
p_upper = p;
|
|
|
|
% ************************************************
|
|
% Remove linear cutting planes from problem
|
|
% ************************************************
|
|
p_upper.F_struc(p_upper.K.f+p_upper.KCut.l,:)=[];
|
|
p_upper.K.l = p_upper.K.l - length(p_upper.KCut.l);
|
|
|
|
% ************************************************
|
|
% Remove sdp cutting planes from problem
|
|
% ************************************************
|
|
if length(p_upper.KCut.s)>0
|
|
starts = p_upper.K.f+p_upper.K.l + [1 1+cumsum((p_upper.K.s).^2)];
|
|
remove_these = [];
|
|
for i = 1:length(p_upper.KCut.s)
|
|
j = p_upper.KCut.s(i);
|
|
remove_these = [remove_these;(starts(j):starts(j+1)-1)'];
|
|
end
|
|
p_upper.F_struc(remove_these,:)=[];
|
|
p_upper.K.s(p_upper.KCut.s) = [];
|
|
end
|
|
|
|
% ************************************************
|
|
% INITILIZATION
|
|
% ************************************************
|
|
p.depth = 0;
|
|
p.dpos = 0;
|
|
p.lower = NaN;
|
|
upper = inf;
|
|
lower = NaN;
|
|
gap = inf;
|
|
x_min = zeros(length(p.c),1);
|
|
stack = [];
|
|
solved_nodes = 0;
|
|
solved_lower = 0;
|
|
solved_upper = 0;
|
|
solved_lp = 0;
|
|
|
|
if isempty(p.x0)
|
|
p.x0 = zeros(length(p.c),1);
|
|
end
|
|
|
|
x0 = evaluate_nonlinear(p,p.x0);
|
|
upper_residual = resids(p,x0);
|
|
x0_feasible = all(upper_residual(1:p.K.f)>=-options.bmibnb.eqtol) & all(upper_residual(1+p.K.f:end)>=options.bmibnb.pdtol);
|
|
if p.options.usex0 & x0_feasible
|
|
x_min = x0;
|
|
upper = p.f+p.c'*x0+x0'*Q*x0;
|
|
end
|
|
|
|
% ************************************************
|
|
% Branch & bound loop
|
|
% ************************************************
|
|
if options.bmibnb.verbose>0
|
|
fprintf('******************************************************************************************************************\n')
|
|
fprintf('#node Was'' up gap upper node lower dpth stk Memory Vol-red\n')
|
|
fprintf('******************************************************************************************************************\n')
|
|
end
|
|
|
|
doplot = 0;
|
|
if doplot
|
|
close all;
|
|
hold on;
|
|
end
|
|
|
|
t_start = cputime;
|
|
go_on = 1;
|
|
while go_on
|
|
|
|
if doplot;ellipplot(diag([200 50]),1,'y',[p.dpos;-p.depth]);drawnow;end;
|
|
% ********************************************
|
|
% ASSUME THAT WE WON'T FATHOME
|
|
% ********************************************
|
|
keep_digging = 1;
|
|
% ********************************************
|
|
% REDUCE BOX
|
|
% ********************************************
|
|
if ~options.bmibnb.lpreduce
|
|
% [p.lb,p.ub] = tightenbounds(-p.F_struc(1+p.K.f:p.K.f+p.K.l,2:end),p.F_struc(1+p.K.f:p.K.f+p.K.l,1),p.lb,p.ub,[]);
|
|
vol_reduction = 1;
|
|
feasible = 1;
|
|
else
|
|
[p,feasible,vol_reduction] = boxreduce(p,upper,lower,lpsolver,options);
|
|
end
|
|
|
|
% ********************************************
|
|
% SOLVE LOWER AND UPPER
|
|
% ********************************************
|
|
if feasible
|
|
|
|
output = solvelower(p,options,lowersolver);
|
|
|
|
info_text = '';
|
|
switch output.problem
|
|
case 1
|
|
if doplot;ellipplot(diag([200 25]),1,'r',[p.dpos;-p.depth]);drawnow;end;
|
|
info_text = 'Infeasible node';
|
|
keep_digging = 0;
|
|
cost = inf;
|
|
feasible = 0;
|
|
|
|
case 2
|
|
cost = -inf;
|
|
|
|
case {0,3,4}
|
|
|
|
x = output.Primal;
|
|
|
|
cost = f+c'*x+x'*Q*x;
|
|
|
|
z = evaluate_nonlinear(p,x);
|
|
p = addsdpcut(p,z);
|
|
|
|
% Maybe the relaxed solution is feasible
|
|
relaxed_residual = resids(p_upper,z);
|
|
relaxed_feasible = all(relaxed_residual(1:p.K.f)>=-options.bmibnb.eqtol) & all(relaxed_residual(1+p.K.f:end)>=options.bmibnb.pdtol);
|
|
if relaxed_feasible
|
|
this_upper = f+c'*z+z'*Q*z;
|
|
if (this_upper < (1-1e-5)*upper) & (this_upper < upper - 1e-5)
|
|
x_min = z;
|
|
upper = this_upper;
|
|
info_text = 'Improved upper bound';
|
|
cost = cost-1e-10; % Otherwise we'll fathome!
|
|
end
|
|
end
|
|
|
|
% UPDATE THE LOWER BOUND
|
|
if isnan(lower)
|
|
lower = cost;
|
|
end
|
|
if ~isempty(stack)
|
|
lower =min(cost,min([stack.lower]));
|
|
else
|
|
lower = min(lower,cost);
|
|
end
|
|
|
|
if cost<upper
|
|
output = solveupper(p,p_upper,x,options,uppersolver);
|
|
xu = evaluate_nonlinear(p,output.Primal);
|
|
upper_residual = resids(p_upper,xu);
|
|
if output.problem == 0 | (all(upper_residual(1:p_upper.K.f)>=-options.bmibnb.eqtol) & all(upper_residual(1+p_upper.K.f:end)>=options.bmibnb.pdtol))
|
|
this_upper = f+c'*xu+xu'*Q*xu;
|
|
if (this_upper < (1-1e-5)*upper) & (this_upper < upper - 1e-5)
|
|
x_min = xu;
|
|
upper = this_upper;
|
|
info_text = 'Improved upper bound';
|
|
end
|
|
end
|
|
else
|
|
if doplot;ellipplot(diag([200 25]),1,'b',[p.dpos;-p.depth]);drawnow;end
|
|
info_text = 'Poor lower bound';
|
|
keep_digging = 0;
|
|
end
|
|
otherwise
|
|
end
|
|
else
|
|
if doplot;ellipplot(diag([200 25]),1,'r',[p.dpos;-p.depth]);drawnow;end
|
|
info_text = 'Infeasible during box-reduction';
|
|
keep_digging = 0;
|
|
cost = inf;
|
|
feasible = 0;
|
|
end
|
|
solved_nodes = solved_nodes+1;
|
|
|
|
if ~isempty(stack)
|
|
[stack,lower] = prune(stack,upper,options,solved_nodes);
|
|
end
|
|
lower = min(lower,cost);
|
|
|
|
% **********************************
|
|
% CONTINUE SPLITTING?
|
|
% **********************************
|
|
if keep_digging & max(p.ub(p.branch_variables)-p.lb(p.branch_variables))>options.bmibnb.vartol
|
|
spliton = branchvariable(p,options,x);
|
|
bounds = partition(p,options,spliton,x_min);
|
|
|
|
node_1 = savetonode(p,spliton,bounds(1),bounds(2),-1,x,cost);
|
|
node_2 = savetonode(p,spliton,bounds(2),bounds(3),1,x,cost);
|
|
stack = push(stack,node_1);
|
|
stack = push(stack,node_2);
|
|
end
|
|
|
|
% Pick and create a suitable node to continue on
|
|
[p,stack] = selectbranch(p,options,stack,x_min,upper);
|
|
|
|
if isempty(p)
|
|
if ~isinf(upper)
|
|
relgap = 0;
|
|
end
|
|
depth = 0;
|
|
else
|
|
relgap = 100*(upper-lower)/(1+abs(upper));
|
|
depth = p.depth;
|
|
end
|
|
if options.bmibnb.verbose>0
|
|
ws = whos; %Work-space
|
|
Mb = sum([ws(:).bytes])/1024^2; %Megs
|
|
showprogress(sprintf(['%3d ' info_text repmat(' ',1,35-length(info_text)) ' %8.2f%% %8.4f %8.4f %8.4f %3d %3d %5.2fMB %4.1f%% '],solved_nodes,relgap,upper,cost,lower,depth,length(stack),Mb,100-vol_reduction*100),options.bmibnb.verbose)
|
|
end
|
|
|
|
absgap = upper-lower;
|
|
% **************************************
|
|
% Continue?
|
|
% **************************************
|
|
time_ok = cputime-t_start < options.bmibnb.maxtime;
|
|
iter_ok = solved_nodes < options.bmibnb.maxiter;
|
|
any_nodes = ~isempty(p);
|
|
relgap_too_big = (isinf(lower) | isnan(relgap) | relgap>100*options.bmibnb.relgaptol);
|
|
absgap_too_big = (isinf(lower) | isnan(absgap) | absgap>options.bmibnb.absgaptol);
|
|
taget_not_met = upper>options.bmibnb.target;
|
|
go_on = taget_not_met & time_ok & any_nodes & iter_ok & relgap_too_big & absgap_too_big ;
|
|
end
|
|
if options.bmibnb.verbose>0
|
|
fprintf('******************************************************************************************************************\n')
|
|
if options.bmibnb.verbose;showprogress([num2str2(solved_nodes,3) ' Finishing. Cost: ' num2str(upper) ' Gap: ' num2str(relgap) '%'],options.bnb.verbose);end
|
|
fprintf('******************************************************************************************************************\n')
|
|
end
|
|
|
|
|
|
|
|
function stack = push(stackin,p)
|
|
if ~isempty(stackin)
|
|
stack = [p;stackin];
|
|
else
|
|
stack(1)=p;
|
|
end
|
|
|
|
function [p,stack] = pull(stack,method,x_min,upper);
|
|
if ~isempty(stack)
|
|
switch method
|
|
case 'maxvol'
|
|
for i = 1:length(stack)
|
|
vol(i) = sum(stack(i).ub(stack(i).branch_variables)-stack(i).lb(stack(i).branch_variables));
|
|
end
|
|
[i,j] = max(vol);
|
|
p=stack(j);
|
|
stack = stack([1:1:j-1 j+1:1:end]);
|
|
|
|
case 'best'
|
|
[i,j]=min([stack.lower]);
|
|
p=stack(j);
|
|
stack = stack([1:1:j-1 j+1:1:end]);
|
|
|
|
otherwise
|
|
end
|
|
else
|
|
p = [];
|
|
end
|
|
|
|
|
|
function s = num2str2(x,d,c);
|
|
if nargin==3
|
|
s = num2str(x,c);
|
|
else
|
|
s = num2str(x);
|
|
end
|
|
s = [repmat(' ',1,d-length(s)) s];
|
|
|
|
|
|
function res = resids(p,x)
|
|
res= [];
|
|
if p.K.f>0
|
|
res = -abs(p.F_struc(1:p.K.f,:)*[1;x]);
|
|
end
|
|
if p.K.l>0
|
|
res = [res;p.F_struc(p.K.f+1:p.K.f+p.K.l,:)*[1;x]];
|
|
end
|
|
if (length(p.K.s)>1) | p.K.s>0
|
|
top = 1+p.K.f+p.K.l;
|
|
for i = 1:length(p.K.s)
|
|
n = p.K.s(i);
|
|
X = p.F_struc(top:top+n^2-1,:)*[1;x];top = top+n^2;
|
|
X = reshape(X,n,n);
|
|
res = [res;min(eig(X))];
|
|
end
|
|
end
|
|
|
|
res = [res;min([p.ub-x;x-p.lb])];
|
|
|
|
|
|
function [stack,lower] = prune(stack,upper,options,solved_nodes)
|
|
% *********************************
|
|
% PRUNE STACK W.R.T NEW UPPER BOUND
|
|
% *********************************
|
|
if ~isempty(stack)
|
|
toolarge = find([stack.lower]>upper*(1-1e-4));
|
|
if ~isempty(toolarge)
|
|
if options.bnb.verbose;showprogress([num2str2(solved_nodes,3) ' Pruned ' num2str(length(toolarge)) ' nodes'],options.bnb.verbose-1);end
|
|
stack(toolarge)=[];
|
|
end
|
|
end
|
|
if ~isempty(stack)
|
|
lower = min([stack.lower]);
|
|
else
|
|
lower = upper;
|
|
end
|
|
|
|
function pcut = addmcgormick(p)
|
|
|
|
pcut = p;
|
|
top = 0;
|
|
row = [];
|
|
col = [];
|
|
val = [];
|
|
F_temp = [];
|
|
for i = 1:size(p.nonlins,1)
|
|
z = p.nonlins(i,1);
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
x_lb = p.lb(x);
|
|
x_ub = p.ub(x);
|
|
y_lb = p.lb(y);
|
|
y_ub = p.ub(y);
|
|
top = 0;
|
|
row = [];
|
|
col = [];
|
|
val = [];
|
|
|
|
if x~=y
|
|
row = [1;1;1;1;2;2;2;2;3;3;3;3;4;4;4;4];
|
|
col = [1 ; z+1 ; x+1 ; y+1 ; 1 ; z+1 ; x+1 ; y+1 ; 1 ; z+1 ; x+1 ; y+1 ; 1 ; z+1 ; x+1 ; y+1];
|
|
val = [x_lb*y_lb;1;-y_lb;-x_lb;x_ub*y_ub;1;-y_ub;-x_ub;-x_ub*y_lb;-1;y_lb;x_ub;-x_lb*y_ub;-1;y_ub;x_lb];
|
|
F_temp = [F_temp;sparse(row,col,val,4,size(pcut.F_struc,2))];
|
|
else
|
|
|
|
nr = 3;
|
|
row = [1;1;1;2;2 ;2; 3; 3; 3];
|
|
col = [1 ;z+1 ;x+1 ;1 ;z+1 ;x+1 ;1 ;z+1 ;x+1];
|
|
val = [-x_ub*x_lb;-1;x_lb+x_ub;x_lb*y_lb;1;-y_lb-x_lb;x_ub*y_ub;1;-y_ub-x_ub];
|
|
|
|
F_temp = [F_temp;sparse(row,col,val,nr,1+length(p.c))];
|
|
end
|
|
bounds = [x_lb*y_lb x_lb*y_ub x_ub*y_lb x_ub*y_ub];
|
|
if x==y
|
|
pcut.lb(pcut.nonlins(i,1)) = max(pcut.lb(pcut.nonlins(i,1)),max(0,min(bounds)));
|
|
else
|
|
pcut.lb(pcut.nonlins(i,1)) = max(pcut.lb(pcut.nonlins(i,1)),min(bounds));
|
|
end
|
|
pcut.ub(pcut.nonlins(i,1)) = min(pcut.ub(pcut.nonlins(i,1)),max(bounds));
|
|
end
|
|
|
|
keep = find(~isinf(F_temp(:,1)));
|
|
F_temp = F_temp(keep,:);
|
|
pcut.F_struc = [F_temp;pcut.F_struc];
|
|
pcut.K.l = pcut.K.l+size(F_temp,1);
|
|
|
|
function [p,feasible,lower] = lpbmitighten(p,lower,upper,lpsolver)
|
|
|
|
% Construct problem with only linear terms
|
|
% and add cuts from lower/ upper bounds
|
|
c = p.c;
|
|
p_test = p;
|
|
p_test.K.s = 0;
|
|
p_test.F_struc = p_test.F_struc(1+p_test.K.f:1:p_test.K.l+p_test.K.f,:);
|
|
|
|
if ~isnan(lower)
|
|
p_test.F_struc = [-(p.lower-abs(p.lower)*0.01) p_test.c';p_test.F_struc];
|
|
end
|
|
if upper < inf
|
|
p_test.F_struc = [upper+abs(upper)*0.01 -p_test.c';p_test.F_struc];
|
|
end
|
|
|
|
p_test.F_struc = [p_test.lpcuts;p_test.F_struc];
|
|
p_test.K.l = size(p_test.F_struc,1);
|
|
|
|
% Add cuts for nonlinear terms
|
|
p_test = addmcgormick(p_test);
|
|
|
|
p_test.F_struc = [p.F_struc(1:1:p.K.f,:);p_test.F_struc];
|
|
|
|
|
|
feasible = 1;
|
|
i = 1;
|
|
|
|
p_test = clean_bounds(p_test);
|
|
|
|
j = 1;
|
|
n = ceil(max(p.options.bmibnb.lpreduce*length(p_test.linears),1));
|
|
res = zeros(length(p.lb),1);
|
|
for i = 1:size(p.nonlins,1)
|
|
res(p.nonlins(i,2)) = res(p.nonlins(i,2)) + abs( p.x0(p.nonlins(i,1))-p.x0(p.nonlins(i,2)).*p.x0(p.nonlins(i,3)));
|
|
res(p.nonlins(i,3)) = res(p.nonlins(i,3)) + abs( p.x0(p.nonlins(i,1))-p.x0(p.nonlins(i,2)).*p.x0(p.nonlins(i,3)));
|
|
end
|
|
res = res(p.linears);
|
|
[ii,jj] = sort(abs(res));
|
|
jj = jj(end-n+1:end);
|
|
|
|
while feasible & j<=length(jj)
|
|
i = p_test.linears(jj(j));
|
|
if abs(p.ub(i)-p.lb(i)>0.1)
|
|
p_test.c = eyev(length(p_test.c),i);
|
|
|
|
output = feval(lpsolver,p_test);
|
|
if output.problem == 0
|
|
if p_test.lb(i) < output.Primal(i)-1e-5
|
|
p_test.lb(i) = output.Primal(i);
|
|
p_test = updateonenonlinearbound(p_test,i);
|
|
end
|
|
p_test.c = -eyev(length(p_test.c),i);
|
|
output = feval(lpsolver,p_test);
|
|
if output.problem == 0
|
|
if p_test.ub(i) > output.Primal(i)+1e-5
|
|
p_test.ub(i) = output.Primal(i);
|
|
p_test = updateonenonlinearbound(p_test,i);
|
|
end
|
|
end
|
|
if output.problem == 1
|
|
feasible = 0;
|
|
end
|
|
end
|
|
if output.problem == 1
|
|
feasible = 0;
|
|
end
|
|
p_test = clean_bounds(p_test);
|
|
end
|
|
j = j + 1;
|
|
end
|
|
p.lb = p_test.lb;
|
|
p.ub = p_test.ub;
|
|
|
|
|
|
function p = updateonenonlinearbound(p,changed_var);
|
|
for i = 1:size(p.nonlins,1)
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
if (x==changed_var) | (y==changed_var)
|
|
z = p.nonlins(i,1);
|
|
x_lb = p.lb(x);
|
|
x_ub = p.ub(x);
|
|
y_lb = p.lb(y);
|
|
y_ub = p.ub(y);
|
|
bounds = [x_lb*y_lb x_lb*y_ub x_ub*y_lb x_ub*y_ub];
|
|
if x==y
|
|
p.lb(p.nonlins(i,1)) = max([p.lb(z) 0 min(bounds)]);
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(z),max(bounds));
|
|
else
|
|
p.lb(p.nonlins(i,1)) = max(p.lb(z),min(bounds));
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(z),max(bounds));
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function p = updatenonlinearbounds(p,changed_var,keepbest);
|
|
% if nargin>1
|
|
% changed_var
|
|
% else
|
|
% i = 1:size(p.nonlins,1);
|
|
% end
|
|
for i = 1:size(p.nonlins,1)
|
|
z = p.nonlins(i,1);
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
x_lb = p.lb(x);
|
|
x_ub = p.ub(x);
|
|
y_lb = p.lb(y);
|
|
y_ub = p.ub(y);
|
|
bounds = [x_lb*y_lb x_lb*y_ub x_ub*y_lb x_ub*y_ub];
|
|
if x==y
|
|
p.lb(p.nonlins(i,1)) = max([p.lb(z) 0 min(bounds)]);
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(z),max(bounds));
|
|
else
|
|
p.lb(p.nonlins(i,1)) = max(p.lb(z),min(bounds));
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(z),max(bounds));
|
|
end
|
|
end
|
|
return
|
|
|
|
if nargin > 1
|
|
|
|
for i = 1:size(p.nonlins,1)
|
|
z = p.nonlins(i,1);
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
if isempty(changed_var) | (x==changed_var) | (y == changed_var) | nargin==3
|
|
bound_x1 = [p.lb(p.nonlins(i,2));p.ub(p.nonlins(i,2))];
|
|
bound_x2 = [p.lb(p.nonlins(i,3));p.ub(p.nonlins(i,3))];
|
|
bounds = [bound_x1(1)*bound_x2(1) bound_x1(1)*bound_x2(2) bound_x1(2)*bound_x2(1) bound_x1(2)*bound_x2(2)];
|
|
if nargin==3
|
|
if x==y
|
|
p.lb(p.nonlins(i,1)) = max([p.lb(p.nonlins(i,1)) 0 min(bounds)]);
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(p.nonlins(i,1)),max(bounds));
|
|
else
|
|
p.lb(p.nonlins(i,1)) = max(p.lb(p.nonlins(i,1)),min(bounds));
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(p.nonlins(i,1)),max(bounds));
|
|
end
|
|
else
|
|
if x==y
|
|
p.lb(p.nonlins(i,1)) = max(0,min(bounds));
|
|
p.ub(p.nonlins(i,1)) = max(bounds);
|
|
else
|
|
p.lb(p.nonlins(i,1)) = min(bounds);
|
|
p.ub(p.nonlins(i,1)) = max(bounds);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for i = 1:size(p.nonlins,1)
|
|
z = p.nonlins(i,1);
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
bound_x1 = [p.lb(p.nonlins(i,2));p.ub(p.nonlins(i,2))];
|
|
bound_x2 = [p.lb(p.nonlins(i,3));p.ub(p.nonlins(i,3))];
|
|
bounds = [bound_x1(1)*bound_x2(1) bound_x1(1)*bound_x2(2) bound_x1(2)*bound_x2(1) bound_x1(2)*bound_x2(2)];
|
|
if x==y
|
|
p.lb(p.nonlins(i,1)) = max( p.lb(p.nonlins(i,1)) ,max(0,min(bounds)));
|
|
p.ub(p.nonlins(i,1)) = min( p.ub(p.nonlins(i,1)) ,max(bounds));
|
|
else
|
|
p.lb(p.nonlins(i,1)) = max(p.lb(p.nonlins(i,1)),min(bounds));
|
|
p.ub(p.nonlins(i,1)) = min(p.ub(p.nonlins(i,1)),max(bounds));
|
|
end
|
|
end
|
|
end
|
|
|
|
% *************************************
|
|
% DERIVE LINEAR CUTS FROM SDPs
|
|
% THESE ARE ONLY USED IN BOXREDUCE
|
|
% *************************************
|
|
function p = addsdpcut(p,x)
|
|
if p.K.s > 0
|
|
top = p.K.f+p.K.l+1;
|
|
newcuts = 1;
|
|
newF = [];
|
|
for i = 1:length(p.K.s)
|
|
n = p.K.s(i);
|
|
X = p.F_struc(top:top+n^2-1,:)*[1;x];
|
|
X = reshape(X,n,n);
|
|
[d,v] = eig(X);
|
|
for m = 1:length(v)
|
|
if v(m,m)<0
|
|
for j = 1:length(x)+1;
|
|
newF(newcuts,j)= d(:,m)'*reshape(p.F_struc(top:top+n^2-1,j),n,n)*d(:,m);
|
|
end
|
|
% max(abs(newF(:,2:end)),[],2)
|
|
newF(newcuts,1)=newF(newcuts,1)+1e-6;
|
|
newcuts = newcuts + 1;
|
|
if size(p.lpcuts,1)>0
|
|
dist = p.lpcuts*newF(newcuts-1,:)'/(newF(newcuts-1,:)*newF(newcuts-1,:)');
|
|
if any(abs(dist-1)<1e-3)
|
|
newF = newF(1:end-1,:);
|
|
newcuts = newcuts - 1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
top = top+n^2;
|
|
end
|
|
|
|
if ~isempty(newF)
|
|
% Don't keep all
|
|
m = size(newF,2);
|
|
% size(p.lpcuts)
|
|
p.lpcuts = [newF;p.lpcuts];
|
|
violations = p.lpcuts*[1;x];
|
|
p.lpcuts = p.lpcuts(violations<0.1,:);
|
|
|
|
if size(p.lpcuts,1)>15*m
|
|
violations = p.lpcuts*[1;x];
|
|
[i,j] = sort(violations);
|
|
p.lpcuts = p.lpcuts(j(1:15*m),:);
|
|
p.lpcuts = p.lpcuts(end-15*m+1:end,:);
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function spliton = branchvariable(p,options,x)
|
|
|
|
% Split if box is to narrow
|
|
width = abs(p.ub(p.branch_variables)-p.lb(p.branch_variables));
|
|
if min(width)/max(width) < 0.1
|
|
[i,j] = max(width);
|
|
spliton = p.branch_variables(j);
|
|
else
|
|
% res = zeros(length(p.lb),1);
|
|
% for i = 1:size(p.nonlins,1)
|
|
% res(p.nonlins(i,2)) = res(p.nonlins(i,2)) + abs( x(p.nonlins(i,1))-x(p.nonlins(i,2)).*x(p.nonlins(i,3)));
|
|
% res(p.nonlins(i,3)) = res(p.nonlins(i,3)) + abs( x(p.nonlins(i,1))-x(p.nonlins(i,2)).*x(p.nonlins(i,3)));
|
|
% end
|
|
%
|
|
% [ii,jj] = sort(abs(res));
|
|
% spliton = jj(end);
|
|
|
|
res = x(p.nonlins(:,1))-x(p.nonlins(:,2)).*x(p.nonlins(:,3));
|
|
[ii,jj] = sort(abs(res));
|
|
v1 = p.nonlins(jj(end),2);
|
|
v2 = p.nonlins(jj(end),3);
|
|
|
|
acc_res1 = sum(abs(res(find((p.nonlins(:,2)==v1) | p.nonlins(:,3)==v1))));
|
|
acc_res2 = sum(abs(res(find((p.nonlins(:,2)==v2) | p.nonlins(:,3)==v2))));
|
|
|
|
if (acc_res1>acc_res2) & ismember(v1,p.branch_variables)
|
|
spliton = v1;
|
|
else
|
|
spliton = v2;
|
|
end
|
|
end
|
|
|
|
function bounds = partition(p,options,spliton,x_min)
|
|
|
|
switch options.bmibnb.branchrule
|
|
case 'omega'
|
|
if ~isempty(x_min)
|
|
bounds = [p.lb(spliton) 0.5*max(p.lb(spliton),min(x_min(spliton),p.ub(spliton)))+0.5*(p.lb(spliton)+p.ub(spliton))/2 p.ub(spliton)];
|
|
else
|
|
bounds = [p.lb(spliton) (p.lb(spliton)+p.ub(spliton))/2 p.ub(spliton)];
|
|
end
|
|
case 'bisect'
|
|
bounds = [p.lb(spliton) (p.lb(spliton)+p.ub(spliton))/2 p.ub(spliton)];
|
|
otherwise
|
|
bounds = [p.lb(spliton) (p.lb(spliton)+p.ub(spliton))/2 p.ub(spliton)];
|
|
end
|
|
|
|
|
|
|
|
function [p,feasible,vol_reduction] = boxreduce(p,upper,lower,lpsolver,options);
|
|
|
|
if options.bmibnb.lpreduce
|
|
|
|
vol_start = prod(p.ub(p.branch_variables)-p.lb(p.branch_variables));
|
|
diag_before = sum(p.ub(p.branch_variables)-p.lb(p.branch_variables));
|
|
diag_before0 = diag_before;
|
|
|
|
[pcut,feasible,lower] = lpbmitighten(p,lower,upper,lpsolver);
|
|
diag_after = sum(pcut.ub(p.branch_variables)-pcut.lb(p.branch_variables));
|
|
iterations = 0;
|
|
while (diag_after/(1e-18+diag_before) < 0.75 ) & feasible & iterations<4
|
|
[pcut,feasible,lower] = lpbmitighten(pcut,lower,upper,lpsolver);
|
|
diag_before = diag_after;
|
|
diag_after = sum(pcut.ub(p.branch_variables)-pcut.lb(p.branch_variables));
|
|
iterations = iterations + 1;
|
|
end
|
|
|
|
% Clean up...
|
|
for i = 1:length(pcut.lb)
|
|
if (pcut.lb(i)>pcut.ub(i)) & (pcut.lb-pcut.ub < 1e-3)
|
|
pcut.lb(i)=pcut.ub(i);
|
|
pcut = updatenonlinearbounds(pcut,i);
|
|
end
|
|
end
|
|
p.lb = pcut.lb;
|
|
p.ub = pcut.ub;
|
|
|
|
% Metric = (V0/V)^(1/n)
|
|
vol_reduction = max(0,min(1,(prod(p.ub(p.branch_variables)-p.lb(p.branch_variables))/(1e-12+vol_start))^(1/length(p.branch_variables))));
|
|
p.lb(p.lb<-1e12) = -inf;
|
|
p.ub(p.ub>1e12) = inf;
|
|
else
|
|
vol_reduction = 1;
|
|
feasible = 1;
|
|
end
|
|
|
|
function output = solvelower(p,options,lowersolver)
|
|
|
|
% ********************************************
|
|
% Convex envelope
|
|
% ********************************************
|
|
%p.binary_variables = [];
|
|
p_with_bilinear_cuts = p;
|
|
p_with_bilinear_cuts.F_struc(1:p.K.f,:)=[];
|
|
p_with_bilinear_cuts = addmcgormick(p_with_bilinear_cuts);
|
|
p_with_bilinear_cuts.F_struc = [p.F_struc(1:p.K.f,:);p_with_bilinear_cuts.F_struc];
|
|
|
|
% **************************************
|
|
% SOLVE NODE PROBLEM
|
|
% **************************************
|
|
if any(p_with_bilinear_cuts.ub<p_with_bilinear_cuts.lb)
|
|
output.problem=1;
|
|
else
|
|
% We are solving relaxed problem (penbmi might be local solver)
|
|
p_with_bilinear_cuts.monomtable = eye(length(p_with_bilinear_cuts.c));
|
|
|
|
% fix implied from mccormick
|
|
% p_with_bilinear_cuts.lb(p.linears) = -inf;
|
|
% p_with_bilinear_cuts.ub(p.linears) = inf;
|
|
% p_with_bilinear_cuts.lb(p.nonlins(:,1)) = -inf;
|
|
% p_with_bilinear_cuts.ub(p.nonlins(:,1)) = inf;
|
|
|
|
output = feval(lowersolver,p_with_bilinear_cuts);
|
|
|
|
relaxed_residual = resids(p_with_bilinear_cuts,output.Primal);
|
|
% Minor clean-up
|
|
output.Primal(output.Primal<p.lb) = p.lb(output.Primal<p.lb);
|
|
output.Primal(output.Primal>p.ub) = p.ub(output.Primal>p.ub);
|
|
end
|
|
|
|
function [p,stack] = selectbranch(p,options,stack,x_min,upper);
|
|
switch options.bmibnb.branchmethod
|
|
case 'maxvol'
|
|
[node,stack] = pull(stack,'maxvol',x_min,upper);
|
|
case 'best'
|
|
[node,stack] = pull(stack,'best',x_min,upper);
|
|
otherwise
|
|
[node,stack] = pull(stack,'best',x_min,upper);
|
|
end
|
|
% Copy node data to p
|
|
if isempty(node)
|
|
p = [];
|
|
else
|
|
p.depth = node.depth;
|
|
p.dpos = node.dpos;
|
|
p.lb = node.lb;
|
|
p.ub = node.ub;
|
|
p.lower = node.lower;
|
|
p.lpcuts = node.lpcuts;
|
|
p.x0 = node.x0;
|
|
end
|
|
|
|
|
|
|
|
function output = solveupper(p,p_original,x,options,uppersolver)
|
|
|
|
p_upper = p_original;
|
|
|
|
% Pick an initial point (this can be a bit tricky...)
|
|
% Use relaxed point, shifted towards center of box
|
|
if all(x<=p.ub) & all(x>=p.lb)
|
|
p_upper.x0 = 0.1*x + 0.9*(p.lb+p.ub)/2;
|
|
else
|
|
p_upper.x0 = (p.lb+p.ub)/2;
|
|
end
|
|
% Shift towards interior for variables with unbounded lower or upper
|
|
lbinfbounds = find(isinf(p.lb));
|
|
ubinfbounds = find(isinf(p.ub));
|
|
p_upper.x0(ubinfbounds) = x(ubinfbounds)+0.01;
|
|
p_upper.x0(lbinfbounds) = x(lbinfbounds)-0.01;
|
|
ublbinfbounds = find(isinf(p.lb) & isinf(p.ub));
|
|
p_upper.x0(ublbinfbounds) = x(ublbinfbounds);
|
|
% ...expand the current node just slightly
|
|
p_upper.lb = p.lb;
|
|
p_upper.ub = p.ub;
|
|
p_upper.lb(~isinf(p_original.lb)) = 0.99*p.lb(~isinf(p_original.lb))+p_original.lb(~isinf(p_original.lb))*0.01;
|
|
p_upper.ub(~isinf(p_original.ub)) = 0.99*p.ub(~isinf(p_original.ub))+p_original.ub(~isinf(p_original.ub))*0.01;
|
|
p_upper.lb(isinf(p_original.lb)) = p_upper.lb(isinf(p_original.lb)) - 0.001;
|
|
p_upper.ub(isinf(p_original.ub)) = p_upper.ub(isinf(p_original.ub)) + 0.001;
|
|
p_upper.options.saveduals = 0;
|
|
|
|
% Solve upper bounding problem
|
|
p_upper.options.usex0 = 1;
|
|
output = feval(uppersolver,p_upper);
|
|
% Project into the box (numerical issue)
|
|
output.Primal(output.Primal<p_upper.lb) = p_upper.lb(output.Primal<p_upper.lb);
|
|
output.Primal(output.Primal>p_upper.ub) = p_upper.ub(output.Primal>p_upper.ub);
|
|
|
|
|
|
% This one needs a lot of work
|
|
function p = nonlinear_constraint_propagation(p)
|
|
|
|
for i = 1:size(p.nonlins,1)
|
|
x = p.nonlins(i,2);
|
|
y = p.nonlins(i,3);
|
|
z = p.nonlins(i,1);
|
|
|
|
if y==x & p.ub(z)>0
|
|
p.ub(x) = min(p.ub(x),sqrt(p.ub(z)));
|
|
p.lb(x) = max(p.lb(x),-sqrt(p.ub(z)));
|
|
end
|
|
|
|
if p.lb(y)>0 & p.ub(z)>0 & p.ub(x)>0
|
|
p.ub(x) = min(p.ub(x),p.ub(z)/p.lb(y));
|
|
end
|
|
if p.lb(x)>0 & p.ub(z)>0 & p.ub(y)>0
|
|
p.ub(y) = min(p.ub(y),p.ub(z)/p.lb(x));
|
|
end
|
|
|
|
if p.ub(y)>0 & p.lb(y)>0 & p.lb(z)>0
|
|
p.lb(x) = max(p.lb(x),p.lb(z)/p.ub(y));
|
|
end
|
|
if p.ub(x)>0 & p.lb(x)>0 & p.lb(z)>0
|
|
p.lb(y) = max(p.lb(y),p.lb(z)/p.ub(x));
|
|
end
|
|
end
|
|
|
|
|
|
function vars = decide_branch_variables(p)
|
|
|
|
if p.options.bmibnb.lowrank==0
|
|
nonlinear = find(~(sum(p.monomtable~=0,2)==1 & sum(p.monomtable,2)==1));
|
|
vars = find(sum(abs(full(p.monomtable(nonlinear,:))),1));
|
|
else
|
|
pool1 = p.nonlins(1,2);
|
|
pool2 = p.nonlins(1,3);
|
|
|
|
for i = 2:size(p.nonlins,1)
|
|
v1 = p.nonlins(i,2);
|
|
v2 = p.nonlins(i,3);
|
|
if v1==v2
|
|
% We are fucked
|
|
pool1 = [pool1 v1];
|
|
pool2 = [pool2 v2];
|
|
else
|
|
if ismember(v1,pool1)
|
|
pool2 = [pool2 v2];
|
|
elseif ismember(v1,pool2)
|
|
pool1 = [pool1 v2];
|
|
elseif ismember(v2,pool1)
|
|
pool2 = [pool2 v1];
|
|
elseif ismember(v2,pool2)
|
|
pool1 = [pool1 v1];
|
|
else
|
|
% No member yet
|
|
pool1 = [pool1 v1];
|
|
pool2 = [pool2 v2];
|
|
end
|
|
end
|
|
end
|
|
pool1 = unique(pool1);
|
|
pool2 = unique(pool2);
|
|
if isempty(intersect(pool1,pool2))
|
|
if length(pool1)<=length(pool2)
|
|
vars = pool1;
|
|
else
|
|
vars = pool2;
|
|
end
|
|
else
|
|
nonlinear = find(~(sum(p.monomtable~=0,2)==1 & sum(p.monomtable,2)==1));
|
|
vars = find(sum(abs(full(p.monomtable(nonlinear,:))),1));
|
|
end
|
|
end
|
|
|
|
|
|
function x = evaluate_nonlinear(p,x);
|
|
x(p.nonlins(:,1)) = x(p.nonlins(:,2)).*x(p.nonlins(:,3));
|
|
|
|
function p = clean_bounds(p)
|
|
% Fix to improve numerica with integer bounds
|
|
%close = find(1e-6>abs(p.ub - round(p.ub)));
|
|
%p.ub(close) = round(p.ub(close));
|
|
close = 1e-6>abs(p.ub - round(p.ub));
|
|
p.ub(close) = round(p.ub(close));
|
|
|
|
close = 1e-6>abs(p.lb - round(p.lb));
|
|
p.lb(close) = round(p.lb(close));
|
|
|
|
p.ub(p.binary_variables) = floor(p.ub(p.binary_variables) + 1e-2);
|
|
%p.lb(p.binary_variables) = ceil(p.lb(p.binary_variables) - 1e-2);
|
|
%p = updatenonlinearbounds(p);
|
|
|
|
% Nothing coded to do non-linear propagation
|
|
%p = nonlinear_constraint_propagation(p);
|
|
p.lb(p.lb<-1e12) = -inf;
|
|
p.ub(p.ub>1e12) = inf;
|
|
|
|
|
|
|
|
function node = savetonode(p,spliton,bounds1,bounds2,direction,x,cost);
|
|
|
|
node.lb = p.lb;
|
|
node.ub = p.ub;
|
|
node.lb(spliton) = bounds1;
|
|
node.ub(spliton) = bounds2;
|
|
if direction == -1
|
|
node.dpos = p.dpos-1/(2^sqrt(p.depth));
|
|
else
|
|
node.dpos = p.dpos+1/(2^sqrt(p.depth));
|
|
end
|
|
node.depth = p.depth+1;
|
|
node.x0 = x;
|
|
node.lpcuts = p.lpcuts;
|
|
node.lower = cost;
|
|
|