How to code subtour constraint in cplex python - python

I am quite new in python cplex. I tried to model VRP and needed to eliminate subtours for feasible solution, but I cannot do that with the following code:
from docplex.mp.model import Model
import numpy as np
n = 10
Q = 20
N = [i for i in range(1, n+1)]
V = [0] + N
rnd = np.random
rnd.seed(0)
q = {i: rnd.randint(1, 10) for i in N}
loc_x = rnd.rand(len(V))*200
loc_y = rnd.rand(len(V))*100
A = [(i, j) for i in V for j in V if i != j]
c = {(i, j): np.hypot(loc_x[i]-loc_x[j], loc_y[i]-loc_y[j]) for i, j in A}
mdl = Model('CVRP')
x = mdl.binary_var_dict(A, name='x')
u = mdl.continuous_var_dict(N, lb=0, name='u')
mdl.minimize(mdl.sum(c[i, j]*x[i, j] for i, j in A))
mdl.add_constraints(mdl.sum(x[i, j] for j in V if j != i) == 1 for i in N)
mdl.add_constraints(mdl.sum(x[i, j] for i in V if i != j) == 1 for j in N)
mdl.add_constraints(mdl.add(u[i] - u[j] + n*(x[i,j]) <= n - 1 for i,j in N if i!=j))
mdl.parameters.timelimit = 15
solution = mdl.solve()
print(solution)
When I run it, I get the following error:
File "C:\Users\user.spyder-py3\TSP\cplexExp.py", line 25, in
mdl.add_constraints(mdl.add(u[i] - u[j] + n*(x[i,j]) <= n - 1 for i,j in N if i!=j))
TypeError: cannot unpack non-iterable int object
I really appreciate for any help. thanks!!

Thanks guys, the problem is that N is one dimensional array, but I considered it as two dimensional by using i,j in N. I figured that out. By the way, do you know any good resource to learn CPLEX Python? I am new and do not know where to start. Thanks!!

Have a look at the Docplex examples here:
https://github.com/IBMDecisionOptimization/docplex-examples

Related

Is there a faster way to solve the following problem?

