How to write constraints to check whether a variable is bounded between two values - linear-programming

Does anyone know what is a good way to indicate whether a model variable is bounded between certain values? For example, indicator1 = 1 when 0<= variable x <=200 else 0, indicator2 = 1 when 200<= variable x <= 300.
One use case of this is to calculate weight dependent shipping cost, e.g. if the shipment weighs less than 200 lbs then it costs $z/lb; if the shipment weighs more than 200lb and less than 300 lbs then it costs $y/lb.
Minimize W1*z + W2*y
Weight = W1 + W2
0 <= W1 <= 200*X1
200*X2 <= W2 <= 300*X2
X1+ X2 = 1
X1, X2 binary
Weight, W1, W2 >= 0
Above is the formulation I came up with for this situation. However, now I have more than 200 buckets of values to check, so this formulation does not seem efficient enough. I am wondering whether there are better ways to model this?

This problem can also be modeled as a Generalized Disjunctive Program (GDP). It's more verbose, but more descriptive.
from pyomo.environ import *
from pyomo.gdp import *
m = ConcreteModel()
m.total_weight_cost = Var(domain=NonNegativeReals)
m.weight = Var(domain=NonNegativeReals)
m.weight_buckets = RangeSet(2)
m.weight_bucket_lb = Param(m.weight_buckets, initialize={1: 0, 2: 200})
m.weight_bucket_ub = Param(m.weight_buckets, initialize={1: 200, 2: 300})
m.weight_bucket_cost = Param(m.weight_buckets, initialize={1: z, 2: y})
m.weight_bucket_disjunction = Disjunction(expr=[
[m.total_weight_cost == m.weight_bucket_cost[bucket] * m.weight,
m.weight_bucket_lb[bucket] <= m.weight,
m.weight <= m.weight_bucket_ub[bucket]
for bucket in m.weight_buckets]
])
TransformationFactory('gdp.bigm').apply_to(m)
SolverFactory('gurobi').solve(m, tee=True)
m.display()

Related

Linear Programming, max function (if statement)

I have a value x, which is a combination of decision variables.
I need to calculate a cost, which only triggers if x > 100. So cost = MAX(x - 100, 0) * 20.
Is there any way to do this in linear programming?
I've tried creating two binary variables (y1 & y2), in which y1 = 1 when x <= 100 & y2 = 1 when x > 100 & y1 + y2 = 1, from this website - https://uk.mathworks.com/matlabcentral/answers/693740-linear-programming-with-conditional-constraints. However, my excel solver is still giving non-linearity complaints...
Any advice on how I can fix this?
The objective
min cost = max(x-100,0)*20
can be implemented in an LP as:
min cost = y*20
y >= x - 100
x >= 0, y >= 0
There is no need for binary variables.

lp_solve return uniform solution

