Skip to content

Commit

Permalink
Merge pull request loggi#9 from loggi/fillipe/update-task1-tests
Browse files Browse the repository at this point in the history
Add Great Circle distance to tests in task 1
  • Loading branch information
Fillipe Goulart authored Apr 11, 2021
2 parents 42433cf + 60f868e commit 54edd0e
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 36 deletions.
22 changes: 18 additions & 4 deletions loggibud/v1/baselines/task2/kmeans_greedy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@


@dataclass
class KmeansGreedyParams:
class KMeansGreedyParams:
fixed_num_clusters: Optional[int] = None
variable_num_clusters: Optional[int] = None
seed: int = 0
Expand All @@ -45,16 +45,16 @@ def get_baseline(cls):

@dataclass
class KMeansGreedyModel:
params: KmeansGreedyParams
params: KMeansGreedyParams
clustering: KMeans
subinstance: Optional[CVRPInstance] = None
cluster_subsolutions: Optional[Dict[int, List[CVRPSolutionVehicle]]] = None


def pretrain(
instances: List[CVRPInstance], params: Optional[KmeansGreedyParams] = None
instances: List[CVRPInstance], params: Optional[KMeansGreedyParams] = None
) -> KMeansGreedyModel:
params = params or KmeansGreedyParams.get_baseline()
params = params or KMeansGreedyParams.get_baseline()

