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 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=-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.ubp.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.Primalp_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;