route-explainer / utils /data_utils /cvrptw_dataset.py
daisuke.kikuta
first commit
719d0db
import os
import numpy as np
from utils.data_utils.dataset_base import DatasetBase
from models.solvers.general_solver import GeneralSolver
from models.classifiers.ground_truth.ground_truth_cvrptw import GroundTruthCVRPTW
class CVRPTWDataset(DatasetBase):
"""
CVPRTW dataset by J.K. Falkner et al. https://arxiv.org/abs/2006.09100
L. Xin et al. also adopt the dataset (normalized coords ver.). https://arxiv.org/abs/2110.07983
"""
def __init__(self, coord_dim, num_samples, num_nodes, solver="ortools", classifier="ortools", annotation=True, parallel=True, random_seed=1234, num_cpus=os.cpu_count()):
super().__init__(coord_dim, num_samples, num_nodes, annotation, parallel, random_seed, num_cpus)
# CAPACITY = {
# 20: 500,
# 50: 750,
# 100: 1000
# }
CAPACITY = {
10: 20,
20: 30,
50: 40,
100: 50
}
self.capacity = CAPACITY[num_nodes]
problem = "cvrptw"
solver_type = solver
classifier_type = classifier
self.cvrptw_solver = GeneralSolver(problem=problem, solver_type=solver_type)
self.classifier = GroundTruthCVRPTW(solver_type=classifier_type)
# @override
def generate_instance(self, seed):
np.random.seed(seed)
#-------------
# coordinates
#-------------
coords = np.random.uniform(size=(self.num_nodes+1, self.coord_dim))
#---------
# demands
#---------
# demands = np.random.normal(15, 10, (self.num_nodes+1, )).astype("int")
# demands = np.maximum(np.minimum(np.ceil(np.abs(demands)), 42), 1) # clipping
demands = np.random.randint(1, 10, size=(self.num_nodes+1, ))
demands[0] = 0
#-------------
# time window
#-------------
dist = np.sqrt(((coords[0:1] - coords) ** 2).sum(-1)) * 100
# define sampling horizon
a0 = 0; b0 = 1000
a_sample = np.floor(dist) + 1
b_sample = b0 - a_sample - 10
# sample horizon of each node
a = np.random.uniform(size=(self.num_nodes+1,))
a = (a * (b_sample - a_sample) + a_sample).astype("int")
eps = np.maximum(np.abs(np.random.normal(0, 1, (self.num_nodes+1,))), 0.01)
b = np.minimum(np.ceil(a + 300 * eps), b_sample)
a[0] = a0; b[0] = b0
a = a / 100
b = b / 100
time_window = np.concatenate((a[:, None], b[:, None]), -1)
return {
"coords": coords,
"demand": demands.astype(np.int64),
"time_window": time_window,
"grid_size": np.array([1.0]),
"capacity": np.array([self.capacity], dtype=np.int64)
}
# @override
def annotate(self, instance):
# Solve CVRPTW
node_feats = instance
cvrptw_tours = self.cvrptw_solver.solve(node_feats)
if cvrptw_tours is None:
return
inputs = self.classifier.get_inputs(cvrptw_tours, 0, node_feats)
labels = self.classifier(inputs, annotation=True)
instance.update({"tour": cvrptw_tours, "labels": labels})
return instance