Skip to content

Commit ff325cb

Browse files
authored
Merge pull request #126 from Kuifje02/non-elementary-routes
Great job @torressa !!!
2 parents 667a118 + 7dd4d00 commit ff325cb

20 files changed

+470
-320
lines changed

benchmarks/run.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from multiprocessing import Pool, cpu_count
55
from pathlib import Path
66
from typing import List, Dict, Union
7+
78
from networkx import DiGraph
9+
810
from benchmarks.augerat_dataset import AugeratDataSet
911
from benchmarks.solomon_dataset import SolomonDataSet
1012
from benchmarks.utils.csv_table import CsvTable
@@ -202,7 +204,7 @@ def _run_single_problem(path_to_instance: Path, **kwargs):
202204

203205

204206
def main():
205-
""" Run parallel or series"""
207+
"""Run parallel or series"""
206208
if SERIES:
207209
run_series()
208210
else:

benchmarks/tests/graph_issue101

16.2 KB
Binary file not shown.

benchmarks/tests/pytest.ini

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
[pytest]
2+
log_cli = 1
3+
log_cli_level = INFO
4+
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5+
log_cli_date_format=%Y-%m-%d %H:%M:%S
26
python_files = test_*.py
37
#testpaths = tests/
48
filterwarnings =
5-
ignore::DeprecationWarning
9+
ignore::DeprecationWarning

benchmarks/tests/test_cvrptw_solomon.py

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@
44

55