A is a mn matrix
B is a nn matrix
I want to return matrix C of size m*n such that:
In python it could be like below
for i in range(m):
for j in range(n):
C[i][j] = 0
for k in range(n):
C[i][j] += max(0, A[i][j] - B[j][k])
this runs on O(m*n^2)
if A[i][j] - B[j][k] is always > 0 it could easily be improved as
C[i][j] = n*A[i][j] - sum(B[j])
but it is possible to improve as well when there are cases of A[i][j] - B[j][k]< 0 ? I think some divide and conquer algorithms might help here but I am not familiar with them.
For each j, You can sort each column B[j][:] and compute cumulative sums.
Then for a given A[i][j] you can find the sum of B[j][k] that are larger than A[i][j] in O(log n) time using binary search. If there's x elements of B[j][:] that are greater than A[i][j] and their sum is S, then C[i][j] = A[i][j] * x - S.
This gives you an overall O((m+n)n log n) time algorithm.
I would look on much simpler construct and go from there..
lets say the max between 0 and the addition wasn't there.
so the answer would be : a(i,j)n - sum(b(j,)
on this you could just go linearly by sum each vector and erase it from a(i,j)n
and because you need sum each vector in b only once per j it can be done in max(mn,nn)
now think about simple solution for the max problem...
if you would find which elements in b(j,) is bigger than a(i,j) you could just ignore their sum and substract their count from the multipication of a(i,j)
All of that can be done by ordering the vector b(j,) by size and make a summing array to each vector from the biggest to lowest (it can be done in nnlog(n) because you order each b(j,) vector once)
then you only need to binary search where is the a(i,j) in the ordered vector and take the sum you already found and subtract it from a(i,j) * the position you found in the binary search.
Eventually you'll get O( max( mnlog(n),nnlog(n) ) )
I got for you also the implementation:
import numpy as np
M = 4
N = 7
array = np.random.randint(100, size=(M,N))
array2 = np.random.randint(100, size=(N,N))
def matrixMacossoOperation(a,b, N, M):
cSlow = np.empty((M,N))
for i in range(M):
for j in range(N):
cSlow[i][j] = 0
for k in range(N):
cSlow[i][j] += max(0, a[i][j] - b[j][k])
for i in range(N):
b[i].sort()
sumArr = np.copy(b)
for j in range(N):
for i in range(N - 1):
sumArr[j][i + 1] += sumArr[j][i]
c = np.empty((M,N))
for i in range(M):
for j in range(N):
sumIndex = np.searchsorted(b[j],a[i][j])
if sumIndex == 0:
c[i][j] = 0;
else:
c[i][j] = ((sumIndex) * a[i][j]) - sumArr[j][sumIndex - 1]
print(c)
assert(np.array_equal(cSlow,c))
matrixMacossoOperation(array,array2,N,M)

how to fix 'gurobipy.LinExpr' object is not iterable

I am having some issues with my Linear Program Model in gurobi. I am quite new to programming, and don't really understand what the error is. It comes up as 'gurobipy.LinExpr' object is not iterable with my Objective Function. This is my code currently
from gurobipy import *
IDtoLVC = [
[40.4,61.2,69.3,60.9,86.8,13.1,70.5,69.7],
[59.5,5.6,45.8,85.6,62.6,46.9,77.8,29.7],
[27.2,62.7,26.9,30.1,30.3,62.6,11.7,45.0]
]
CCDPop = [2180,4887,2174,4244,2444,2156,2433,2096,4148,3636,4935,3160,4153,2058,4614,3862,4588,4264,2559,2770,3964,2333,3681,4786,3256]
CCDtoLVC = [
[48.8,0,0,0,0,19.2,0,0],
[46.3,0,0,0,0,8.3,0,0],
[0,37.2,0,0,0,17.7,0,0],
[0,14.0,0,0,0,0,0,36.1],
[0,18.4,0,0,0,0,0,42.2],
[35.5,0,0,0,0,20.1,0,0],
[33.3,0,0,0,0,6.8,0,0],
[24.9,0,0,0,0,18.7,0,0],
[0,17.4,0,0,0,30.8,0,0],
[0,12.4,0,0,0,0,0,20.1],
[23.5,0,0,31.6,0,0,0,0],
[6.9,0,0,28.3,0,0,0,0],
[17.9,0,27.7,0,0,0,0,0],
[0,24.4,20.3,0,0,0,0,16.4],
[0,28.9,26.1,0,0,0,0,11.5],
[28.3,0,0,12.0,0,0,0,0],
[13.0,0,0,14.4,0,0,24.9,0],
[13.1,0,26.7,31.0,0,0,24.0,0],
[0,0,6.5,0,17.1,0,0,25.4],
[0,0,20.3,0,16.4,0,0,23.3],
[0,0,0,15.1,0,0,35.3,0],
[27.2,0,0,14.1,0,0,7.8,0],
[0,0,25.6,0,25.0,0,14.7,0],
[0,0,26.7,0,12.0,0,0,0],
[0,0,30.2,0,15.5,0,0,0]
]
LVCCost = [194, 105, 184]
# Sets
I = range(3) # no. of IDs
J = range(8) # no. of LVCs
K = range(25) # no. of CCDs
T = range(6) # length of time
# Model
m = Model('Vaccine Distribution')
X = { (i,j,t): m.addVar() for i in I for j in J for t in T }
Y = { (j,k,t): m.addVar() for j in J for k in K for t in T }
m.setObjective((0.20 * quicksum(quicksum(quicksum(X[i,j,t] * IDtoLVC[i][j] for i in I for j in J for t in T)))) +
(quicksum(quicksum(quicksum(Y[j,k,t] * CCDtoLVC[k][j] for j in J for k in K for t in T)))) +
(quicksum(quicksum(quicksum(X[i,j,t] * LVCCost[i] for i in I for j in J for t in T)))),
GRB.MINIMIZE)
m.optimize()
You don't need three nested calls to quicksum in order to express a triple sum. Instead, you just call quicksum once:
sum1 = quicksum(X[i,j,t] * IDtoLVC[i][j] for i in I for j in J for t in T)
sum2 = quicksum(Y[j,k,t] * CCDtoLVC[k][j] for j in J for k in K for t in T)
sum3 = quicksum(X[i,j,t] * LVCCost[i] for i in I for j in J for t in T)
m.setObjective(0.20 * sum1 + sum2 + sum3, GRB.MINIMIZE)

Subtour constraint causes warnings in docplex

mdl = Model('CVRP')
x = mdl.binary_var_dict(A, name='x')
u = mdl.continuous_var_dict(N, name='u')
mdl.minimize(mdl.sum(distanceList[i][j]*x[i, j] for i, j in A))
mdl.add_constraints(mdl.sum(x[i, j] for j in V if j != i) == 1 for i in N)
mdl.add_constraints(mdl.sum(x[i, j] for i in V if i != j) == 1 for j in N)
mdl.add_constraints(mdl.add(u[i] - u[j] + n*(x[i,j]) <= n - 1 for i in N for j in N if i!=j))
When I ran this code, I got this warning:
Warning: constraint has already been posted: 25x_25_24-u_24+u_25 <= 24, index is: 649
I think my model duplicates same constraints many times, but I cannot figure that why this is happening. It is related to the last constraint that provides subtour elimination.
I really appreciate that somebody helps me to get rid of duplication or a new subtour elimination constraint. Thanks.
Model.add(..) builds a constraint , add it to the model and returns it.
Thus when you call Model.add_constraints on the results of the various Model.add calls, you end adding constraints *which have already been added.
Removing the call to Model.add should do the trick:
mdl.add_constraints((u[i] - u[j] + n*(x[i,j]) <= n - 1) for i in N for j in N if i!=j)

facility location with OR-tools

i'm trying to write facility location MIP solution with OR tools. i translated a Scip solution, from here:
https://scipbook.readthedocs.io/en/latest/flp.html
but im getting a table with only zeros means a no solution..
is the framing of the problem or/and the way I'm using OR-tools here supposed to work?
def or_tools_scip_mine(facilities, customers, time_limit=None):
import numpy
import datetime
if time_limit is None:
time_limit = 1000 * 60 # 1 minute
solver = pywraplp.Solver.CreateSolver('SCIP')
customer_count = range(len(customers))
facility_count = range(len(facilities))
x =[[] for _ in range(len(customers))]
y = []
facility_capacities=[facilities[i][2] for i in facility_count]
facility_setup_costs = [facilities[i][1] for i in facility_count]
demands=[customers[i][1] for i in customer_count]
c=dist_matrix(facilities,customers)
for j in facility_count:
y.append(solver.BoolVar("y(%s)" % j))
for i in customer_count:
x[i].append(solver.BoolVar("x(%s,%s)" % (i, j)))
for i in customer_count:
solver.Add(solver.Sum(x[i][j] for j in facility_count) <= demands[i])#, "Demand(%s)" % i
for j in facility_count:
solver.Add(solver.Sum(x[i][j] for i in customer_count) <= facility_capacities[j] * y[j])#, "Capacity(%s)" % j)
for j in facility_count:
for i in customer_count:
solver.Add(x[i][j] <= demands[i] * y[j])
a=solver.Sum((facility_setup_costs[j] * y[j] for j in facility_count))
b=solver.Sum((c[i, j] * x[i][j] for i in customer_count for j in facility_count))
func_=solver.Sum([a,b])
solver.Minimize(func_)
solver.set_time_limit(time_limit)
result_status = solver.Solve()
print(result_status)
val = solver.Objective().Value()
x_val = [[] for _ in range(len(customers))]
solution = []
for j in range(len(facilities)):
for i in range(len(customers)):
x_val[i].append(int(x[i][j].solution_value()))
x_val = numpy.array(x_val)
for j in range(len(customers)):
solution.append(numpy.where(x_val[:, j] == 1)[0][0])
return val, solution
the Error:
solution.append(numpy.where(x_val[:, j] == 1)[0][0])
IndexError: index 0 is out of bounds for axis 0 with size 0
Add solver.EnableOutput() after CreateSolver line in your code. This will give you more insight what is going on.
Below are the helping solver parameters and status. If you need more
details click on this documentation link
print('Number of variables = %d' % solver.NumVariables())
print('Number of constraints = %d' % solver.NumConstraints())
print('The Optimal Objective value =',solver.Objective().Value())
print('Total Iterations:',solver.iterations())
print('Total Nodes:',solver.nodes())
print('Total number of Variables:',solver.NumVariables())
print(pywraplp.Solver.FEASIBLE)
print(pywraplp.Solver.MODEL_INVALID)
print(pywraplp.Solver.OPTIMAL)
print(pywraplp.Solver.INFEASIBLE)
print(pywraplp.Solver.UNBOUNDED)

Redundant lines in 0/1 Matrix output

I am trying to create a script that can allows me to output all the different combinations possible for different project optimization choices. In short, there are 6 projets (A B C D E F) that each have 2, 3 or 6 possible choices of optimization, that are mutually exclusive (you can't choose F4 and F5 at the same time for example).
import numpy as np
A = range(1, 3)
B = range(1, 3)
C = range(1, 7)
D= range(1,3)
E=range(1,3)
F=range(1,4)
length = len(A) + len(B) + len(C) + len(D) + len(E) + len(F)
nb_projet = len(A) * len(B) * len(C) * len(D) * len(E) * len(F)
result = np.zeros((length, nb_projet))
for k in range(len(A)):
for i in range(len(A)):
for j in range(nb_projet):
result[i, j] = (i+j % len(A)) == k
for k in range(len(B)):
for i in range(len(B)):
for j in range(nb_projet):
result[i + len(A), j] = (i+j % len(B)) == k
for k in range(len(C)):
for i in range(len(C)):
for j in range(nb_projet):
result[i + len(A)+len(B), j] = (i+j % len(C)) == k
for k in range(len(D)):
for i in range(len(D)):
for j in range(nb_projet):
result[i + len(A)+len(B)+len (C), j] = (i+j % len(D)) == k
for k in range(len(E)):
for i in range(len(E)):
for j in range(nb_projet):
result[i + len(A)+len(B)+len (C)+len(D), j] = (i+j % len(E)) == k
for k in range(len(F)):
for i in range(len(F)):
for j in range(nb_projet):
result[i + len(A)+len(B)+len (C)+len(D)+len(E), j] = (i+j % len(F)) == 0
print (result.T)
np.savetxt("ResultsS2.txt", result, delimiter=" ")
Basically the code is supposed to add a 1 if the optimization is chosen. At the moment it only generates 6 differents scenarios and not the 250+ that are possible.
Does anyone have an idea on how to fix this ?
Thank yu a lot !
This is just a bunch of concatenated one-hot arrays, so utilizing the second answer here and meshgrid to create the full factorial, you can just do something like this:
projects = [2,2,6,2,2,3] #[A.size, B.size, C.size . . .]
m = np.meshgrid(*[np.arange(i) for i in projects])
oneHots = [np.eye(projects[i])[m[i].flat] for i in range(len(projects))]
out = np.hstack(oneHots).T
out.shape
>(17, 288)
You can use something like this. Not the smartest, but if the arrays are not too long, it would work perfectly.
import numpy as np
A = range(0, 2)
B = range(0, 2)
C = range(0, 6)
length = len(A) + len(B) + len(C)
nb_projet = len(A) * len(B) * len(C)
result = np.zeros((length, nb_projet))
selectedList = []
count = 0
for i in A:
for j in B:
for k in C:
result[(i,count)]= 1
result[(len(A)+j,count)]=1
result[(len(A)+len(B)+k, count)] = 1
count+=1
Note, that I had changed the ranges to fit better.

Categories