0

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.

1 Answers1

0

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.

Philippe Couronne
  • 826
  • 1
  • 5
  • 6
  • Dear Philippe, thanks for you reply. This was exactly the constrained I needed. However, I added the symmetrical constraint to my model, however this does not give me the three routes. Was this the only thing you changed? Thanks in advance! – Anne-Fleur Jun 05 '19 at 16:26
  • Dear Philippe, Thanks again for your reply. I think i managed it in a slightly different way, I tried your suggestion but unfortunately it did not work as hoped. However, i've got an additional question: the optimality gap of the results I get is very high (>60%) for instances of 10 and 25 customers. If I run it longer (let's say 30 minutes) this still remains very high (>30%) Do you have any suggestions on how to improve my model to solve it quicker? Thanks a lot in advance. Kind regards – Anne-Fleur Jun 05 '19 at 22:28
  • Dear Anne-Fleur, I checked and yes, I only added the 'one vehicle per route' limitation. This should avoid the initial problem you reported. Still, the problem has many symmetries, especially on vehicles: if two vehicles have same type, using one or the other would not change the cost. If you can assume that a vehicle has exactly one type, I would try a slightly different approach. If a fleet (vehicle type) has F vehicles, I would state that vehicle must be used before vehicle when f ranges in 1..F, to break symmetries in vehicles. That would be worth a try, IMHO – Philippe Couronne Jun 07 '19 at 07:29
  • Dear Philippe, thanks for your reply. Could you maybe add some code in which you implement de abovementioned (which adjustment I have to make to my code in order to do this?) I hope you can help me. Thanks a lot. – Anne-Fleur Jun 08 '19 at 13:00