I have to optimize an objective using binary integer linear programming, my objective function is:
Maximize f= (c1 * x1) + (c2 * x2) +(c3 * x3) + ... + (c10000 * x10000)
Subject to some constraints
For solving the problem efficiently I want to use some heuristics, according to one of the heuristics, some variables(xi) have more chance to be part of the answer (Xi=1), so my goal is to give priority (preference) to such variables to solve the problem faster than usual way, I know the solution may be sub-optimal but our main concern is time.
So my question are:
How to prioritize this variables in the LP model?
Can we multiply coefficients of this variables by constant (C>1) in order to increase their priority? or decrease priority of other variables by multiply their coefficients by another constant (D<1)?
If we use the approach of question #2, do we have to do that just with objective function coefficients or both of objective function coefficients and constraints coefficients should be altered related to those variables?
It should be noted that in the approach of question #2, after solving the LP model, we rollback any of changes in the coefficients according to the solution (Which variables are in the solution).
Thanks in advance
If you know that xi will be part of solution, you should include it as 1 into initial point x0 you pass to bintprog. The same for xj known to be likely not part of solution should be included as 0. If initial point is very close to the solution, this will reduce time to find it.
x = bintprog(f,A,b,Aeq,beq,x0);
Another option is to relax BILP problem to LP problem with adding two extra conditions
x <= 1
-x <= 0
and then using rounded solution for this problem as initial point for BILP problem.
Here authors state that bintprog performs well only on small problems. As I use Octave instead of Matlab, I tried GNU Linear Programming Kit (glpk). I tried to solve BILP problem from Matlab documentation and here is a script
close all; clear all;
f = [25,35,28,20,40,-10,-20,-40,-18,-36,-72,-11,-22,-44,-9,-18,-36,-10,-20]';
A = zeros(14,19);
A(1,1:19) = [25 35 28 20 40 5 10 20 7 14 28 6 12 24 4 8 16 8 16];
A(2,1) = 1; A(2,6) = -1; A(2,7) = -1; A(2,8) = -1;
A(3,2) = 1; A(3,9) = -1; A(3,10) = -1; A(3,11) = -1;
A(4,3) = 1; A(4,12) = -1; A(4,13) = -1; A(4,14) = -1;
A(5,4) = 1; A(5,15) = -1; A(5,16) = -1; A(5,17) = -1;
A(6,5) = 1; A(6,18) = -1; A(6,19) = -1;
A(7,1) = -5; A(7,6) = 1; A(7,7) = 2; A(7,8) = 4;
A(8,2) = -4; A(8,9) = 1; A(8,10) = 2; A(8,11) = 4;
A(9,3) = -5; A(9,12) = 1; A(9,13) = 2; A(9,14) = 4;
A(10,4) = -7; A(10,15) = 1; A(10,16) = 2; A(10,17) = 4;
A(11,5) = -3; A(11,18) = 1; A(11,19) = 2;
A(12,2) = 1; A(12,5) = 1;
A(13,1) = 1; A(13,2) = -1; A(13,3) = -1;
A(14,3) = -1; A(14,4) = -1; A(14,5) = -1;
b = [125 0 0 0 0 0 0 0 0 0 0 1 0 -2]';
lb = zeros(size(f));
ub = ones(size(f));
ctype = repmat("U" , size(b))'; # inequality constraint
sense = 1; # minimization
param.msglev = 0;
vartype = repmat("C" , size(f)); # continuous variables
tic
for i = 1:10000
[xopt, fmin, errnum, extra] = glpk (f, A, b, lb, ub, ctype, vartype, sense, param);
end
toc
fprintf('Solution %s with value %f\n', mat2str(xopt), fmin)
vartype = repmat("I" , size(f)); # integer variables
tic
for i = 1:10000
[xopt, fmin, errnum, extra] = glpk (f, A, b, lb, ub, ctype, vartype, sense, param);
end
toc
fprintf('Solution %s with value %f\n', mat2str(xopt), fmin)
These are found solutions:
Elapsed time is 7.9 seconds.
Solution [0;0.301587301587301;1;1;0;0;0;0;0;0.603174603174603;0;1;1;0.5;1;1;1;0;0] with value -81.158730
Elapsed time is 11.5 seconds.
Solution [0;0;1;1;0;0;0;0;0;0;0;1;0;1;1;1;1;0;0] with value -70.000000
I had to perform 10000 iterations to make performance difference visible as the problem is still quite small. LP solution is faster comparing to BILP solution, and they are quite close.
According to CPLEX Performance Tuning for Mixed Integer Programs and Issuing priority orders we can set priority orders to increase or decrease priority of some variables in CPLEX, this approach is as follows:
options = cplexoptimset('cplex');
options.mip.ordertype=fsl;
[x,fval,exitflag,output] = cplexbilp(f, Aineq, bineq, Aeq, beq,[],options);
fsl is priority array for problem variables.
Because CPLEX can generate a priority order automatically, based on problem-data characteristics, we can leave the prioritization decision to CPLEX as follows:
value branching priority order
===== ========================
0 no automatic priority order will be generated (default)
1 decreasing cost coefficients among the variables
2 increasing bound range among the variables
3 increasing cost per matrix coefficient count among the variables
After using priorities, my problem is solved, the solution is valid and converging to solution is faster than before!
Related
I have got two sine waves shifted 180 degrees from each other. I would like to create another signal from these two, but for that I have to add separate intervals separately and the resultant should be continuous as well.
Here are two sine waves:
t = 0:0.00001:0.02;
w= 2*pi*50;
ma = 0.8*sin(w*t);
mb = 0.8*sin(w*t-pi);
Now I want to create another signal mcm. For an interval "0 to 0.005 (quarter cycle)" I want mcm = 1 + ma. For interval "0.005 to 0.01" I want mcm = 1 + mb.
And likewise for the other two quarters.
How do we go about doing it?
Edit after the question was changed in the comment below
Given the need to generalise this to multiple cycles (note the addition of the user defined input n = number of cycles), here is what I would suggest.
Firstly, each function has to be explicitly defined. Secondly, for each point along the length of t, the function that needs to be used at that data point needs to be (explicitly) defined.
Thus in the code below, functions and functionOrder are used to set the functions to be used, and the order/frequency in which they are to be used.
In the Calculate mcm section, a variable ind is used to iterate over the output, and select the function to be used at each point.
% User inputs
f = 50;
n = 3; % number of cycles
inc = 0.00001; % increment
% Preliminaries
t_interval = 1/f;
t = 0:inc:t_interval*n;
t = t(1:end-1);
w = 2*pi*f;
ma = 0.8*sin(w*t);
mb = 0.8*sin(w*t-pi);
% Define mcm
f1 = #(ii) -1 - mb(ii);
f2 = #(ii) 1 - ma(ii);
f3 = #(ii) -1 + mb(ii);
f4 = #(ii) 1 - mb(ii);
functions = {f1, f2, f3, f4};
functionOrder = [1 2 3 4];
% Calculate mcm
ind = repmat(functionOrder, round(t_interval/inc)/length(functionOrder), 1);
ind = reshape(ind, numel(ind), 1);
ind = repmat(ind, n, 1);
mcm = zeros(size(ind));
for ii = 1:length(ind)
mcm(ii) = functions{ind(ii)}(ii);
end
% Plot
figure; plot(t,ma);
hold on; plot(t,mb);
plot(t, mcm);
================================================
Previous answer to the simpler question, without the need for generalisability
The way I would approach this would be a compromise between ease of use in the current situation and ease of replication/dynamism as the example changes.
First create a variable that is in indicator of where ma should be used and mb should be used. Note that using 0 and 1 makes future steps easier: if you had more functions to utilise in this piecewise operation, the construction of this indicator would have to be different.
ind = zeros(size(t)); %indicator
ind(t >= 0.000 && t < 0.005) = 1;
ind(t >= 0.010 && t <=0.015) = 1;
Then, the addition of ma and mb is relatively straightforward.
mcm = 1 + ind.*ma + (1-ind).*mb;
[Note that this follows the mathematical formulation described in the text, but the resulting curve is not continuous. I do not see a way to do this so that ma and mb alternate every quarter cycle and the curve is also continuous.]
In Matlab, I have two single row (1x249) vectors in a 2x249 matrix and I have to create a matrix A by replicating them many times, each time shifting the vectors of 2 positions to the right. I would like to fill the entries on the left with zeros. Is there a smart way to do this? Currently, I am using a for loop and circshift, and I add at each iteration I add the new row to A, but probably this is highly inefficient.
Code (myMat is the matrix I want to shift):
A = [];
myMat = [1 0 -1 zeros(1,246); 0 2 0 -2 zeros(1,245)];
N = 20;
for i=1:N-1
aux = circshift(myMat,[0,2*(i-1)]);
aux(:,1:2*(i-1)) = 0;
A =[A; aux];
end
As you are probably aware, loops in Matlab are not so efficient.
I know that the Mathworks keep saying this is no longer so with JIT
compilation, but I haven't experienced the fast loops yet.
I put your method for constructiong the matrix A in a function:
function A = replvector1(myMat,shift_right,width,N)
pre_alloc = true; % make implementation faster using pre-allocation yes/no
% Pad myMat with zeros to make it wide enough
myMat(1,width)=0;
% initialize A
if pre_alloc
A = zeros(size(myMat,1)*(N-1),width);
else
A = [];
end
% Fill A
for i=1:N-1
aux = circshift(myMat,[0,shift_right*(i-1)]);
aux(:,1:min(width,shift_right*(i-1))) = 0;
A(size(myMat,1)*(i-1)+1:size(myMat,1)*i,:) =aux;
end
Your matrix-operation looks a lot like a kronecker product, but the
block-matrixces have overlapping column ranges so a direct kronecker product
will not work. Instead, I constructed the following function:
function A = replvector2(myMat,shift_right,width,N)
[i,j,a] = find(myMat);
i = kron(ones(N-1,1),i) + kron([0:N-2]',ones(size(i))) * size(myMat,1);
j = kron(ones(N-1,1),j) + kron([0:N-2]',ones(size(j))) * shift_right;
a = kron(ones(N-1,1),a);
ok = j<=width;
A = full(sparse(i(ok),j(ok),a(ok),(N-1)*size(myMat,1),width));
You can follow the algorithm by removing semicolons and looking at intermediate
results.
The following main program runs your example, and can easily be modified to
run similar examples:
% inputs (you may vary them to see that it always works)
shift_right = 2;
width = 249;
myMat1 = [ 1 0 -1 0 ;
0 2 0 -2 ];
N = 20;
% Run your implementation
tic;
A = replvector1(myMat,shift_right,width,N);
disp(sprintf('\n original implementation took %e sec',toc))
% Run the new implementation
tic;
B = replvector2(myMat,shift_right,width,N);
disp(sprintf(' new implementation took %e sec',toc))
disp(sprintf('\n norm(B-A)=%e\n',norm(B-A)))
I've taken Nathan's code (see his answer to this question), and added another possible implementation (replvector3).
My idea here stems from you not really needing a circular shift. You need to right-shift and add zeros to the left. If you start with a pre-allocated array (this is really where the big wins in time are for you, the rest is peanuts), then you already have the zeros. Now you just need to copy over myMat to the right locations.
These are the times I see (MATLAB R2017a):
OP's, with pre-allocation: 1.1730e-04
Nathan's: 5.1992e-05
Mine: 3.5426e-05
^ shift by one on purpose, to make comparison of times easier
This is the full copy, copy-paste into an M-file and run:
function so
shift_right = 2;
width = 249;
myMat = [ 1 0 -1 0 ;
0 2 0 -2 ];
N = 20;
A = replvector1(myMat,shift_right,width,N);
B = replvector2(myMat,shift_right,width,N);
norm(B(:)-A(:))
C = replvector3(myMat,shift_right,width,N);
norm(C(:)-A(:))
timeit(#()replvector1(myMat,shift_right,width,N))
timeit(#()replvector2(myMat,shift_right,width,N))
timeit(#()replvector3(myMat,shift_right,width,N))
% Original version, modified to pre-allocate
function A = replvector1(myMat,shift_right,width,N)
% Assuming width > shift_right * (N-1) + size(myMat,2)
myMat(1,width) = 0;
M = size(myMat,1);
A = zeros(M*(N-1),width);
for i = 1:N-1
aux = circshift(myMat,[0,shift_right*(i-1)]);
aux(:,1:shift_right*(i-1)) = 0;
A(M*(i-1)+(1:M),:) = aux;
end
% Nathan's version
function A = replvector2(myMat,shift_right,width,N)
[i,j,a] = find(myMat);
i = kron(ones(N-1,1),i) + kron((0:N-2)',ones(size(i))) * size(myMat,1);
j = kron(ones(N-1,1),j) + kron((0:N-2)',ones(size(j))) * shift_right;
a = kron(ones(N-1,1),a);
ok = j<=width;
A = full(sparse(i(ok),j(ok),a(ok),(N-1)*size(myMat,1),width));
% My trivial version with loops
function A = replvector3(myMat,shift_right,width,N)
% Assuming width > shift_right * (N-1) + size(myMat,2)
[M,K] = size(myMat);
A = zeros(M*(N-1),width);
for i = 1:N-1
A(M*(i-1)+(1:M),shift_right*(i-1)+(1:K)) = myMat;
end
I need to solve the following non-linear problem with constraints but I am not sure to use the appropriate function and solver as changing from the algorithm: 'interior-point' to 'sqp' give different answers. Could you help first to make sure fmincon is the most appropriate function here and also if the options should be set up in a different way to ensure that the optimization does not stop too early. I have tried to set up: 'MaxFunctionEvaluations' to 1000000 and 'MaxIterations'to 10000 and this helps but the time required increases as well dramatically.
w is a vector whose values have to be optimized.
scores is a vector of the same size as w.
the size if w and scores is usually between 10 and 40.
Here is the optimization setup:
objective function: sum(log(abs(w)));
linear inequalities1: w_i > 0 if scores_i > 0
linear inequalities2: w_i < 0 if scores_i < 0
non-linear equalities1: sum(abs(w)) = 1
non-linear inequalities1: sqrt(w * sigma * w') <= 0.1
Here is my code:
N = numel(scores);
%%%LINEAR EQUALITIES CONSTRAINTS
Aeq = [];
beq = [];
%%%LINEAR INEQUALITIES CONSTRAINTS
temp = ones(0,N);
temp(scores >= 0) = -1;
temp(scores < 0) = 1;
A = diag(temp);
b = zeros(N,1);
%%%BOUNDs CONSTRAINTS
lb = [];
ub = [];
w0 = ones(1,N)/N;
nonlcon = #(w)nonlconstr(w,sigma,tgtVol);
options = optimoptions('fmincon','Algorithm','interior-point','MaxFunctionEvaluations',1000000,'MaxIterations',10000);
%options = optimoptions('fmincon','Algorithm','interior-point','OptimalityTolerance',1e-7);
[w,fval,exitflag,output] = fmincon(#(w)objectfun(w),w0,A,b,Aeq,beq,lb,ub,nonlcon,options);
function [c,ceq] = nonlconstr(w,sigma,tgtVol)
c = sqrt(w*sigma*w') - tgtVol;
ceq = sum(abs(w)) - 1;
end
function f = objectfun(w)
f = sum(-log(abs(w)));
end
I am very new to using IBM CPLEX , and am using CPLEX with Matlab. I was wondering how to compile a custom objective function in CPLEX using Matlab. The objective function is as follows:
Here aj is a column vector of size 36000 X 1 and A is a sparse matrix of size 36000 x 4503. wj is a column vector of size 4503 x 1 of optimization variables. Until now this is a simple cplexlsqnonneglin if we include the wj >=0 constraint. But I would also like to include the two other sum terms with 'beta' and lambda and the wjj = 0 constraint. Any help in recreating this optimization problem in CPLEX would be much appreciated.
Thanks in advance!
When you add the other terms into the objective, your problem becomes a general quadratic program. Since wj >= 0 we have that ||wj||_1 = e'*wj. So we can write your problem as:
minimize 0.5*(aj - A*wj)'*(aj - A*wj) + 0.5*beta*wj'*wj + lambda*e'*w
wj
subject to wj >= 0, wj(j) = 0
After pushing the quadratic terms in the objective together we have the following QP:
minimize 0.5*aj'*aj -aj'*A*wj + 0.5*wj'*(A'*A + beta*I)*w + lambda*e'*w
wj
subject to wj >= 0, w(j) = 0
I can't help you with CPLEX. But you can solve this problem with Gurobi in MATLAB using the following code
m = 36000;
n = 4503;
A = sprand(m, n, .01);
aj = rand(m, 1);
lambda = 0.1;
beta = 0.4;
j = 300;
model.objcon = 0.5*aj'*aj;
model.obj = -aj'*A + lambda*ones(1,n);
model.A = sparse(1, n);
model.sense = '=';
model.rhs = 0;
model.Q = 0.5*(A'*A + beta*speye(n));
model.vtype = 'C';
model.lb = zeros(n, 1);
model.ub = inf(n,1);
model.ub(j) = 0; % set 0 <= wj(j) <= 0
params.outputflag = 1;
result = gurobi(model, params);
if strcmp(result.status, 'OPTIMAL')
wj = result.x(1:n);
end
For more details see the documentation on Gurobi's MATLAB interface:
http://www.gurobi.com/documentation/5.6/reference-manual/matlab_gurobi
Note you may want to create variables and constraints to avoid forming A'*A + beta*I in the objective. For example you could create a new variable r and a constraint r = A*wj. Then the objective wj'*(A'*A + beta*I) wj would become r'*r + beta*wj'*wj. This may help with the numerics.
I have the following program
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
for i=1:500000
omegan=1.+0.0001*i;
a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;
if(abs(det(a))<1E-10) sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
end
end
Analytical solution of the above system, and the same program written in fortran gives out values of omegan equal to 16.3818 and 32.7636 (fortran values; analytical differ a little, but they're there somewhere).
So, now I'm wondering ... where am I going wrong with this ? Why is matlab not giving the expected results ?
(this is probably something terribly simple, but it's giving me headaches)
You're looking for too small of determinant values because Matlab is using a different determinant function (or some other reason like something to do with the floating point accuracy involved in the two different methods). I'll show you that Matlab is essentially giving you the correct values and a better way to approach this problem in general.
First, let's take your code and change it slightly.
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
vals = zeros(1,500000);
for i=1:500000
omegan=1.+0.0001*i;
a(1,1) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(1,2) = 2; a(1,3) = 0; a(1,4) = 0;
a(2,1) = 1; a(2,2) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(2,3) = 1; a(2,4) = 0;
a(3,1) = 0; a(3,2) = 1; a(3,3) = ((omegan^2)*(Jm/(G*J))*d^2)-2; a(3,4) = 1;
a(4,1) = 0; a(4,2) = 0; a(4,3) = 2; a(4,4) = ((omegan^2)*(Jm/(G*J))*d^2)-2;
vals(i) = abs(det(a));
if(vals(i)<1E-10)
sprintf('omegan= %8.3f det= %8.3f',omegan,det(a))
end
end
plot(1.+0.0001*(1:500000),log(vals))
All that I've done really is logged the values of the determinant for all values of omegan and plotted the log of those determinant values as a function of omegan. Here is the plot:
You notice three major dips in the graph. Two coincide with your results of 16.3818 and 32.7636, but there is also an additional one which you were missing (probably because your condition of the determinant being less than 1e-10 was too low even for your Fortran code to pick it up). Therefore, Matlab is also telling you that those are the values of omegan that you were looking for, but because of the determinant was determined in a different manner in Matlab, the values weren't the same - not surprising when dealing with badly conditioned matrices. Also, it probably has to do with Fortran using single precision floats as someone else said. I'm not going to look into why they aren't because I don't want to waste my time on that. Instead, let's look at what you are trying to do and try a different approach.
You, as I'm sure you are aware, are trying to find the eigenvalues of the matrix
a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0 0 2 -2]];
, set them equal to
-omegan^2*(Jm/(G*J)*d^2)
and solve for omegan. This is how I went about it:
format compact; format short g; clear; clc;
L = 140; J = 77; Jm = 10540; G = 0.8*10^8; d = L/3;
C1 = (Jm/(G*J)*d^2);
a = [[-2 2 0 0]; [1 -2 1 0]; [0 1 -2 1]; [0,0,2,-2]];
myeigs = eig(a);
myeigs(abs(myeigs) < eps) = 0.0;
for i=1:4
sprintf('omegan= %8.3f', sqrt(-myeigs(i)/C1))
end
This gives you all four solutions - not just the two that you had found with your Fortran code (though one of them, zero, was outside of your testing range for omegan ). If you want to go about solving this by checking the determinant in Matlab, as you've been trying to do, then you'll have to play with the value that you're checking the absolute value of the determinant to be less than. I got it to work for a value of 1e-4 (it gave 3 solutions: 16.382, 28.374, and 32.764).
Sorry for such a long solution, but hopefully it helps.
Update:
In my first block of code above, I replaced
vals(i) = abs(det(a));
with
[L,U] = lu(a);
s = det(L);
vals(i) = abs(s*prod(diag(U)));
which is the algorithm that det is supposedly using according to the Matlab docs. Now, I am able to use 1E-10 as the condition and it works. So maybe Matlab isn't calculating the determinant exactly as the docs say? This is kind of disturbing.
New answer:
You can investigate this problem using symbolic equations, which gives me the correct answers:
>> clear all %# Clear all existing variables
>> format long %# Display more digits of precision
>> syms Jm d omegan G J %# Your symbolic variables
>> a = ((Jm*(d*omegan)^2)/(G*J)-2).*eye(4)+... %# Create the matrix a
diag([2 1 1],1)+...
diag([1 1 2],-1);
>> solns = solve(det(a),'omegan') %# Solve for where the determinant is 0
solns =
0
0
(G*J*Jm)^(1/2)/(Jm*d)
-(G*J*Jm)^(1/2)/(Jm*d)
-(2*(G*J*Jm)^(1/2))/(Jm*d)
(2*(G*J*Jm)^(1/2))/(Jm*d)
(3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)
-(3^(1/2)*(G*J*Jm)^(1/2))/(Jm*d)
>> solns = subs(solns,{G,J,Jm,d},{8e7,77,10540,140/3}) %# Substitute values
solns =
0
0
16.381862247021893
-16.381862247021893
-32.763724494043785
32.763724494043785
28.374217734436371
-28.374217734436371
I think you either just weren't choosing values in your loop close enough to the solutions for omegan or your threshold for how close the determinant is to zero is too strict. When I plug in the given values to a, along with omegan = 16.3819 (which is the closest value to one solution your loop produces), I get this:
>> det(subs(a,{omegan,G,J,Jm,d},{16.3819,8e7,77,10540,140/3}))
ans =
2.765476845475786e-005
Which is still larger in absolute amplitude than 1e-10.
I put this as an answer because I cannot paste this into a comment: Here's how Matlab calculates the determinant. I assume the rounding errors come from calculating the product of multiple diagonal elements in U.
Algorithm
The determinant is computed from the
triangular factors obtained by
Gaussian elimination
[L,U] = lu(A) s = det(L)
%# This is always +1 or -1
det(A) = s*prod(diag(U))