user20050294
user20050294

Reputation:

Linear Programming in 'scipy.linprog' with non-standard functions

I am trying to learn about implementation of linear programming (LP) problems in scipy.linprog. I understand how it works with basic functions, for example:

max 2x+3y
st. 2x-y <= 0
    5x+y >= -10
from scipy.optimize import linprog
c = [-2, -3]
A = [[2, -1], [-5, -1]]
b = [0, 10]

Now, when it comes to more complex cases, like this there are quite a few things I don't understand.

First, I don't understand how and why Minimize 1*abs(x_0) + 1*abs(x_1) + ... + 1*abs(x_n)) is changed to Minimize 1*y_0 + 1*y_1 + ... + 1*y_n by adding the following constraint -y_i <= x_i <= y_i // for i in n. EDIT: I understand now that y_i is a slack variable, which also is the reason that the A_aux matrix is added. But I don't understand that shape of the Matrix.

Second, here they use the constraint to c = np.hstack((np.zeros(N), np.ones(N_AUX))). In my example above, there are two variables, which gives a list of the length 2. But here there are N variables, but c is a list of N*2? In my example it is easy to understand the values, but here they are [0,0,0,0,0,1,1,1,1,1] and I don't see any hint in the function to why these values are chosen.

Third, I understand the A_orig matrix.

A_orig = [[0, 1, -1, 0, 0, 0, 0, 0, 0, 0],   # orig constraint 1
          [0, 0, 1, -1, 0, 0, 0, 0, 0, 0],   # orig constraint 2
          [-1, -1, 0, 0, 0, 0, 0, 0, 0, 0],  # more interesting problem
          [0, -1, -1, 0, 0, 0, 0, 0, 0, 0]]  # ""   ""          ""

Each row simply represents the number of x_i and y_i for i in range(1,6) for every constraint.

But then a second matrix A_aux is added:

A_aux = [[-1, 0, 0, 0, 0, -1, 0, 0, 0, 0],
         [0, -1, 0, 0, 0, 0, -1, 0, 0, 0],
         [0, 0, -1, 0, 0, 0, 0, -1, 0, 0],
         [0, 0, 0, -1, 0, 0, 0, 0, -1, 0],
         [0, 0, 0, 0, -1, 0, 0, 0, 0, -1],
         [1, 0, 0, 0, 0, -1, 0, 0, 0, 0],
         [0, 1, 0, 0, 0, 0, -1, 0, 0, 0],
         [0, 0, 1, 0, 0, 0, 0, -1, 0, 0],
         [0, 0, 0, 1, 0, 0, 0, 0, -1, 0],
         [0, 0, 0, 0, 1, 0, 0, 0, 0, -1]]

Which is a skew-symmetric matrix. And I have no idea and can't find any information about why this is added. I don't understand why it is 10*10 either.

Finally, bounds are added. bnds = [(0, 50) for i in range(N)] + [(None, None) for i in range(N_AUX)] # some custom bounds. Here I find it strange that they set (0,50) for the first 5 rows, and then no bounds for the last 5 rows. I don't see any indication to why it should be bounded by 50?

There are quite a lot of questions here. But if someone could point me in the right direction I would be very grateful.

Thank you.

Upvotes: 0

Views: 708

Answers (1)

joni
joni

Reputation: 7157

This is rather a math question than a programming question. Anyway, to answer your other questions:

In my example above, there are two variables, which gives a list of the length 2. But here there are N variables, but c is a list of N*2?

You need to introduce an additional variable y_i for each abs(x_i). You have N terms abs(x_i), therefore you'll have N+N = 2*N variables after linearizing/reformulating all the absolute values. Each variable x doesn't occurs in the objective in the reformulation, therefore each coefficient of x_i is zero in the objective. And obviously, each one of y_i is 1.

Which is a skew-symmetric matrix. And I have no idea and can't find any information about why this is added.

You need the matrix A_aux to model the constraints -y_i <= x_i <= y_i. Both y_i and x_i are optimization variables, so you can't pass this constraints as variable bounds. This only works if the bounds are given constants like -1 <= x_i = 2.

Let's say you have the constraints -y_1 <= x_1 <= y_1 and -y_2 <= x_2 <= y_2. This is the same as:

-x_1 - y_1 <= 0
 x_1 - y_1 <= 0
-x_2 - y_2 <= 0
 x_2 - y_2 <= 0

Adding the hidden zeros, this is the same as

-1*x_1 + 0*x_2 - 1*y_1 + 0*y_2 <= 0
 1*x_1 + 0*x_2 - 1*y_1 + 0*y_2 <= 0
 0*x_1 - 1*x_2 + 0*y_1 - 1*y_2 <= 0
 0*x_0 + 1*x_2 + 0*y_1 - 1*y_2 <= 0

Can you see it now? It's just a simply matrix vector product:

(-1  0  -1   0 ) (x_1) <= 0
( 1  0  -1   0 ) (x_2) <= 0
( 0 -1   0  -1 ) (y_1) <= 0
( 0  1   0  -1 ) (y_2) <= 0

Here, the left matrix is just A_aux. If you change the row order, you'll get exactly the same matrix as in your linked answer.

Finally, bounds are added. bnds = [(0, 50) for i in range(N)] + [(None, None) for i in range(N_AUX)] # some custom bounds. Here I find it strange that they set (0,50) for the first 5 rows, and then no bounds for the last 5 rows. I don't see any indication to why it should be bounded by 50?

The first N variables (x) are non-negative after the reformulation. The N addition variables y are unbounded, i.e. can be negative or positive, there there are no bounds. However, there's no real need to bound the x variables with 50 from above. It's only meant to be a sharper bound of the optimization variables based on the right-hand side of the other linear constraints in the example.

PS: I can second Sascha's comment in your linked answer, that you should prefer a library like cvxpy if you don't want to do all these transformations/reformulations on your own.

Upvotes: 1

Related Questions