Linearization of non-linear program or other solver - linear-programming

I have a linear program that represent a time series of actions (the variables are ordered). The objective function is MIN. For every variable I have the constraint
Xi <= max_value.
I want the constraint to represent the real world more precisely, so I need to change that constrain as follow:
if sum(X1,...,Xi-1) > some_value then Xi <= max_value_1, else Xi <= max_value_2.
Is there a way to make it linear?
If not, is there a ready made solver (like ortools) for this?
Thanks

Provided your variable are integral, or can be scaled up to be integral, you can use CP-SAT for this.
This is explained in the section of the documentation.

if sum(X1,...,Xi-1) > some_value then Xi <= max_value_1, else Xi <= max_value_2.
y(i) = sum(X(1),...,X(i-1))
y(i) <= (1-δ(i))*some_value + δ(i)*M
y(i) >= δ(i)*(some_value+0.00001) - (1-δ(i))*M
X(i) <= max_value_1*δ(i) + max_value_2*(1-δ(i))
δ(i) ∈ {0,1}
Here M is a large enough constant, and y is an extra continuous variable.

Related

Pulp - LP Objective function formulation

I am working on solving a set covering problem for electric vehicle charging stations. My objective is to maximize the demand covered by the radius of a charging station.
I have two variables to make up the objective function.
Yij denotes the demand location i is covered by the radius of charging station j.
Similarly, Xj denotes if charging station j is open.
I am looking to create an objective function such as the following:
Maximize OF = ((Y11 + Y21+ Y31 + .... Yn1) * X1) + ((Y12 + Y22+ Y32 + .... Yn2) * X2) + ....
I tried the following, but am running into issues:
OptModel += lpSum(((Y[i,j] for i in range (I)) * X[j]) for j in range(J))
Any idea on how to formulate this?
It isn't clear from your description why Y is a variable? It sounds like it should be a parameter (known value) if the demand location is within the radius of some source.... There must be some other nuance as to why this isn't known. (If it can be calculated, do it and make it a parameter and your problem is solved.)
The statement you propose is illegal because you are multiplying variables together and that makes the statement non-linear. You need to reformulate....
You have an implicit "and" condition in there in that you only want to receive credit if both Y and X are true, so you will need an additional variable or be clever in how you relate X and Y because you can't multiply them.
Why don't you just add this constraint:
Y[i, j] <= X[j] for each j
that would essentially change the meaning of Y to "in range of an operating charger".
Also realize if you sum up all of these Y vars (or as you appear to try to do in your objective) you will get double counting of any demand that can be charged from multiple stations--not sure if that is intent or not.

Gurobi constraints and objective function

I am very new to Gurobi. I am trying to solve the following ILP
minimize \sum_i c_i y_i + \sum_i \sum_j D_{ij} x_{ij}
Here D is stored as a 2D numpy array.
My constraints are as follows
x_{ij} <= y_i
y_i + \sum_j x_{ij} = 1
Here's the image of the algebra :
My code so far is as follows,
from gurobipy import *
def gurobi(D,c):
n = D.shape[0]
m = Model()
X = m.addVars(n,n,vtype=GRB.BINARY)
y = m.addVars(n,vtype=GRB.BINARY)
m.update()
for j in range(D.shape[0]):
for i in range(D.shape[0]):
m.addConstr(X[i,j] <= y[i])
I am not sure about, how to implement the second constraint and specify the objective function, as objective terms includes a numpy array. Any help ?
Unfortunately I don't have GUROBI because it's really expensive...
but, according to this tutorial the second constraint should be implemented like this :
for i in range(n):
m.addConstr(y[i] + quicksum(X[i,j] for j in range(n), i) == 1)
while the objective function can be defined as :
m.setObjective(quicksum(c[i]*y[i] for i in range(n)) + quicksum(quicksum(D[i,j] * x[i,j]) for i in range(n) for j in range(n)), GRB.MINIMIZE)
N.B: I'm assuming D is a matrix n x n
This is a very simple case. You can write the first constraint this way. It is a good habit to name your constraints.
m.addConstrs((x[i,j] <= y[j] for i in range(D.shape[0]) for j in range(D.shape[0])), name='something')
If you want to add the second constraint, you can write it like this
m.addConstrs((y[i] + x.sum(i, '*') <= 1 for i in range(n)), name='something')
you could write the second equations ass well using quicksum as suggested by digEmAll.
The advantage of using quicksum is that you can add if condition so that you don't um over all values of j. Here is how you could do it
m.addConstrs((y[i] + quicksum(x[i, j] for j in range(n)) <= 1 for i in range(n)), name='something')
if you only needed some values of j to sum over then you could:
m.addConstrs((y[i] + quicksum(x[i, j] for j in range(n) if j condition) <= 1 for i in range(n)), name='something')
I hope this helps

