Capacitated Vehicle Routing problem CVRP

Link to Capacitated Vehicle Routing problem CVRP == https://github.com/mayashenoi/DO/blob/main/cvrp-cplex(2).ipynb


# Capcitaed Vehicle Routing problem. 
# Minimize the number of trips by a vehicle based on the capacity of the vehicle delivering quantities to customers
import numpy as np
rnd = np.random
rnd.seed(0)
n = 10 #number of customers/locations
Q = 20 #vehicle capacity
N = [i for i in range(1, n+1)] #customer set
V = [0] + N # set of nodes, customer + 1 depot
q = {i: rnd.randint(1, 10) for i in N} #amount to be delivered to this client random number from 1 to 10
loc_x = rnd.rand(len(V))*200
loc_y = rnd.rand(len(V))*100
import matplotlib.pyplot as plt
plt.scatter(loc_x[1:], loc_y[1:], c='b')
for i in N:
    plt.annotate('$q_%d=%d$' % (i, q[i]), (loc_x[i]+2, loc_y[i]))
plt.plot(loc_x[0], loc_y[0], c='r', marker='s')
plt.axis('equal')
(-5.390764142541891, 202.16699573081289, 7.525723005074169, 102.16355380509556)

A = [(i, j) for i in V for j in V if i != j] #A is set of arcs
c = {(i, j): np.hypot(loc_x[i]-loc_x[j], loc_y[i]-loc_y[j]) for i, j in A} # cost to travel over the arcs
from docplex.mp.model import Model
mdl = Model('CVRP')
x = mdl.binary_var_dict(A, name='x') # dictionary of arcs
u = mdl.continuous_var_dict(N, ub=Q, name='u') #dictionary of packages/quantities to be delivered at each customer
mdl.minimize(mdl.sum(c[i, j]*x[i, j] for i, j in A)) # minimize distance travelled
mdl.add_constraints(mdl.sum(x[i, j] for j in V if j != i) == 1 for i in N) # ensure each customer is visited only once
mdl.add_constraints(mdl.sum(x[i, j] for i in V if i != j) == 1 for j in N) # ensure vehicle leaves the customer location
mdl.add_indicator_constraints(mdl.indicator_constraint(x[i, j], u[i]+q[j] == u[j]) for i, j in A if i != 0 and j != 0) #route starts from depot and does not go in subtour(??)
mdl.add_constraints(u[i] >= q[i] for i in N) #truck's capacity is greater than customer's quantity to be delivered
mdl.parameters.timelimit = 15
solution = mdl.solve(log_output=True)
WARNING: Number of workers has been reduced to 2 to comply with platform limitations.
Version identifier: 22.1.1.0 | 2023-06-15 | d64d5bd77
CPXPARAM_Read_DataCheck                          1
CPXPARAM_Threads                                 2
CPXPARAM_TimeLimit                               15
Found incumbent of value 1784.255531 after 0.00 sec. (0.01 ticks)
Tried aggregator 2 times.
MIP Presolve eliminated 10 rows and 0 columns.
MIP Presolve modified 45 coefficients.
Aggregator did 45 substitutions.
Reduced MIP has 65 rows, 165 columns, and 335 nonzeros.
Reduced MIP has 110 binaries, 0 generals, 0 SOSs, and 90 indicators.
Presolve time = 0.01 sec. (0.35 ticks)
Probing time = 0.00 sec. (0.32 ticks)
Tried aggregator 1 time.
Detecting symmetries...
Reduced MIP has 65 rows, 165 columns, and 335 nonzeros.
Reduced MIP has 110 binaries, 0 generals, 0 SOSs, and 90 indicators.
Presolve time = 0.00 sec. (0.23 ticks)
Probing time = 0.00 sec. (0.31 ticks)
Clique table members: 65.
MIP emphasis: balance optimality and feasibility.
MIP search method: dynamic search.
Parallel mode: deterministic, using up to 2 threads.
Root relaxation solution time = 0.00 sec. (0.13 ticks)

        Nodes                                         Cuts/
   Node  Left     Objective  IInf  Best Integer    Best Bound    ItCnt     Gap