points = np.array(
[
Expand Down Expand Up @@ -150,6 +150,20 @@ def finish(instance: CVRPInstance, model: KMeansGreedyModel) -> CVRPSolution:
)


def solve_instance(
model: KMeansGreedyModel, instance: CVRPInstance
) -> CVRPSolution:
"""Solve an instance dinamically using a solver model"""
logger.info("Finetunning on evaluation instance.")
model_finetuned = finetune(model, instance)

logger.info("Starting to dynamic route.")
for delivery in tqdm(instance.deliveries):
model_finetuned = route(model_finetuned, delivery)

return finish(instance, model_finetuned)


if __name__ == "__main__":

logging.basicConfig(level=logging.INFO)
Expand Down
18 changes: 15 additions & 3 deletions loggibud/v1/baselines/task2/qrp_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ class QRPModel:
subinstance: Optional[CVRPInstance] = None
# Center of all historical packages, used to translate new deliveries and
# compute their equivalent angles
center: np.ndarray
center: np.ndarray = np.zeros((0, 2))
# Angle intervals describing each sub-region. They are described by a
# `n_cluster` x 2 array with the form
# [[-pi, angle_1], [angle_1, angle_2], ..., [angle_n, pi]]
angle_intervals: np.ndarray
angle_intervals: np.ndarray = np.zeros((0, 2))

def predict(self, delivery: Delivery) -> int:
"""Predict the best subregion for a given delivery
Expand Down Expand Up @@ -157,7 +157,7 @@ def _get_number_of_vehicles(instance: CVRPInstance) -> int:
total_demand = sum(delivery.size for delivery in instance.deliveries)
return int(np.ceil(total_demand / instance.vehicle_capacity))

num_clusters = params.num_clusters or max(
num_clusters = params.num_clusters or min(
_get_number_of_vehicles(instance) for instance in instances
)

Expand Down Expand Up @@ -257,6 +257,18 @@ def finish(instance: CVRPInstance, model: QRPModel) -> CVRPSolution:
)


def solve_instance(model: QRPModel, instance: CVRPInstance) -> CVRPSolution:
"""Solve an instance dinamically using a solver model"""
logger.info("Finetunning on evaluation instance.")
model_finetuned = finetune(model, instance)

logger.info("Starting to dynamic route.")
for delivery in tqdm(instance.deliveries):
model_finetuned = route(model_finetuned, delivery)

return finish(instance, model_finetuned)


if __name__ == "__main__":

logging.basicConfig(level=logging.INFO)
Expand Down
29 changes: 0 additions & 29 deletions tests/v1/test_cvrp_baselines.py

This file was deleted.

79 changes: 79 additions & 0 deletions tests/v1/test_task1_baselines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import json

import pytest
from dacite import from_dict
from mock import patch

from loggibud.v1.types import CVRPInstance
from loggibud.v1.baselines.shared import ortools
from loggibud.v1.baselines.task1 import lkh_3
from loggibud.v1.distances import (
calculate_distance_matrix_great_circle_m,
calculate_route_distance_great_circle_m,
)
from loggibud.v1.eval.task1 import evaluate_solution


@pytest.fixture
def toy_cvrp_instance():
with open("tests/results/cvrp-instances/train/rj-0/cvrp-0-rj-0.json") as f:
data = json.load(f)

return from_dict(CVRPInstance, data)


@pytest.fixture
def mocked_ortools_osrm_distance_matrix():
"""Monkey-patch the OR-Tools OSRM distance matrix with the Great Circle"""

with patch(
"loggibud.v1.baselines.shared.ortools.calculate_distance_matrix_m",
new=calculate_distance_matrix_great_circle_m,
) as mock_osrm:
yield mock_osrm


@pytest.fixture
def mocked_lkh_osrm_distance_matrix():
"""Monkey-patch the LKH OSRM distance matrix with the Great Circle"""

with patch(
"loggibud.v1.data_conversion.calculate_distance_matrix_m",
new=calculate_distance_matrix_great_circle_m,
) as mock_osrm:
yield mock_osrm


@pytest.fixture
def mocked_osrm_route_distance():
"""Monkey-patch the OSRM route distance with the Great Circle"""

with patch(
"loggibud.v1.eval.task1.calculate_route_distance_m",
new=calculate_route_distance_great_circle_m,
) as mock_osrm:
yield mock_osrm


def test_ortools_solver(
toy_cvrp_instance,
mocked_ortools_osrm_distance_matrix,
mocked_osrm_route_distance,
):
result = ortools.solve(toy_cvrp_instance)

total_distance = evaluate_solution(toy_cvrp_instance, result)

assert total_distance


def test_lkh_solver(
toy_cvrp_instance,
mocked_lkh_osrm_distance_matrix,
mocked_osrm_route_distance,
):
result = lkh_3.solve(toy_cvrp_instance)

total_distance = evaluate_solution(toy_cvrp_instance, result)

assert total_distance
102 changes: 102 additions & 0 deletions tests/v1/test_task2_baselines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import json

import pytest
from dacite import from_dict
from mock import patch
from pathlib import Path

from loggibud.v1.types import CVRPInstance
from loggibud.v1.baselines.shared.ortools import ORToolsParams
from loggibud.v1.baselines.task2 import kmeans_greedy, qrp_sweep
from loggibud.v1.distances import (
calculate_distance_matrix_great_circle_m,
calculate_route_distance_great_circle_m,
)
from loggibud.v1.eval.task1 import evaluate_solution


@pytest.fixture
def train_instances():
file_instances = Path("tests/results/cvrp-instances/train/rj-0/").rglob(
"*.json"
)

def load_instance(file_instance):
with open(file_instance) as f:
data = json.load(f)

return from_dict(CVRPInstance, data)

instances = [
load_instance(file_instance) for file_instance in file_instances
]

return instances


@pytest.fixture
def dev_instance():
with open("tests/results/cvrp-instances/dev/rj-0/cvrp-0-rj-3.json") as f:
data = json.load(f)

return from_dict(CVRPInstance, data)


@pytest.fixture
def mocked_ortools_osrm_distance_matrix():
"""Monkey-patch the OR-Tools OSRM distance matrix with the Great Circle"""

with patch(
"loggibud.v1.baselines.shared.ortools.calculate_distance_matrix_m",
new=calculate_distance_matrix_great_circle_m,
) as mock_osrm:
yield mock_osrm


@pytest.fixture
def mocked_osrm_route_distance():
"""Monkey-patch the OSRM route distance with the Great Circle"""

with patch(
"loggibud.v1.eval.task1.calculate_route_distance_m",
new=calculate_route_distance_great_circle_m,
) as mock_osrm:
yield mock_osrm


def test_kmeans_greedy_solver(
train_instances,
dev_instance,
mocked_osrm_route_distance,
mocked_ortools_osrm_distance_matrix,
):
# Limit OR-Tools TSP solver to 100 ms (this is just a test, a good solution
# is not required)
params = kmeans_greedy.KMeansGreedyParams(
ortools_tsp_params=ORToolsParams(time_limit_ms=100)
)
model = kmeans_greedy.pretrain(train_instances, params=params)
result = kmeans_greedy.solve_instance(model, dev_instance)

total_distance = evaluate_solution(dev_instance, result)

assert total_distance


def test_qrp_sweep_solver(
train_instances,
dev_instance,
mocked_osrm_route_distance,
mocked_ortools_osrm_distance_matrix,
):
# Limit OR-Tools TSP solver to 100 ms (this is just a test, a good solution
# is not required)
params = qrp_sweep.QRPParams(
ortools_tsp_params=ORToolsParams(time_limit_ms=100)
)
model = qrp_sweep.pretrain(train_instances, params=params)
result = qrp_sweep.solve_instance(model, dev_instance)

total_distance = evaluate_solution(dev_instance, result)

assert total_distance

0 comments on commit 54edd0e

Please sign in to comment.