In my code, I managed to implement different vehicle types (I think) and to indicate the site-dependency. However, it seems that in the output of my optimization, vehicles can drive more then one route. I would like to implement that my vehicle, once it returns to the depot (node 0), that a new vehicle is assigned to perform another route. Could you help me with that? :)
I'm running on Python Jupyter notebook with the Docplex solver
all_units = [0,1,2,3,4,5,6,7,8,9]
ucp_raw_unit_data = {
"customer": all_units,
"loc_x": [40,45,45,42,42,42,40,40,38,38],
"loc_y" : [50,68,70,66,68,65,69,66,68,70],
"demand": [0,10,30,10,10,10,20,20,20,10],
"req_vehicle":[[0,1,2], [0], [0], [0],[0], [0], [0], [0], [0], [0]],
}
df_units = DataFrame(ucp_raw_unit_data, index=all_units)
# Display the 'df_units' Data Frame
df_units
Q = 50
N = list(df_units.customer[1:])
V = [0] + N
k = 15
# n.o. vehicles
K = range(1,k+1)
# vehicle 1 = type 1 vehicle 6 = type 2 and vehicle 11 = type 0
vehicle_types = {1:[1],2:[1],3:[1],4:[1],5:[2],6:[2],7:[2],8:[2],9:
[2],10:[2],11:[0],12:[0],13:[0],14:[0],15:[0]}
lf = 0.5
R = range(1,11)
# Create arcs and costs
A = [(i,j,k,r) for i in V for j in V for k in K for r in R if i!=j]
Y = [(k,r) for k in K for r in R]
c = {(i,j):np.hypot(df_units.loc_x[i]-df_units.loc_x[j],
df_units.loc_y[i]-df_units.loc_y[j]) for i,j,k,r in A}
from docplex.mp.model import Model
import docplex
mdl = Model('SDCVRP')
# decision variables
x = mdl.binary_var_dict(A, name = 'x')
u = mdl.continuous_var_dict(df_units.customer, ub = Q, name = 'u')
y = mdl.binary_var_dict(Y, name = 'y')
# objective function
mdl.minimize(mdl.sum(c[i,j]*x[i,j,k,r] for i,j,k,r in A))
#constraint 1 each node only visited once
mdl.add_constraints(mdl.sum(x[i,j,k,r] for k in K for r in R for j in V
if j != i and vehicle_types[k][0] in df_units.req_vehicle[j]) == 1 for i
in N)
##contraint 2 each node only exited once
mdl.add_constraints(mdl.sum(x[i,j,k, r] for k in K for r in R for i in V
if i != j and vehicle_types[k][0] in df_units.req_vehicle[j]) == 1 for j
in N )
##constraint 3 -- Vehicle type constraint (site-dependency)
mdl.add_constraints(mdl.sum(x[i,j,k,r] for k in K for r in R for i in V
if i != j and vehicle_types[k][0] not in
df_units.req_vehicle[j]) == 0 for j in N)
#Correcte constraint 4 -- Flow constraint
mdl.add_constraints((mdl.sum(x[i, j, k,r] for j in V if j != i) -
mdl.sum(x[j, i, k,r] for j in V if i != j)) == 0 for i in
N for k in K for r in R)
#constraint 5 -- Cumulative load of visited nodes
mdl.add_indicator_constraints([mdl.indicator_constraint(x[i,j,k,r],u[i] +
df_units.demand[j]==u[j]) for i,j,k,r in A if i!=0 and j!=0])
## constraint 6 -- one vehicle to one route
mdl.add_constraints(mdl.sum(y[k,r] for r in R) <= 1 for k in K)
mdl.add_indicator_constraints([mdl.indicator_constraint(x[i,j,k,r],y[k,r]
== 1) for i,j,k,r in A if i!=0 and j!=0])
##constraint 7 -- cumulative load must be equal or higher than demand in
this node
mdl.add_constraints(u[i] >=df_units.demand[i] for i in N)
##constraint 8 minimum load factor
mdl.add_indicator_constraints([mdl.indicator_constraint(x[j,0,k,r],u[j]
>= lf*Q) for j in N for k in K for r in R if j != 0])
mdl.parameters.timelimit = 15
solution = mdl.solve(log_output=True)
print(solution)
I expect every route to be visited with another vehicle, however the same vehicles perform multiple routes. Also, now the cumulative load is calculated for visited nodes, I would like to have this for the vehicle on the routes so that the last constraint (minimum load factor) can be performed.
I understand K indices are for vehicles and R are for routes. I ran your code and got the follwing assignments:
y_11_9=1
y_12_4=1
y_13_7=1
y_14_10=1
y_15_10=1
which seem to show many vehicles share the same route.
This is not forbidden by the sum(y[k,r] for r in R) <=1) constraint,
as it forbids one vehicle from working several routes.
Do you want to limit the number of assigned vehicles to one route to 1, as this is the symmetrical constraint from constraint #6?
If I got it wrong, plese send the solution you get and the constraint you want to add.
If I add the symmetrical constraint, that is, limit assignments vehicles to routes to 1 (no two vehicles on the same route), by:
mdl.add_constraints(mdl.sum(y[k, r] for r in R) <= 1 for k in K)
mdl.add_constraints(mdl.sum(y[k, r] for k in K) <= 1 for r in R)
I get a solution with the same cost, and only three vehicle-route assignments:
y_11_3=1
y_12_7=1
y_15_9=1
Still, I guess the best solution would be to add some cost factor of using a vehicle, and introducing this into the final objective. This might also reduce the symmetries in the problem.
Philippe.
Related
I am dealing with a vehicle routing problem with multiple working days. I want to add constraints that if a working day is 6 or its multipliers, a worker must work only 240 minutes. Therefore, I put two loops for working days (k) and workers (w) and each k and w, I want to make sure total travel and service times do not exceed a prespesified time (S). As you can see on my codes, I used if else structure for 240 and 480 minutes daily times. However, I got "DOcplexException: Expecting iterable Error" and saw this:
OcplexException: Expecting iterable, got: 480x_0_1_1_1+100x_0_2_1_1+480x_0_3_1_1+20x_0_4_1_1+300x_0_5_1_1+100x_0_6_1_1+200x_0_7_1_1+480x_0_8_1_1+80x_0_9_1_1+200x_0_10_1_1+120x_0_11_1_1+260x_0_12_1_1+280x_0_13_1_1+340x_0_14_1_1+400x_0_15_1_1+120x_0_16_1_1+80x_0_17_1_1+50x_0_18_1_1+20x_0_19_1_1+320x_0_20_1_1+180x_0_21_1_1+50x_0_22_1_1+100x_0_23_1_1+80x_0_24_1_1+140 ...
The constraints are following:
# for w in W:
# for k in D:
# if k%6==0:
# mdl.add_constraints(mdl.sum((distanceList[i][j]*6/7000)*x[i, j, w, k] for i in N for j in N if j != i) + mdl.sum(serviceTime[j-1]*x[i, j, w, k] for i in V for j in N if j != i) <= S*0.5)
# else:
# mdl.add_constraints(mdl.sum((distanceList[i][j]*6/7000)*x[i, j, w, k] for i in N for j in N if j != i) + mdl.sum(serviceTime[j-1]*x[i, j, w, k] for i in V for j in N if j != i) <= S)
I really appreciate for your help! Thanks.
Model.add_constraints() expects either a list or a comprehension (both work), in other terms, something that can be iterated upon. Based on the (non-executable) code snippet you posted, I have the impression the argument of add_constraints is a constraint:
Model.add_constraints( mdl.sum(a[i,w,k]) + mdl.sum(b[i,w,k]) <= S)
which is not accepted.
You should transform the argument to add_constraints to a Python comprehension, something like:
Model.add_constraints( mdl.sum(a[i,w,k]+mdl.sum(b[i,w,k] <= S for w in W for k in K)
Thus, add_constraints receives a comprehension as expected. If parts of the constraints depend on the indices w,k use auxiliary functions in the comprehension. Let me give an example with the rhs:
def my_rhs(wk,k): # rhs of the constraint as a function of w,k
... return w+k % 42 # silly example
Model.add_constraints( mdl.sum(a[i,w,k]+mdl.sum(b[i,w,k] <= my_rhs(w,k) for w in W for k in K)
Im trying to implement merge sort in Python based on the following pseudo code. I know there are many implementations out there, but I have not been able to find one that followis this pattern with a for loop at the end as opposed to while loop(s). Also, setting the last values in the subarrays to infinity is something I haven't seen in other implementation. NOTE: The following pseudo code has 1 based index i.e. index starts at 1. So I think my biggest issue is getting the indexing right. Right now its just not sorting properly and its really hard to follow with the debugger. My implementation is at the bottom.
Current Output:
Input: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Merge Sort: [0, 0, 0, 3, 0, 5, 5, 5, 8, 0]
def merge_sort(arr, p, r):
if p < r:
q = (p + (r - 1)) // 2
merge_sort(arr, p, q)
merge_sort(arr, q + 1, r)
merge(arr, p, q, r)
def merge(A, p, q, r):
n1 = q - p + 1
n2 = r - q
L = [0] * (n1 + 1)
R = [0] * (n2 + 1)
for i in range(0, n1):
L[i] = A[p + i]
for j in range(0, n2):
R[j] = A[q + 1 + j]
L[n1] = 10000000 #dont know how to do infinity for integers
R[n2] = 10000000 #dont know how to do infinity for integers
i = 0
j = 0
for k in range(p, r):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
return A
First of all you need to make sure if the interval represented by p and r is open or closed at its endpoints. The pseudocode (for loops include last index) establishes that the interval is closed at both endpoints: [p, r].
With last observation in mind you can note that for k in range(p, r): doesn't check last number so the correct line is for k in range(p, r + 1):.
You can represent "infinity" in you problem by using the maximum element of A in the range [p, r] plus one. That will make the job done.
You not need to return the array A because all changes are being done through its reference.
Also, q = (p + (r - 1)) // 2 isn't wrong (because p < r) but correct equation is q = (p + r) // 2 as the interval you want middle integer value of two numbers.
Here is a rewrite of the algorithm with “modern” conventions, which are the following:
Indices are 0-based
The end of a range is not part of that range; in other words, intervals are closed on the left and open on the right.
This is the resulting code:
INF = float('inf')
def merge_sort(A, p=0, r=None):
if r is None:
r = len(A)
if r - p > 1:
q = (p + r) // 2
merge_sort(A, p, q)
merge_sort(A, q, r)
merge(A, p, q, r)
def merge(A, p, q, r):
L = A[p:q]; L.append(INF)
R = A[q:r]; R.append(INF)
i = 0
j = 0
for k in range(p, r):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
A = [433, 17, 585, 699, 942, 483, 235, 736, 629, 609]
merge_sort(A)
print(A)
# → [17, 235, 433, 483, 585, 609, 629, 699, 736, 942]
Notes:
Python has a handy syntax for copying a subrange.
There is no int infinity in Python, but we can use the float one, because ints and floats can always be compared.
There is one difference between this algorithm and the original one, but it is irrelevant. Since the “midpoint” q does not belong to the left range, L is shorter than R when the sum of their lengths is odd. In the original algorithm, q belongs to L, and so L is the longer of the two in this case. This does not change the correctness of the algorithm, since it simply swaps the roles of L and R. If for some reason you need not to have this difference, then you must calculate q like this:
q = (p + r + 1) // 2
In mathematics, we represent all real numbers which are greater than or equal to i and smaller than j by [i, j). Notice the use of [ and ) brackets here. I have used i and j in the same way in my code to represent the region that I am dealing with currently.
ThThe region [i, j) of an array covers all indexes (integer values) of this array which are greater or equal to i and smaller than j. i and j are 0-based indexes. Ignore the first_array and second_array the time being.
Please notice, that i and j define the region of the array that I am dealing with currently.
Examples to understand this better
If your region spans over the whole array, then i should be 0 and j should be the length of array [0, length).
The region [i, i + 1) has only index i in it.
The region [i, i + 2) has index i and i + 1 in it.
def mergeSort(first_array, second_array, i, j):
if j > i + 1:
mid = (i + j + 1) // 2
mergeSort(second_array, first_array, i, mid)
mergeSort(second_array, first_array, mid, j)
merge(first_array, second_array, i, mid, j)
One can see that I have calculated middle point as mid = (i + j + 1) // 2 or one can also use mid = (i + j) // 2 both will work. I will divide the region of the array that I am currently dealing with into 2 smaller regions using this calculated mid value.
In line 4 of the code, MergeSort is called on the region [i, mid) and in line 5, MergeSort is called on the region [mid, j).
You can access the whole code here.
I'm trying to implement an algorithm from Algorithmic Toolbox course on Coursera that takes an arithmetic expression such as 5+8*4-2 and computes its largest possible value. However, I don't really understand the choice of indices in the last part of the shown algorithm; my implementation fails to compute values using the ones initialized in 2 tables (which are used to store maximized and minimized values of subexpressions).
The evalt function just takes the char, turns it into the operand and computes a product of two digits:
def evalt(a, b, op):
if op == '+':
return a + b
#and so on
MinMax computes the minimum and the maximum values of subexpressions
def MinMax(i, j, op, m, M):
mmin = 10000
mmax = -10000
for k in range(i, j-1):
a = evalt(M[i][k], M[k+1][j], op[k])
b = evalt(M[i][k], m[k+1][j], op[k])
c = evalt(m[i][k], M[k+1][j], op[k])
d = evalt(m[i][k], m[k+1][j], op[k])
mmin = min(mmin, a, b, c, d)
mmax = max(mmax, a, b, c, d)
return(mmin, mmax)
And this is the body of the main function
def get_maximum_value(dataset):
op = dataset[1:len(dataset):2]
d = dataset[0:len(dataset)+1:2]
n = len(d)
#iniitializing matrices/tables
m = [[0 for i in range(n)] for j in range(n)] #minimized values
M = [[0 for i in range(n)] for j in range(n)] #maximized values
for i in range(n):
m[i][i] = int(d[i]) #so that the tables will look like
M[i][i] = int(d[i]) #[[i, 0, 0...], [0, i, 0...], [0, 0, i,...]]
for s in range(n): #here's where I get confused
for i in range(n-s):
j = i + s
m[i][j], M[i][j] = MinMax(i,j,op,m,M)
return M[0][n-1]
Sorry to bother, here's what had to be improved:
for s in range(1,n)
in the main function, and
for k in range(i, j):
in MinMax function. Now it works.
The following change should work.
for s in range(1,n):
for i in range(0,n-s):
I have this LP problem, and I'm trying to solve it using PuLP in Python-3. One option that I can think of is to write all variable explicitly, but I want to avoid it. Is there a way that I can use lists/dicts in this problem? (I did refer to https://pythonhosted.org/PuLP/CaseStudies/a_sudoku_problem.html where dicts were being used, but didn't quite understand the entire solution)
Assume wt{i,j,type} denotes the number of traded goods between person[i] and person[j] of type.
LP Problem:
(Here, cost{i,j} is a known cost of pairing for all (i,j) pairs.
subject to:
I would be really grateful for any help, as I'm a beginner to both optimization and python/pulp.
The 'lists/dicts' is a way to define variables over domains (indexed variables).
The indexs argument of LpVariable.dicts() defines the domain - cartesian product of the supplied sets. See also documentation of PuLP - LpVariable.
The code sample below does not contain all your constraints, but I believe you can easily fill-in the remaining ones. Constraint 1 (with const1 and const2) is treated via the lower and upper bound of the wt variable instead.
from pulp import LpProblem, LpVariable, LpMaximize, LpInteger, lpSum, value
prob = LpProblem("problem", LpMaximize)
# define 'index sets'
I = range(10) # [0, 1, ..., 9]
J = range(10)
T = range(3)
# define parameter cost[i,j]
cost = {}
for i in I:
for j in J:
cost[i,j] = i + j # whatever
# define wt[i,j,t]
const1 = 0 # lower bound for w[i,j,t]
const2 = 100 # upper bound for w[i,j,t]
wt = LpVariable.dicts(name="wt", indexs=(I, J, T), lowBound=const1, upBound=const2, cat=LpInteger)
# define assign[i,j]
assign = LpVariable.dicts(name="assign", indexs=(I, J))
# contraint
for i in I:
for j in J:
prob += assign[i][j] == lpSum(wt[i][j][t] for t in T), ""
# objective
prob += lpSum(cost[i,j] * assign[i][j] for i in I for j in J)
prob.solve()
for i in I:
for j in J:
for t in T:
print "wt(%s, %s, %s) = %s" % (i, j, t, value(wt[i][j][t]))
for i in I:
for j in J:
print "assign(%s, %s) = %s" % (i, j, value(assign[i][j]))
I'm trying to understand the Hirschberg algorithm and I came across this piece of algorithm from Wikipedia. I do not understand how the NeedlemanWunsch() function works.
function Hirschberg(X,Y)
Z = ""
W = ""
if length(X) == 0 or length(Y) == 0
if length(X) == 0
for i=1 to length(Y)
Z = Z + '-'
W = W + Yi
end
else if length(Y) == 0
for i=1 to length(X)
Z = Z + Xi
W = W + '-'
end
end
else if length(X) == 1 or length(Y) == 1
(Z,W) = NeedlemanWunsch(X,Y)
else
xlen = length(X)
xmid = length(X)/2
ylen = length(Y)
ScoreL = NWScore(X1:xmid, Y)
ScoreR = NWScore(rev(Xxmid+1:xlen), rev(Y))
ymid = PartitionY(ScoreL, ScoreR)
(Z,W) = Hirschberg(X1:xmid, y1:ymid) + Hirschberg(Xxmid+1:xlen, Yymid+1:ylen)
end
return (Z,W)
Can someone explain about the NeedlemanWunsch algorithm and how can it be implemented through Python? Thank you very much!
This looks like a homework/coursework question so I won't give you the full solution. However, I will guide you into producing a working solution.
Needleman-Wunsch Algorithm
The Needleman-Wunsch algorithm is a method used to align sequences. It is essentially made up of two components:
A similarity matrix, F.
A linear penalty gap, d.
When aligning sequences, there can be many possibilities. What this matrix allows you to do is to find the most optimal one and discard all the other sequences.
What you will have to do is:
Create a 2-dimensional array to hold the matrix, F.
A method to initialise matrix F with the scores.
A method to compute the optimal sequence.
Creating a 2-dimensional array to hold the matrix, F
You can either use numpy for this, or you could just generate the matrix as follows. Assume you have two sequences A and B:
F = [[0 for x in xrange(len(A)] for x in xrange(len(B))]
A method to initialise matrix F with the scores.
Create a method which takes a parameters the length of each sequence, the linear penalty gap, and the matrix F:
def createSimilarityMatrix(lengthOfA, lengthOfB, penalityGap, F):
You then need to implement the following pseudo-code:
for i=0 to length(A)
F(i,0) ← d*i
for j=0 to length(B)
F(0,j) ← d*j
for i=1 to length(A)
for j=1 to length(B)
{
Match ← F(i-1,j-1) + S(Ai, Bj)
Delete ← F(i-1, j) + d
Insert ← F(i, j-1) + d
F(i,j) ← max(Match, Insert, Delete)
}
Hint: Research optimal ways to write this algorithm in idiomatic Python. Also note that in the double for-loop at the bottom, you can collapse in a one-liner.
A method to compute the optimal sequence
Once you have the similarity matrix done, then you can implement the main algorithm to compute the optimal sequence. For this, create a method which takes your two sequences A and B as parameters:
def needlemanWunsch (a, b):
You will then need to implement this method using the following pseudocode:
AlignmentA ← ""
AlignmentB ← ""
i ← length(A)
j ← length(B)
while (i > 0 or j > 0)
{
if (i > 0 and j > 0 and F(i,j) == F(i-1,j-1) + S(Ai, Bj))
{
AlignmentA ← Ai + AlignmentA
AlignmentB ← Bj + AlignmentB
i ← i - 1
j ← j - 1
}
else if (i > 0 and F(i,j) == F(i-1,j) + d)
{
AlignmentA ← Ai + AlignmentA
AlignmentB ← "-" + AlignmentB
i ← i - 1
}
else (j > 0 and F(i,j) == F(i,j-1) + d)
{
AlignmentA ← "-" + AlignmentA
AlignmentB ← Bj + AlignmentB
j ← j - 1
}
}
The psuedo-code has been taken from this page on Wikipedia. For more information on the Needleman-Wunsch algorithm, please have a look at this presentation.