cuGenOpt/benchmark/common/instances.py

137 lines
4 KiB
Python
Raw Normal View History

"""
标准实例解析器 TSPLIB / CVRPLIB 官方文件读取数据
数据文件位于 data/tsplib/ data/cvrplib/
"""
import math
import os
DATA_ROOT = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data")
TSPLIB_DIR = os.path.join(DATA_ROOT, "tsplib")
CVRPLIB_DIR = os.path.join(DATA_ROOT, "cvrplib")
def parse_tsp(filepath):
"""解析 TSPLIB .tsp 文件EUC_2D 格式)"""
meta = {}
coords = []
reading_coords = False
with open(filepath) as f:
for line in f:
line = line.strip()
if not line:
continue
if line == "NODE_COORD_SECTION":
reading_coords = True
continue
if line in ("EOF", "DISPLAY_DATA_SECTION"):
break
if reading_coords:
parts = line.split()
coords.append((float(parts[1]), float(parts[2])))
else:
if ":" in line:
key, val = line.split(":", 1)
meta[key.strip()] = val.strip()
n = int(meta.get("DIMENSION", len(coords)))
assert len(coords) == n, f"Expected {n} coords, got {len(coords)}"
return {"name": meta.get("NAME", ""), "n": n, "coords": coords}
def parse_vrp(filepath):
"""解析 CVRPLIB .vrp 文件"""
meta = {}
coords = []
demands = []
section = None
with open(filepath) as f:
for line in f:
line = line.strip()
if not line:
continue
if line == "NODE_COORD_SECTION":
section = "coord"
continue
elif line == "DEMAND_SECTION":
section = "demand"
continue
elif line in ("DEPOT_SECTION", "EOF"):
section = None
continue
if section == "coord":
parts = line.split()
coords.append((float(parts[1]), float(parts[2])))
elif section == "demand":
parts = line.split()
demands.append(int(parts[1]))
elif ":" in line:
key, val = line.split(":", 1)
meta[key.strip()] = val.strip()
n = int(meta.get("DIMENSION", len(coords)))
capacity = int(meta.get("CAPACITY", 0))
name = meta.get("NAME", "")
comment = meta.get("COMMENT", "")
optimal = 0
if "Optimal value:" in comment:
optimal = int(comment.split("Optimal value:")[-1].strip().rstrip(")"))
return {
"name": name,
"n": n,
"coords": coords,
"demands": demands,
"capacity": capacity,
"optimal": optimal,
}
def euc2d_dist_matrix(coords):
"""EUC_2D 距离矩阵(四舍五入到整数,与 TSPLIB 标准一致)"""
n = len(coords)
dist = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
dx = coords[i][0] - coords[j][0]
dy = coords[i][1] - coords[j][1]
dist[i][j] = round(math.sqrt(dx * dx + dy * dy))
return dist
# ============================================================
# 预定义实例列表(文件名 → 已知最优)
# ============================================================
TSP_INSTANCES = [
{"file": "eil51.tsp", "optimal": 426},
{"file": "eil76.tsp", "optimal": 538},
{"file": "kroA100.tsp", "optimal": 21282},
{"file": "ch150.tsp", "optimal": 6528},
{"file": "tsp225.tsp", "optimal": 3916},
{"file": "lin318.tsp", "optimal": 42029},
{"file": "pcb442.tsp", "optimal": 50778},
]
VRP_INSTANCES = [
{"file": "A-n32-k5.vrp", "optimal": 784, "n_vehicles": 5},
]
def load_tsp(entry):
"""加载一个 TSP 实例"""
data = parse_tsp(os.path.join(TSPLIB_DIR, entry["file"]))
data["optimal"] = entry["optimal"]
return data
def load_vrp(entry):
"""加载一个 VRP 实例"""
data = parse_vrp(os.path.join(CVRPLIB_DIR, entry["file"]))
data["optimal"] = entry["optimal"]
data["n_vehicles"] = entry["n_vehicles"]
return data