mirror of
https://github.com/L-yang-yang/cugenopt.git
synced 2026-04-25 12:16:21 +02:00
136 lines
4 KiB
Python
136 lines
4 KiB
Python
"""
|
||
标准实例解析器 — 从 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
|