文章目录
什么是VRPLIB标准格式?
CVRPLIB的测试数据为了方便不同平台,不同代码的测试和迁移,针对CVRP问题具有标准的数据格式——VRPLIB
格式,它是一种存放CVRP问题数据的经典形式。
以CVRP标准测试数据 Set-F 中的 F-n45-k4 为例,这份实例文件下有两部分,一部分是输入数据,另一部分是输出的路径方案。
输入数据 .vrp
格式
F-n45-k4 实例数据如下,通过冒号分隔了键值对。
NAME
: 文件名COMMENT
: 数据说明TYPE
: 数据的问题类型DIMENSION
: 节点数EDGE_WEIGHT_TYPE
: 基于节点坐标距离取权值的方式(EUC_2D
:双精度距离,不进行四舍五入)CAPACITY
: 车辆的容量
以上键值对可以通过冒号进行扩展。而对于数据存储部分,则以_SECTION
结尾的单词进行提示。
NODE_COORD_SECTION
: 每行数据代表一个节点的坐标信息,依次存放了节点的索引信息、x坐标、y坐标;DEMAND_SECTION
: 每行数据代表一个节点的坐标信息,依次存放了节点的索引信息、节点的需求量;DEPOT_SECTION
: 指明 depot 节点的索引信息,一般索引值为 1 的都表示 depot 点。
最后用 EOF(End of File )
收尾。
NAME : F-n45-k4
COMMENT : (Fisher: problem 10, No of trucks: 4, Optimal value: 724)
TYPE : CVRP
DIMENSION : 45
EDGE_WEIGHT_TYPE : EUC_2D
CAPACITY : 2010
NODE_COORD_SECTION
1 0 0
...
45 24 -19
DEMAND_SECTION
1 0
...
45 42
DEPOT_SECTION
1
-1
EOF
输出方案 .sol
格式
以 F-n45-k4 实例数据的输出方案为例,方案中用 Route #
打头作为新的一条车辆路径。最后一行是方案的成本。
注意,路径方案中默认省略了出发点的索引,其中的序号是上面输入数据的索引号加1,例如这里的节点1,表示第1个客户节点,即上面输入数据中索引值为“2”的节点。
Route #1: 43 44 28 33 29 27 6 5 7 35 3 4 14 13 12 11 18 17 10
Route #2: 42 30 41 32 31 34 40 39 36 38 37
Route #3: 24 9 15 1 2 16
Route #4: 20 21 25 26 23 22 19 8
Cost 724
Python调用vrplib库解析VRPLIB格式文件
vrplib
是一个专门用于处理带容量车辆路径问题 (CVRP) 实例的 Python 包。它最主要的特点是可以解析和编写VRPLIB标准格式的数据,以及从CVRPLIB上下载标准测试数据。它依赖于numpy
库,且要求python 3.8+
的解释器版本。
1. 安装并引入 vrplib
通过清华源安装 vrplib
库:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple vrplib
在代码开头引入:
import vrplib
2. 利用vrplib库下载标准数据集
vrplib
还有一个常用的功能就是能从 CVRPLIB
上下载相应的文件,并将数据文件存放在指定的 path 路径下。
vrplib.download_instance("F-n45-k4", path)
vrplib.download_solution("F-n45-k4", path")
3. 解析相应的数据文件
通过下面两行代码即可解析.vrp
数据文件和.sol
数据文件,并会自动打印解析的内容,我们来看下。
instance = vrplib.read_instance("F-n45-k4.vrp")
solution = vrplib.read_solution("F-n45-k4.sol")
以下是解析了.vrp
数据文件得到的内容,是一个包含所有解析数据的字典,同时会根据权重计算方式,将坐标信息转化为节点之间的运输权值(成本)。
{'name': 'F-n45-k4',
'comment': '(Fisher: problem 10, No of trucks: 4, Optimal value: 724)',
'type': 'CVRP',
'dimension': 45,
'edge_weight_type': 'EUC_2D',
'capacity': 2010,
'node_coord': array([[ 0.00e+00, 0.00e+00],
[ 3.00e+00, 5.00e+00],
...
[ 2.40e+01, -1.80e+01],
[ 2.40e+01, -1.90e+01]]),
'demand': array([0, .... 42]),
'depot': array([0]),
'edge_weight': array([[ 0. , 5.83095189, 9.34077085, ..., 28.0713377 ,
30. , 30.61045573],
[ 5.83095189, 0. , 4.03112887, ..., 25.96150997,
31.144823 , 31.89043744],
[ 9.34077085, 4.03112887, 0. , ..., 27.77138815,
34.51448971, 35.30226622],
...,
[28.0713377 , 25.96150997, 27.77138815, ..., 0. ,
16.4924225 , 17.4642492 ],
[30. , 31.144823 , 34.51448971, ..., 16.4924225 ,
0. , 1. ],
[30.61045573, 31.89043744, 35.30226622, ..., 17.4642492 ,
1. , 0. ]])}
而解析了.sol
数据文件得到的内容如下,是一个包含了路径信息,以及方案成本的字典。
{'routes': [[43, 44, 28, 33, 29, 27, 6, 5, 7, 35, 3, 4, 14, 13, 12, 11, 18, 17, 10], [42, 30, 41, 32, 31, 34, 40, 39, 36, 38, 37], [24, 9,
15, 1, 2, 16], [20, 21, 25, 26, 23, 22, 19, 8]],
'cost': 724}
4. 解析 Solomon 数据集中的 txt 文件
前面文章中提到,solomon数据集也是经典的CVRP数据集,但是它的输入数据文件是txt
文件,是否也可以用vrplib
进行解析呢?答案是可以的。
instance = vrplib.read_instance("C101.txt", instance_format="solomon")
具体的返回内容与.vrp
文件的返回内容类似。
利用vrplib库将数据编写为VRPLIB格式文件
假设现在我们是有了数据字典,想将它转化为VRPLIB格式文件,以方便测试其他标准CVRP数据的代码能够直接用来测试我们的数据,这时候就可以用到vrplib
的转写功能,具体如下:
## 转写 vrp 数据
instance_loc = "instance.vrp"
instance_data = {
"NAME": "instance",
"TYPE": "CVRP",
"VEHICLES": 2,
"DIMENSION": 1,
"CAPACITY": 1,
"EDGE_WEIGHT_TYPE": "EUC_2D",
"NODE_COORD_SECTION": [[250, 250], [500, 500]],
"DEMAND_SECTION": [1, 1],
"DEPOT_SECTION": [1],
}
vrplib.write_instance(instance_loc, instance_data)
# 转写 sol 方案数据
solution_loc = "solution.sol"
routes = [[1], [2, 3], [4, 5, 6]]
solution_data = {"Cost": 42, "Vehicle types": [1, 2, 3]}
vrplib.write_solution(solution_loc, routes, solution_data)
分别会得到的一个名为 instance
的.vrp
文件,和一个名为 solution
的 .sol
文件
NAME: instance
TYPE: CVRP
VEHICLES: 2
DIMENSION: 1
CAPACITY: 1
EDGE_WEIGHT_TYPE: EUC_2D
NODE_COORD_SECTION
1 250 250
2 500 500
DEMAND_SECTION
1 1
2 1
DEPOT_SECTION
1
EOF
Route #1: 1
Route #2: 2 3
Route #3: 4 5 6
Cost: 42
Vehicle types: [1, 2, 3]