Vehicle Routing Problem Backhauls
模型说明:Vehicle Routing Problem(VRP)+Backhauls
VRP:即多个车辆从depot点出发,向客户customer配送货物,每个customer只能被一辆车配送一次,最后车辆回到depot点
Backhauls:回程过程中取货的问题
VRPB:送货(linehaul-delivery-L)后取货(backhauls-pickup-B),现实生活中:快递小哥配送件的同时+取件,且快递小哥不能全程只单纯取件不配件; 配件主要,取件附属品
数学模型:
参数说明:
A:边; V:所有的customer+depot(由0表示);
: 进入i点的边(ex,.一个点,有进入i的边,和从i出去的边)
: 和从i出去的边
:代表边,是A的子集,: 代表S中最少的车的个数(如果求他是BPP问题)
(1)目标函数,总路程最小
(2,3)对于每一个点,只有一辆车经过一次
(4,5)对于depot(0),出去的车和进来的车都一个为K
(6,7)为capacity constraint cut, 比如(6):在S集合里的点,与剩余的除了depot的点相连的边的个数必须大于这个集合需要的最小的车数.(画一个圈把S里的点全部包含,从S出去/进来的点的边-即和S这个圈圈相交的边的个数和除以2)
问题分解
通过分析问题,可以将模型分解为三部分
第一部分:linehaul部分,即delivery的部分
第二部分:backhaul部分,即pickup部分
第三部分:connection部分,即delivery-pickup的连接弧的部分
注意:depot进入linehaul的边,即总的车辆数>=backhaul 进入depot的边,因为车辆可以直接从linehaul回去,不进行pickup
分解后的模型:
点集合V被分为:L(linehaul点),B(backhaul点), L0(linehaul点+depot0),
B0(backhaul点+depot0)
边集合A被分为:A_L(linehaul边,从L0到L),
A_B(从B到B0), A_C(L到B0)
1.linehaul
2.backhaul
3.connection
python code
小算列数据解释:
车:capacity:2,K=2(车数量)
Linehaul(delivery点):L=[1,2,3] , L0=[0,1,2,3], d(demand)={},A_L: 从L0到L的边
backhaul(pickup点):B=[4,5],B0=[0,4,5],d(demand)={},A_B:从B到B0的边
connection:A_C:从L到B0的边
代码块
import numpy as np
import time
from docplex.mp.model import Model
##############################################################################
'''
This dataset corresponds to our small-scale dataset with 6 nodes including depot
and 5 customers/nodes in which customer 3 requires both delivery and pickup, i.e.,
simultaneous demand.
For instance: one node can be delivery and pickup
We use the CPLEX optimization solver, the academic version, to solve this problem.
'''
##############################################################################
# function that solves VRPB
def CVRPB():
mdl = Model('CVRPB')
# linhaul decision Variables
x = mdl.binary_var_dict (A_L,name='x')
u = mdl.continuous_var_dict (L, name = 'u')
#bakchaul decision variables
y = mdl.binary_var_dict (A_B,name='y')
w = mdl.continuous_var_dict (B,name = 'w')
#connection decision variables
z = mdl.binary_var_dict (A_C,name='z')
#####################################
##### linhaul constraints
#truck constraint
mdl.add_constraint(mdl.sum(x[0,j]for j in L)==k )
#degree constraints
mdl.add_constraints(mdl.sum(x[i,j]for i in L_0 if i!=j)==1 for j in L )
#truck capacity
mdl.add_constraints(u[i]-u[j]+Q*x[i,j]<= Q-d_L[j] for i,j in A_L if i!=0 and j!=0)
##### bakchaul constraints
#truck constraints
mdl.add_constraint(mdl.sum(y[i,0]for i in B)+mdl.sum(z[i,0] for i in L)==k )
#mdl.add_constraint(mdl.sum(y[i,0]for i in B)<=k )
#degree constraints
mdl.add_constraints(mdl.sum(y[i,j]for j in B_0 if j!=i)==1 for i in B)
#truck capacity
mdl.add_constraints(w[i]-w[j]+Q*y[i,j]<=Q-d_B[j] for i,j in A_B if i!=0 and j!=0)
####### connection constraints
#truck constraints
mdl.add_constraint(mdl.sum(z[i,j]for i,j in A_C )==k)
#degree constraints
mdl.add_constraints(mdl.sum(x[i,j] for j in L if j!=i )+ mdl.sum(z[i,j] for j in B_0 )==1 for i in L)
mdl.add_constraints(mdl.sum(y[i,j] for i in B if i!=j )+ mdl.sum(z[i,j] for i in L )==1 for j in B)
###################################
####objective functions
obj_Linhaul = mdl.sum(c[i,j]*x[i,j]for i,j in A_L)
mdl.add_kpi(obj_Linhaul, 'Linehaul Cost')
obj_Backhaul = mdl.sum(c[i,j]*y[i,j]for i,j in A_B)
mdl.add_kpi(obj_Backhaul, 'Backhaul Cost')
obj_Connection = mdl.sum(c[i,j]*z[i,j]for i,j in A_C)
mdl.add_kpi(obj_Connection, 'Connection Cost')
objective = obj_Linhaul+obj_Backhaul+obj_Connection
mdl.add_kpi(objective, 'Total Cost')
#######################################
#solving model
mdl.minimize(objective)
#mdl.parameters.timelimit=5
solution =mdl.solve(log_output=False) #true if you need to see the steps of slover
#mdl.report_kpis()
if not solution:
print('fail in solving, there is no feasible solution')
#mdl.export_as_lp()
########################################
#Collect optimal solution
x_opt=[a for a in A_L if x[a].solution_value> 0.9]
y_opt=[a for a in A_B if y[a].solution_value> 0.9]
z_opt=[a for a in A_C if z[a].solution_value> 0.9]
obj_opt=round(solution.objective_value,2)
return obj_opt, x_opt, y_opt, z_opt
##############################################################################
#### step 1: preparing data
# location of nodes
loc_x = {0:0, 1:0, 2:2, 3:2, 4:0.5, 5:1.5}
loc_y = {0:0, 1:2, 2:2, 3:0, 4:1, 5:1}
# vehicles capacity and number of available vehicles at the depot
Q = 2
k = 2
# all nodes, where 0 states the depot. all links and symetric distance matrix
V = [0, 1, 2, 3, 4, 5]
A = [(i,j) for i in V for j in V]
c = {(i,j): round(np.sqrt((loc_x[j]-loc_x[i])**2 + (loc_y[j]-loc_y[i])**2),2) for i,j in A}
# all nodes and links related to linehaul customers
L = [1, 2, 3]
L_0 = [0] +L
d_L = {1:1, 2:1, 3:1}
A_L = [(i,j) for i in L_0 for j in L if i!=j]
# all nodes and links related to backhaul customers
B = [1,3, 4, 5]
B_0 = B + [0]
d_B = {1:1 ,3:1, 4:1, 5:1}
A_B = [(i,j) for i in B for j in B_0 if i!=j]
# all links related to connecting linehaul routes to backhaul routes or to the depot
A_C= [(i,j) for i in L for j in B_0]
#### step 2: solve the problem
start = time.clock()
obj_opt, x_opt, y_opt, z_opt = CVRPB()
stop = time.clock()
CPU_running_time = round(stop - start,2)
#### step 3: print the solution
print ('The solution for VRPB with simultaneous demand for small-scale dataset is:',
'\nObjective value = ', obj_opt, '\nCPU running time in second = ', CPU_running_time)
print('\n the route sequence is', '\nlinehau = ', x_opt, '\nconnection = ', z_opt, '\nbaclhaul = ', y_opt )
https://github.com/IrandokhtPVZ/Vehicle-Routing-Problem-with-Backhaul
引用的代码
An Exact Algorithm for the Vehicle Routing Problem with Backhauls Author(s): PAOLO TOTH and DANIELE VIGO