Can lp_solve return a unifrom solution? (Is there a flag or something that will force this kinf of behavior?)
Say that I have this:
max: x + y + z + w;
x + y + z + w <= 100;
Results in:
Actual values of the variables:
x 100
y 0
z 0
w 0
However, I would like to have something like:
Actual values of the variables:
x 25
y 25
z 25
w 25
This is an oversimplyfied example, but the idea is that if the variables have the same factor in the objective function, then the result should idealy be more uniform and not everything for one, and the other what is left.
Is this possible to do? (I've tested other libs and some of them seem to do this by default like the solver on Excel or Gekko for Python).
EDIT:
For instance, Gekko has already this behavior without me especifing anything...
from gekko import GEKKO
m = GEKKO()
x1,x2,x3,x4 = [m.Var() for i in range(4)]
#upper bounds
x1.upper = 100
x2.upper = 100
x3.upper = 100
x4.upper = 100
# Constrain
m.Equation(x1 + x2 + x3 + x4 <= 100)
# Objective
m.Maximize(x1 + x2 + x3 + x4)
m.solve(disp=False)
print(x1.value, x2.value, x3.value, x4.value)
>> [24.999999909] [24.999999909] [24.999999909] [24.999999909]
You would need to explicitly model this (as another objective). A solver does nothing automatically: it just finds a solution that obeys the constraints and optimizes the objective function.
Also, note that many linear solvers will produce so-called basic solutions (corner points). So "all variables in the middle" does not come naturally at all.
The example in Gekko ended on [25,25,25,25] because of how the solver took a step towards the solution from an initial guess of [0,0,0,0] (default in Gekko). The problem is under-specified so there are an infinite number of feasible solutions. Changing the guess values gives a different solution.
from gekko import GEKKO
m = GEKKO()
x1,x2,x3,x4 = m.Array(m.Var,4,lb=0,ub=100)
x1.value=50 # change initial guess
m.Equation(x1 + x2 + x3 + x4 <= 100)
m.Maximize(x1 + x2 + x3 + x4)
m.solve(disp=False)
print(x1.value, x2.value, x3.value, x4.value)
Solution with guess values [50,0,0,0]
[3.1593723566] [32.280209196] [32.280209196] [32.280209196]
Here is one method with equality constraints m.Equations([x1==x2,x1==x3,x1==x4]) to modify the problem to guarantee a unique solution that can be used by any linear programming solver.
from gekko import GEKKO
m = GEKKO()
x1,x2,x3,x4 = m.Array(m.Var,4,lb=0,ub=100)
x1.value=50 # change initial guess
m.Equation(x1 + x2 + x3 + x4 <= 100)
m.Maximize(x1 + x2 + x3 + x4)
m.Equations([x1==x2,x1==x3,x1==x4])
m.solve(disp=False)
print(x1.value, x2.value, x3.value, x4.value)
This gives a solution:
[25.000000002] [25.000000002] [25.000000002] [25.000000002]
QP Solution
Switching to a QP solver allows a slight penalty for deviations but doesn't consume a degree of freedom.
from gekko import GEKKO
m = GEKKO()
x1,x2,x3,x4 = m.Array(m.Var,4,lb=0,ub=100)
x1.value=50 # change initial guess
m.Equation(x1 + x2 + x3 + x4 <= 100)
m.Maximize(x1 + x2 + x3 + x4)
penalty = 1e-5
m.Minimize(penalty*(x1-x2)**2)
m.Minimize(penalty*(x1-x3)**2)
m.Minimize(penalty*(x1-x4)**2)
m.solve(disp=False)
print(x1.value, x2.value, x3.value, x4.value)
Solution with QP penalty
[24.999998377] [25.000000544] [25.000000544] [25.000000544]

Cplex for Linear Program: Are DOCplex decision variables assumed to be non-negative?

I want to write a simple LP using docplex. Suppose I have three variables: x, y, and z, the constraint is 4x + 9y - 18.7 <= z. I wrote the constraint with the code model.add_constraint(4 * x + 9 * y - 18.7 <= z). Then I set minimize z as my objective by model.minimize(z).
After solving the model, I get the result of z = 0.000. Can anyone explain the result to me? I don't understand why 0 is the optimal value of this LP. I also tried to print the details of this model:
status = optimal
time = 0 s.
problem = LP
z: 0.000; None
objective: z
constraint: 4z+9y-18.700 <= z
When I tried to model.print_solution(), the program prints z: 0.000; None where I don't understand what does "None" mean, does that mean x and y are None?
Update: Forgot to mention, I created the variable using model.continuous_var()
Indeed if you do not give a range they are non negative.
Small example out of the zoo story:
from docplex.mp.model import Model
mdl = Model(name='buses')
nbbus40 = mdl.continuous_var(name='nbBus40')
nbbus30 = mdl.continuous_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve(log_output=False,)
print("nbbus40.lb =",nbbus40.lb)
for v in mdl.iter_continuous_vars():
print(v," = ",v.solution_value)
mdlv2 = Model(name='buses2')
nbbus40v2 = mdlv2.continuous_var(-2,200,name='nbBus40')
nbbus30v2 = mdlv2.continuous_var(-2,200,name='nbBus30')
mdlv2.add_constraint(nbbus40v2*40 + nbbus30v2*30 >= 300, 'kids')
mdlv2.minimize(nbbus40v2*500 + nbbus30v2*400)
mdlv2.solve(log_output=False,)
print("nbbus40v2.lb =",nbbus40v2.lb)
for v in mdlv2.iter_continuous_vars():
print(v," = ",v.solution_value)
gives
nbbus40.lb = 0
nbBus40 = 7.5
nbBus30 = 0
nbbus40v2.lb = -2
nbBus40 = 9.0
nbBus30 = -2.0

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)