Converting conditional constraints to linear constraints in Linear Programming

I have two variables: x>= 0 and y binary (either 0 or 1), and I have a constant z >= 0. How can I use linear constraints to describe the following condition:
If x = z then y = 1 else y = 0.
I tried to solve this problem by defining another binary variable i and a large-enough positive constant U and adding constraints
y - U * i = 0;
x - U * (1 - i) = z;
Is this correct?
Really there are two classes of constraints that you are asking about:
If y=1, then x=z. For some large constant M, you could add the following two constraints to achieve this:
x-z <= M*(1-y)
z-x <= M*(1-y)
If y=1 then these constraints are equivalent to x-z <= 0 and z-x <= 0, meaning x=z, and if y=0, then these constraints are x-z <= M and z-x <= M, which should not be binding if we selected a sufficiently large M value.
If y=0 then x != z. Technically you could enforce this constraint by adding another binary variable q that controls whether x > z (q=1) or x < z (q=0). Then you could add the following constraints, where m is some small positive value and M is some large positive value:
x-z >= mq - M(1-q)
x-z <= Mq - m(1-q)
If q=1 then these constraints bound x-z to the range [m, M], and if q=0 then these constraints bound x-z to the range [-M, -m].
In practice when using a solver this usually will not actually ensure x != z, because small bounds violations are typically allowed. Therefore, instead of using these constraints I would suggest not adding any constraints to your model to enforce this rule. Then, if you get a final solution with y=0 and x=z, you could adjust x to take value x+epsilon or x-epsilon for some infinitesimally small value of epsilon.
So I change the conditional constraints to
if x = z then y = 0 else y = 1
Then the answer will be
x - z <= M * y;
x - z >= m * y;
where M is a large enough positive number, m is a small enough positive number.

How do I encode Manhattan distance in Mixed Integer Programming

Lets have two points, (x1, y1) and (x2,y2)
dx = |x1 - x2|
dy = |y1 - y2|
D_manhattan = dx + dy where dx,dy >= 0
I am a bit stuck with how to get x1 - x2 positive for |x1 - x2|, presumably I introduce a binary variable representing the polarity, but I am not allowed multiplying a polarity switch to x1 - x2 as they are all unknown variables and that would result in a quadratic.
If you are minimizing an increasing function of |x| (or maximizing a decreasing function, of course),
you can always have the aboslute value of any quantity x in a lp as a variable absx such as:
absx >= x
absx >= -x
It works because the value absx will 'tend' to its lower bound, so it will either reach x or -x.
On the other hand, if you are minimizing a decreasing function of |x|, your problem is not convex and cannot be modelled as a lp.
For all those kind of questions, it would be much better to add a simplified version of your problem with the objective, as this it often usefull for all those modelling techniques.
Edit
What I meant is that there is no general solution to this kind of problem: you cannot in general represent an absolute value in a linear problem, although in practical cases it is often possible.
For example, consider the problem:
max y
y <= | x |
-1 <= x <= 2
0 <= y
it is bounded and has an obvious solution (2, 2), but it cannot be modelled as a lp because the domain is not convex (it looks like the shape under a 'M' if you draw it).
Without your model, it is not possible to answer the question I'm afraid.
Edit 2
I am sorry, I did not read the question correctly. If you can use binary variables and if all your distances are bounded by some constant M, you can do something.
We use:
a continuous variable ax to represent the absolute value of the quantity x
a binary variable sx to represent the sign of x (sx = 1 if x >= 0)
Those constraints are always verified if x < 0, and enforce ax = x otherwise:
ax <= x + M * (1 - sx)
ax >= x - M * (1 - sx)
Those constraints are always verified if x >= 0, and enforce ax = -x otherwise:
ax <= -x + M * sx
ax >= -x - M * sx
This is a variation of the "big M" method that is often used to linearize quadratic terms. Of course you need to have an upper bound of all the possible values of x (which, in your case, will be the value of your distance: this will typically be the case if your points are in some bounded area)

Probability density function from a paper, implemented using C++, not working as intended