66
class TestsSolomon:
7-
87
def setup(self):
98
"""
109
Solomon instance c101, 25 first nodes only including depot
1110
"""
12-
data = SolomonDataSet(path="benchmarks/data/cvrptw/",
13-
instance_name="C101.txt",
14-
n_vertices=25)
11+
data = SolomonDataSet(
12+
path="benchmarks/data/cvrptw/", instance_name="C101.txt", n_vertices=25
13+
)
1514
self.G = data.G
1615
self.n_vertices = 25
17-
self.prob = VehicleRoutingProblem(self.G,
18-
load_capacity=data.max_load,
19-
time_windows=True)
16+
self.prob = VehicleRoutingProblem(
17+
self.G, load_capacity=data.max_load, time_windows=True
18+
)
2019
initial_routes = [
2120
["Source", 13, 17, 18, 19, 15, 16, 14, 12, 1, "Sink"],
2221
["Source", 20, 24, 23, 22, 21, "Sink"],
@@ -26,7 +25,7 @@ def setup(self):
2625
# Set repeating solver arguments
2726
self.solver_args = {
2827
"pricing_strategy": "BestPaths",
29-
"initial_routes": initial_routes
28+
"initial_routes": initial_routes,
3029
}
3130

3231
def test_setup_instance_name(self):
@@ -40,43 +39,18 @@ def test_setup_nodes(self):
4039
assert len(self.G.nodes()) == self.n_vertices + 1
4140

4241
def test_setup_edges(self):
43-
assert len(
44-
self.G.edges()) == self.n_vertices * (self.n_vertices - 1) + 1
42+
assert len(self.G.edges()) == self.n_vertices * (self.n_vertices - 1) + 1
4543

4644
def test_subproblem_lp(self):
4745
# benchmark result
4846
# e.g., in Feillet et al. (2004)
4947
self.prob.solve(**self.solver_args, cspy=False)
5048
assert round(self.prob.best_value, -1) in [190, 200]
51-
52-
def test_schedule_lp(self):
53-
'Tests whether final schedule is time-window feasible'
54-
self.prob.solve(**self.solver_args, cspy=False)
55-
# Check arrival times
56-
for k1, v1 in self.prob.arrival_time.items():
57-
for k2, v2 in v1.items():
58-
assert (self.G.nodes[k2]["lower"] <= v2)
59-
assert (v2 <= self.G.nodes[k2]["upper"])
60-
# Check departure times
61-
for k1, v1 in self.prob.departure_time.items():
62-
for k2, v2 in v1.items():
63-
assert (self.G.nodes[k2]["lower"] <= v2)
64-
assert (v2 <= self.G.nodes[k2]["upper"])
49+
self.prob.check_arrival_time()
50+
self.prob.check_departure_time()
6551

6652
def test_subproblem_cspy(self):
6753
self.prob.solve(**self.solver_args, cspy=True)
6854
assert round(self.prob.best_value, -1) in [190, 200]
69-
70-
def test_schedule_cspy(self):
71-
'Tests whether final schedule is time-window feasible'
72-
self.prob.solve(**self.solver_args)
73-
# Check departure times
74-
for k1, v1 in self.prob.departure_time.items():
75-
for k2, v2 in v1.items():
76-
assert (self.G.nodes[k2]["lower"] <= v2)
77-
assert (v2 <= self.G.nodes[k2]["upper"])
78-
# Check arrival times
79-
for k1, v1 in self.prob.arrival_time.items():
80-
for k2, v2 in v1.items():
81-
assert (self.G.nodes[k2]["lower"] <= v2)
82-
assert (v2 <= self.G.nodes[k2]["upper"])
55+
self.prob.check_arrival_time()
56+
self.prob.check_departure_time()
Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from pytest import fixture
2+
from time import time
3+
import csv
24

35
from vrpy import VehicleRoutingProblem
46

57
from benchmarks.solomon_dataset import SolomonDataSet
68

7-
params = list(range(7, 10))
9+
params = list(range(7, 70))
810

911

1012
@fixture(
@@ -16,17 +18,55 @@ def n(request):
1618
return request.param
1719

1820

21+
REPS_LP = 1
22+
REPS_CSPY = 10
23+
24+
25+
def write_avg(n, times_cspy, iter_cspy, times_lp, iter_lp, name="cspy102fwdearly"):
26+
def _avg(l):
27+
return sum(l) / len(l)
28+
29+
with open(f"benchmarks/results/{name}.csv", "a", newline="") as f:
30+
writer_object = csv.writer(f)
31+
writer_object.writerow(
32+
[n, _avg(times_cspy), _avg(iter_cspy), _avg(times_lp), _avg(iter_lp)]
33+
)
34+
f.close()
35+
36+
1937
class TestsSolomon:
2038
def test_subproblem(self, n):
21-
data = SolomonDataSet(path="benchmarks/data/cvrptw/",
22-
instance_name="C101.txt",
23-
n_vertices=n)
39+
data = SolomonDataSet(
40+
path="benchmarks/data/cvrptw/", instance_name="C101.txt", n_vertices=n
41+
)
2442
self.G = data.G
25-
self.prob = VehicleRoutingProblem(self.G,
26-
load_capacity=data.max_load,
27-
time_windows=True)
28-
self.prob.solve(cspy=False)
29-
best_value_lp = self.prob.best_value
30-
self.prob.solve(cspy=True)
31-
best_value_cspy = self.prob.best_value
32-
assert int(best_value_lp) == int(best_value_cspy)
43+
best_values_lp = None
44+
lp_iter = []
45+
times_lp = []
46+
for r in range(REPS_LP):
47+
prob = VehicleRoutingProblem(
48+
self.G, load_capacity=data.max_load, time_windows=True
49+
)
50+
start = time()
51+
prob.solve(cspy=False)
52+
best_value_lp = prob.best_value
53+
times_lp.append(time() - start)
54+
lp_iter.append(prob._iteration)
55+
del prob
56+
best_values_cspy = []
57+
times_cspy = []
58+
iter_cspy = []
59+
for r in range(REPS_CSPY):
60+
prob = VehicleRoutingProblem(
61+
self.G, load_capacity=data.max_load, time_windows=True
62+
)
63+
start = time()
64+
prob.solve(cspy=True, pricing_strategy="Exact")
65+
times_cspy.append(time() - start)
66+
best_values_cspy.append(prob.best_value)
67+
iter_cspy.append(prob._iteration)
68+
prob.check_arrival_time()
69+
prob.check_departure_time()
70+
del prob
71+
assert all(best_value_lp == val_cspy for val_cspy in best_values_cspy)
72+
write_avg(n, times_cspy, iter_cspy, times_lp, lp_iter)

benchmarks/tests/test_examples.py

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,35 +42,39 @@ def setup(self):
4242
# Define VRP
4343
self.prob = VehicleRoutingProblem(self.G)
4444

45-
def test_cvrp_dive(self):
45+
def test_cvrp_dive_lp(self):
4646
self.prob.load_capacity = 15
4747
self.prob.solve(cspy=False, pricing_strategy="BestEdges1", dive=True)
48-
sol_lp = self.prob.best_value
48+
assert int(self.prob.best_value) == 6208
49+
50+
def test_cvrp_dive_cspy(self):
51+
self.prob.load_capacity = 15
4952
self.prob.solve(pricing_strategy="BestEdges1", dive=True)
50-
sol_cspy = self.prob.best_value
51-
assert int(sol_lp) == 6208
52-
assert int(sol_cspy) == 6208
53+
assert int(self.prob.best_value) == 6208
5354

54-
def test_vrptw_dive(self):
55+
def test_vrptw_dive_lp(self):
5556
self.prob.time_windows = True
5657
self.prob.solve(cspy=False, dive=True)
57-
sol_lp = self.prob.best_value
58-
self.prob.solve(dive=True)
59-
sol_cspy = self.prob.best_value
60-
assert int(sol_lp) == 6528
61-
assert int(sol_cspy) == 6528
58+
assert int(self.prob.best_value) == 6528
59+
60+
def test_vrptw_dive_cspy(self):
61+
self.prob.time_windows = True
62+
self.prob.solve(cspy=True, dive=True)
63+
assert int(self.prob.best_value) == 6528
6264

63-
def test_cvrpsdc_dive(self):
65+
def test_cvrpsdc_dive_lp(self):
6466
self.prob.load_capacity = 15
6567
self.prob.distribution_collection = True
6668
self.prob.solve(cspy=False, pricing_strategy="BestEdges1", dive=True)
67-
sol_lp = self.prob.best_value
69+
assert int(self.prob.best_value) == 6208
70+
71+
def test_cvrpsdc_dive_cspy(self):
72+
self.prob.load_capacity = 15
73+
self.prob.distribution_collection = True
6874
self.prob.solve(pricing_strategy="BestEdges1", dive=True)
69-
sol_cspy = self.prob.best_value
70-
assert int(sol_lp) == 6208
71-
assert int(sol_cspy) == 6208
75+
assert int(self.prob.best_value) == 6208
7276

73-
def test_pdp_dive(self):
77+
def test_pdp_dive_lp(self):
7478
# Set demands and requests
7579
for (u, v) in PICKUPS_DELIVERIES:
7680
self.G.nodes[u]["request"] = v
@@ -83,35 +87,39 @@ def test_pdp_dive(self):
8387
sol_lp = self.prob.best_value
8488
assert int(sol_lp) == 5980
8589

86-
def test_cvrp(self):
90+
def test_cvrp_lp(self):
8791
self.prob.load_capacity = 15
8892
self.prob.solve(cspy=False, pricing_strategy="BestEdges1")
89-
sol_lp = self.prob.best_value
93+
assert int(self.prob.best_value) == 6208
94+
95+
def test_cvrp_cspy(self):
96+
self.prob.load_capacity = 15
9097
self.prob.solve(pricing_strategy="BestEdges1")
91-
sol_cspy = self.prob.best_value
92-
assert int(sol_lp) == 6208
93-
assert int(sol_cspy) == 6208
98+
assert int(self.prob.best_value) == 6208
9499

95-
def test_vrptw(self):
100+
def test_vrptw_lp(self):
96101
self.prob.time_windows = True
97102
self.prob.solve(cspy=False)
98-
sol_lp = self.prob.best_value
103+
assert int(self.prob.best_value) == 6528
104+
105+
def test_vrptw_cspy(self):
106+
self.prob.time_windows = True
99107
self.prob.solve()
100-
sol_cspy = self.prob.best_value
101-
assert int(sol_lp) == 6528
102-
assert int(sol_cspy) == 6528
108+
assert int(self.prob.best_value) == 6528
103109

104-
def test_cvrpsdc(self):
110+
def test_cvrpsdc_lp(self):
105111
self.prob.load_capacity = 15
106112
self.prob.distribution_collection = True
107113
self.prob.solve(cspy=False, pricing_strategy="BestEdges1")
108-
sol_lp = self.prob.best_value
114+
assert int(self.prob.best_value) == 6208
115+
116+
def test_cvrpsdc_cspy(self):
117+
self.prob.load_capacity = 15
118+
self.prob.distribution_collection = True
109119
self.prob.solve(pricing_strategy="BestEdges1")
110-
sol_cspy = self.prob.best_value
111-
assert int(sol_lp) == 6208
112-
assert int(sol_cspy) == 6208
120+
assert int(self.prob.best_value) == 6208
113121

114-
def test_pdp(self):
122+
def test_pdp_lp(self):
115123
# Set demands and requests
116124
for (u, v) in PICKUPS_DELIVERIES:
117125
self.G.nodes[u]["request"] = v

benchmarks/tests/test_issue101.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from networkx import DiGraph, read_gpickle
2+
from vrpy import VehicleRoutingProblem
3+
4+
5+
class TestIssue101_large:
6+
def setup(self):
7+
G = read_gpickle("benchmarks/tests/graph_issue101")
8+
self.prob = VehicleRoutingProblem(G, load_capacity=80)
9+
self.prob.time_windows = True
10+
11+
# def test_lp(self):
12+
# self.prob.solve(cspy=False, solver="gurobi")
13+
# self.prob.check_arrival_time()
14+
# self.prob.check_departure_time()
15+
16+
def test_cspy(self):
17+
self.prob.solve(pricing_strategy="Exact")
18+
self.prob.check_arrival_time()
19+
self.prob.check_departure_time()

examples/pdp.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,18 @@
2121
if __name__ == "__main__":
2222

2323
prob = VehicleRoutingProblem(G, load_capacity=6, pickup_delivery=True, num_stops=6)
24-
prob.solve(cspy=False)
24+
prob.solve(cspy=False, pricing_strategy="Exact")
2525
print(prob.best_value)
2626
print(prob.best_routes)
27+
for (u, v) in PICKUPS_DELIVERIES:
28+
found = False
29+
for route in prob.best_routes.values():
30+
if u in route and v in route:
31+
found = True
32+
break
33+
if not found:
34+
print((u, v), "Not present")
35+
assert False
36+
2737
print(prob.node_load)
2838
assert prob.best_value == 5980

tests/pytest.ini

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
[pytest]
2+
log_cli = 1
3+
log_cli_level = INFO
4+
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
5+
log_cli_date_format=%Y-%m-%d %H:%M:%S
6+
27
python_files = test_*.py
38
#testpaths = tests/
49
filterwarnings =
5-
ignore::DeprecationWarning
10+
ignore::DeprecationWarning

tests/test_consistency.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ def test_consistency_parameters():
5353
G = DiGraph()
5454
G.add_edge("Source", "Sink", cost=1)
5555
prob = VehicleRoutingProblem(G, pickup_delivery=True)
56-
# pickup delivery requires cspy=False
57-
with pytest.raises(NotImplementedError):
58-
prob.solve()
5956
# pickup delivery expects at least one request
6057
with pytest.raises(KeyError):
6158
prob.solve(cspy=False, pricing_strategy="Exact")
@@ -88,9 +85,10 @@ def test_mixed_fleet_consistency():
8885
prob.solve()
8986
G.edges["Source", "Sink"]["cost"] = [1, 2]
9087
with pytest.raises(ValueError):
91-
prob = VehicleRoutingProblem(
92-
G, mixed_fleet=True, load_capacity=[2, 4], fixed_cost=[4]
93-
)
88+
prob = VehicleRoutingProblem(G,
89+
mixed_fleet=True,
90+
load_capacity=[2, 4],
91+
fixed_cost=[4])
9492
prob.solve()
9593

9694

0 commit comments

Comments
 (0)