Incremental entropy computation

Let std::vector<int> counts be a vector of positive integers and let N:=counts[0]+...+counts[counts.length()-1] be the the sum of vector components. Setting pi:=counts[i]/N, I compute the entropy using the classic formula H=p0*log2(p0)+...+pn*log2(pn).
The counts vector is changing --- counts are incremented --- and every 200 changes I recompute the entropy. After a quick google and stackoverflow search I couldn't find any method for incremental entropy computation. So the question: Is there an incremental method, like the ones for variance, for entropy computation?
EDIT: Motivation for this question was usage of such formulas for incremental information gain estimation in VFDT-like learners.
Resolved: See this mathoverflow post.
I derived update formulas and algorithms for entropy and Gini index and made the note available on arXiv. (The working version of the note is available here.) Also see this mathoverflow answer.
For the sake of convenience I am including simple Python code, demonstrating the derived formulas:
from math import log
from random import randint
# maps x to -x*log2(x) for x>0, and to 0 otherwise
h = lambda p: -p*log(p, 2) if p > 0 else 0
# update entropy if new example x comes in
def update(H, S, x):
new_S = S+x
return 1.0*H*S/new_S+h(1.0*x/new_S)+h(1.0*S/new_S)
# entropy of union of two samples with entropies H1 and H2
def update(H1, S1, H2, S2):
S = S1+S2
return 1.0*H1*S1/S+h(1.0*S1/S)+1.0*H2*S2/S+h(1.0*S2/S)
# compute entropy(L) using only `update' function
def test(L):
S = 0.0 # sum of the sample elements
H = 0.0 # sample entropy
for x in L:
H = update(H, S, x)
S = S+x
return H
# compute entropy using the classic equation
def entropy(L):
n = 1.0*sum(L)
return sum([h(x/n) for x in L])
# entry point
if __name__ == "__main__":
L = [randint(1,100) for k in range(100)]
M = [randint(100,1000) for k in range(100)]
L_ent = entropy(L)
L_sum = sum(L)
M_ent = entropy(M)
M_sum = sum(M)
T = L+M
print("Full = ", entropy(T))
print("Update = ", update(L_ent, L_sum, M_ent, M_sum))
You could re-compute the entropy by re-computing the counts and using some simple mathematical identity to simplify the entropy formula
K = count.size();
N = count[0] + ... + count[K - 1];
H = count[0]/N * log2(count[0]/N) + ... + count[K - 1]/N * log2(count[K - 1]/N)
= F * h
h = (count[0] * log2(count[0]) + ... + count[K - 1] * log2(count[K - 1]))
F = -1/(N * log2(N))
which holds because of log2(a / b) == log2(a) - log2(b)
Now given an old vector count of observations so far and another vector of new 200 observations called batch, you can do in C++11
void update_H(double& H, std::vector<int>& count, int& N, std::vector<int> const& batch)
{
N += batch.size();
auto F = -1/(N * log2(N));
for (auto b: batch)
++count[b];
H = F * std::accumulate(count.begin(), count.end(), 0.0, [](int elem) {
return elem * log2(elem);
});
}
Here I assume that you have encoded your observations as int. If you have some kind of symbol, you would need a symbol table std::map<Symbol, int>, and do a lookup for each symbol in batch before you update the count.
This seems the quickest way of writing some code for a general update. If you know that in every batch only few counts actually change, you can do as #migdal does and keep track of the changing counts, subtract their old contribution to the entropy and add the new contribution.