So i'm implementing a heuristic algorithm, and i've come across this function.
I have an array of 1 to n (0 to n-1 on C, w/e). I want to choose a number of elements i'll copy to another array. Given a parameter y, (0 < y <= 1), i want to have a distribution of numbers whose average is (y * n). That means that whenever i call this function, it gives me a number, between 0 and n, and the average of these numbers is y*n.
According to the author, "l" is a random number: 0 < l < n . On my test code its currently generating 0 <= l <= n. And i had the right code, but i'm messing with this for hours now, and i'm lazy to code it back.
So i coded the first part of the function, for y <= 0.5
I set y to 0.2, and n to 100. That means it had to return a number between 0 and 99, with average 20.
And the results aren't between 0 and n, but some floats. And the bigger n is, smaller this float is.
This is the C test code. "x" is the "l" parameter.
//hate how code tag works, it's not even working now
int n = 100;
float y = 0.2;
float n_copy;
for(int i = 0 ; i < 20 ; i++)
{
float x = (float) (rand()/(float)RAND_MAX); // 0 <= x <= 1
x = x * n; // 0 <= x <= n
float p1 = (1 - y) / (n*y);
float p2 = (1 - ( x / n ));
float exp = (1 - (2*y)) / y;
p2 = pow(p2, exp);
n_copy = p1 * p2;
printf("%.5f\n", n_copy);
}
And here are some results (5 decimals truncated):
0.03354
0.00484
0.00003
0.00029
0.00020
0.00028
0.00263
0.01619
0.00032
0.00000
0.03598
0.03975
0.00704
0.00176
0.00001
0.01333
0.03396
0.02795
0.00005
0.00860
The article is:
http://www.scribd.com/doc/3097936/cAS-The-Cunning-Ant-System
pages 6 and 7.
or search "cAS: cunning ant system" on google.
So what am i doing wrong? i don't believe the author is wrong, because there are more than 5 papers describing this same function.
all my internets to whoever helps me. This is important to my work.
Thanks :)
You may misunderstand what is expected of you.
Given a (properly normalized) PDF, and wanting to throw a random distribution consistent with it, you form the Cumulative Probability Distribution (CDF) by integrating the PDF, then invert the CDF, and use a uniform random predicate as the argument of the inverted function.
A little more detail.
f_s(l) is the PDF, and has been normalized on [0,n).
Now you integrate it to form the CDF
g_s(l') = \int_0^{l'} dl f_s(l)
Note that this is a definite integral to an unspecified endpoint which I have called l'. The CDF is accordingly a function of l'. Assuming we have the normalization right, g_s(N) = 1.0. If this is not so we apply a simple coefficient to fix it.
Next invert the CDF and call the result G^{-1}(x). For this you'll probably want to choose a particular value of gamma.
Then throw uniform random number on [0,n), and use those as the argument, x, to G^{-1}. The result should lie between [0,1), and should be distributed according to f_s.
Like Justin said, you can use a computer algebra system for the math.
dmckee is actually correct, but I thought that I would elaborate more and try to explain away some of the confusion here. I could definitely fail. f_s(l), the function you have in your pretty formula above, is the probability distribution function. It tells you, for a given input l between 0 and n, the probability that l is the segment length. The sum (integral) for all values between 0 and n should be equal to 1.
The graph at the top of page 7 confuses this point. It plots l vs. f_s(l), but you have to watch out for the stray factors it puts on the side. You notice that the values on the bottom go from 0 to 1, but there is a factor of x n on the side, which means that the l values actually go from 0 to n. Also, on the y-axis there is a x 1/n which means these values don't actually go up to about 3, they go to 3/n.
So what do you do now? Well, you need to solve for the cumulative distribution function by integrating the probability distribution function over l which actually turns out to be not too bad (I did it with the Wolfram Mathematica Online Integrator by using x for l and using only the equation for y <= .5). That however was using an indefinite integral and you are really integration along x from 0 to l. If we set the resulting equation equal to some variable (z for instance), the goal now is to solve for l as a function of z. z here is a random number between 0 and 1. You can try using a symbolic solver for this part if you would like (I would). Then you have not only achieved your goal of being able to pick random ls from this distribution, you have also achieved nirvana.
A little more work done
I'll help a little bit more. I tried doing what I said about for y <= .5, but the symbolic algebra system I was using wasn't able to do the inversion (some other system might be able to). However, then I decided to try using the equation for .5 < y <= 1. This turns out to be much easier. If I change l to x in f_s(l) I get
y / n / (1 - y) * (x / n)^((2 * y - 1) / (1 - y))
Integrating this over x from 0 to l I got (using Mathematica's Online Integrator):
(l / n)^(y / (1 - y))
It doesn't get much nicer than that with this sort of thing. If I set this equal to z and solve for l I get:
l = n * z^(1 / y - 1) for .5 < y <= 1
One quick check is for y = 1. In this case, we get l = n no matter what z is. So far so good. Now, you just generate z (a random number between 0 and 1) and you get an l that is distributed as you desired for .5 < y <= 1. But wait, looking at the graph on page 7 you notice that the probability distribution function is symmetric. That means that we can use the above result to find the value for 0 < y <= .5. We just change l -> n-l and y -> 1-y and get
n - l = n * z^(1 / (1 - y) - 1)
l = n * (1 - z^(1 / (1 - y) - 1)) for 0 < y <= .5
Anyway, that should solve your problem unless I made some error somewhere. Good luck.
Given that for any values l, y, n as described, the terms you call p1 and p2 are both in [0,1) and exp is in [1,..) making pow(p2, exp) also in [0,1) thus I don't see how you'd ever get an output with the range [0,n)