*     0+    0                         1784.2555        0.0000           100.00%
      0     0      456.3031    10     1784.2555      456.3031       15   74.43%
      0     0      477.8467     8     1784.2555      Cuts: 23       26   73.22%
      0     0      477.8467    14     1784.2555      Cuts: 18       38   73.22%
      0     0      477.8467    12     1784.2555       Cuts: 3       39   73.22%
*     0+    0                          875.2215      477.8467            45.40%
*     0+    0                          851.1001      477.8467            43.86%
Detecting symmetries...
      0     2      477.8467    12      851.1001      477.8467       39   43.86%
Elapsed time = 0.05 sec. (3.90 ticks, tree = 0.02 MB, solutions = 3)
*    63    24      integral     0      792.8484      481.2282      155   39.30%
*    82+   49                          766.8162      481.2282            37.24%
*   103    55      integral     0      732.0147      481.2282      290   34.26%
*   299+  127                          726.2486      483.2994            33.45%

Performing restart 1

Repeating presolve.
Tried aggregator 1 time.
Reduced MIP has 65 rows, 165 columns, and 335 nonzeros.
Reduced MIP has 110 binaries, 0 generals, 0 SOSs, and 90 indicators.
Presolve time = 0.00 sec. (0.16 ticks)
Tried aggregator 1 time.
Reduced MIP has 65 rows, 165 columns, and 335 nonzeros.
Reduced MIP has 110 binaries, 0 generals, 0 SOSs, and 90 indicators.
Presolve time = 0.00 sec. (0.24 ticks)
Represolve time = 0.01 sec. (1.30 ticks)
   3702     0      484.0331    19      726.2486      Cuts: 13    20701   16.38%
   3702     0      490.4492    13      726.2486       Cuts: 3    20704   16.38%
   3702     0      491.3074     7      726.2486      Cuts: 36    20717   16.38%
   3702     0      500.2467    17      726.2486      Cuts: 31    20723   16.38%
   3702     0      503.0517    10      726.2486      Cuts: 20    20734   16.38%
   3702     0      503.0517    15      726.2486      Cuts: 22    20740   16.38%
   3702     0      503.0517    12      726.2486   Impl Bds: 3    20742   16.38%
   4553   282        cutoff            726.2486      607.2656    23920   16.38%
   7445   274      667.3517    15      726.2486      648.1099    46416   10.76%

Clique cuts applied:  1
Cover cuts applied:  66
Implied bound cuts applied:  94
Mixed integer rounding cuts applied:  1
Zero-half cuts applied:  2
Gomory fractional cuts applied:  7

Root node processing (before b&c):
  Real time             =    0.06 sec. (3.87 ticks)
Parallel b&c, 2 threads:
  Real time             =    0.92 sec. (522.21 ticks)
  Sync time (average)   =    0.06 sec.
  Wait time (average)   =    0.00 sec.
                          ------------
Total (root+branch&cut) =    0.98 sec. (526.08 ticks)
print(solution)
solution for: CVRP
objective: 726.249
status: OPTIMAL_SOLUTION(2)
x_0_2=1
x_0_3=1
x_0_10=1
x_1_0=1
x_2_9=1
x_3_5=1
x_4_0=1
x_5_0=1
x_6_1=1
x_7_8=1
x_8_4=1
x_9_7=1
x_10_6=1
u_1=18.000
u_2=2.000
u_3=12.000
u_4=20.000
u_5=20.000
u_6=12.000
u_7=13.000
u_8=16.000
u_9=7.000
u_10=8.000

solution.solve_status
<JobSolveStatus.OPTIMAL_SOLUTION: 2>
active_arcs = [a for a in A if x[a].solution_value > 0.9]
plt.scatter(loc_x[1:], loc_y[1:], c='b')
for i in N:
    plt.annotate('$q_%d=%d$' % (i, q[i]), (loc_x[i]+2, loc_y[i]))
for i, j in active_arcs:
    plt.plot([loc_x[i], loc_x[j]], [loc_y[i], loc_y[j]], c='g', alpha=0.3)
plt.plot(loc_x[0], loc_y[0], c='r', marker='s')
plt.axis('equal')
(-5.390764142541891, 202.16699573081289, 7.525723005074169, 102.16355380509556)

Leave a comment