nextroute 1.11.1.dev0__cp39-cp39-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nextroute/__about__.py +3 -0
- nextroute/__init__.py +18 -0
- nextroute/base_model.py +24 -0
- nextroute/bin/nextroute.exe +0 -0
- nextroute/check/__init__.py +28 -0
- nextroute/check/schema.py +145 -0
- nextroute/options.py +225 -0
- nextroute/schema/__init__.py +29 -0
- nextroute/schema/input.py +87 -0
- nextroute/schema/location.py +16 -0
- nextroute/schema/output.py +140 -0
- nextroute/schema/statistics.py +149 -0
- nextroute/schema/stop.py +65 -0
- nextroute/schema/vehicle.py +72 -0
- nextroute/solve.py +145 -0
- nextroute/version.py +12 -0
- nextroute-1.11.1.dev0.dist-info/LICENSE +87 -0
- nextroute-1.11.1.dev0.dist-info/METADATA +281 -0
- nextroute-1.11.1.dev0.dist-info/RECORD +26 -0
- nextroute-1.11.1.dev0.dist-info/WHEEL +5 -0
- nextroute-1.11.1.dev0.dist-info/top_level.txt +2 -0
- tests/schema/__init__.py +1 -0
- tests/schema/test_input.py +59 -0
- tests/schema/test_output.py +318 -0
- tests/solve_golden/__init__.py +1 -0
- tests/solve_golden/main.py +50 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Defines the output class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from nextroute.base_model import BaseModel
|
|
11
|
+
from nextroute.check.schema import Output as CheckOutput
|
|
12
|
+
from nextroute.schema.location import Location
|
|
13
|
+
from nextroute.schema.statistics import Statistics
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Version(BaseModel):
|
|
17
|
+
"""A version used for solving."""
|
|
18
|
+
|
|
19
|
+
sdk: str
|
|
20
|
+
"""Nextmv SDK."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class StopOutput(BaseModel):
|
|
24
|
+
"""Basic structure for the output of a stop."""
|
|
25
|
+
|
|
26
|
+
id: str
|
|
27
|
+
"""ID of the stop."""
|
|
28
|
+
location: Location
|
|
29
|
+
"""Location of the stop."""
|
|
30
|
+
|
|
31
|
+
custom_data: Optional[Any] = None
|
|
32
|
+
"""Custom data of the stop."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PlannedStopOutput(BaseModel):
|
|
36
|
+
"""Output of a stop planned in the solution."""
|
|
37
|
+
|
|
38
|
+
stop: StopOutput
|
|
39
|
+
"""Basic information on the stop."""
|
|
40
|
+
|
|
41
|
+
arrival_time: Optional[datetime] = None
|
|
42
|
+
"""Actual arrival time at this stop."""
|
|
43
|
+
cumulative_travel_distance: Optional[float] = None
|
|
44
|
+
"""Cumulative distance to travel from the first stop to this one, in meters."""
|
|
45
|
+
cumulative_travel_duration: Optional[float] = None
|
|
46
|
+
"""Cumulative duration to travel from the first stop to this one, in seconds."""
|
|
47
|
+
custom_data: Optional[Any] = None
|
|
48
|
+
"""Custom data of the stop."""
|
|
49
|
+
duration: Optional[float] = None
|
|
50
|
+
"""Duration of the service at the stop, in seconds."""
|
|
51
|
+
early_arrival_duration: Optional[float] = None
|
|
52
|
+
"""Duration of early arrival at the stop, in seconds."""
|
|
53
|
+
end_time: Optional[datetime] = None
|
|
54
|
+
"""End time of the service at the stop."""
|
|
55
|
+
late_arrival_duration: Optional[float] = None
|
|
56
|
+
"""Duration of late arrival at the stop, in seconds."""
|
|
57
|
+
mix_items: Optional[Any] = None
|
|
58
|
+
"""Mix items at the stop."""
|
|
59
|
+
start_time: Optional[datetime] = None
|
|
60
|
+
"""Start time of the service at the stop."""
|
|
61
|
+
target_arrival_time: Optional[datetime] = None
|
|
62
|
+
"""Target arrival time at this stop."""
|
|
63
|
+
travel_distance: Optional[float] = None
|
|
64
|
+
"""Distance to travel from the previous stop to this one, in meters."""
|
|
65
|
+
travel_duration: Optional[float] = None
|
|
66
|
+
"""Duration to travel from the previous stop to this one, in seconds."""
|
|
67
|
+
waiting_duration: Optional[float] = None
|
|
68
|
+
"""Waiting duratino at the stop, in seconds."""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class VehicleOutput(BaseModel):
|
|
72
|
+
"""Output of a vehicle in the solution."""
|
|
73
|
+
|
|
74
|
+
id: str
|
|
75
|
+
"""ID of the vehicle."""
|
|
76
|
+
|
|
77
|
+
alternate_stops: Optional[List[str]] = None
|
|
78
|
+
"""List of alternate stops that were planned on the vehicle."""
|
|
79
|
+
custom_data: Optional[Any] = None
|
|
80
|
+
"""Custom data of the vehicle."""
|
|
81
|
+
route: Optional[List[PlannedStopOutput]] = None
|
|
82
|
+
"""Route of the vehicle, which is a list of stops that were planned on
|
|
83
|
+
it."""
|
|
84
|
+
route_duration: Optional[float] = None
|
|
85
|
+
"""Total duration of the vehicle's route, in seconds."""
|
|
86
|
+
route_stops_duration: Optional[float] = None
|
|
87
|
+
"""Total duration of the stops of the vehicle, in seconds."""
|
|
88
|
+
route_travel_distance: Optional[float] = None
|
|
89
|
+
"""Total travel distance of the vehicle, in meters."""
|
|
90
|
+
route_travel_duration: Optional[float] = None
|
|
91
|
+
"""Total travel duration of the vehicle, in seconds."""
|
|
92
|
+
route_waiting_duration: Optional[float] = None
|
|
93
|
+
"""Total waiting duration of the vehicle, in seconds."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ObjectiveOutput(BaseModel):
|
|
97
|
+
"""Information of the objective (value function)."""
|
|
98
|
+
|
|
99
|
+
name: str
|
|
100
|
+
"""Name of the objective."""
|
|
101
|
+
|
|
102
|
+
base: Optional[float] = None
|
|
103
|
+
"""Base of the objective."""
|
|
104
|
+
custom_data: Optional[Any] = None
|
|
105
|
+
"""Custom data of the objective."""
|
|
106
|
+
factor: Optional[float] = None
|
|
107
|
+
"""Factor of the objective."""
|
|
108
|
+
objectives: Optional[List[Dict[str, Any]]] = None
|
|
109
|
+
"""List of objectives. Each list is actually of the same class
|
|
110
|
+
`ObjectiveOutput`, but we avoid a recursive definition here."""
|
|
111
|
+
value: Optional[float] = None
|
|
112
|
+
"""Value of the objective, which is equivalent to `self.base *
|
|
113
|
+
self.factor`."""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class Solution(BaseModel):
|
|
117
|
+
"""Solution to a Vehicle Routing Problem (VRP)."""
|
|
118
|
+
|
|
119
|
+
unplanned: Optional[List[StopOutput]] = None
|
|
120
|
+
"""List of stops that were not planned in the solution."""
|
|
121
|
+
vehicles: Optional[List[VehicleOutput]] = None
|
|
122
|
+
"""List of vehicles in the solution."""
|
|
123
|
+
objective: Optional[ObjectiveOutput] = None
|
|
124
|
+
"""Information of the objective (value function)."""
|
|
125
|
+
check: Optional[CheckOutput] = None
|
|
126
|
+
"""Check of the solution, if enabled."""
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class Output(BaseModel):
|
|
130
|
+
"""Output schema for Nextroute."""
|
|
131
|
+
|
|
132
|
+
options: Dict[str, Any]
|
|
133
|
+
"""Options used to obtain this output."""
|
|
134
|
+
version: Version
|
|
135
|
+
"""Versions used for the solution."""
|
|
136
|
+
|
|
137
|
+
solutions: Optional[List[Solution]] = None
|
|
138
|
+
"""Solutions to the problem."""
|
|
139
|
+
statistics: Optional[Statistics] = None
|
|
140
|
+
"""Statistics of the solution."""
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Schema for statistics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, List, Optional, Union
|
|
8
|
+
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from nextroute.base_model import BaseModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RunStatistics(BaseModel):
|
|
15
|
+
"""
|
|
16
|
+
Statistics about a general run.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
duration : float, optional
|
|
21
|
+
Duration of the run in seconds.
|
|
22
|
+
iterations : int, optional
|
|
23
|
+
Number of iterations.
|
|
24
|
+
custom : Union[Any, Dict[str, Any]], optional
|
|
25
|
+
Custom statistics created by the user. Can normally expect a `Dict[str,
|
|
26
|
+
Any]`.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
duration: Optional[float] = None
|
|
30
|
+
"""Duration of the run in seconds."""
|
|
31
|
+
iterations: Optional[int] = None
|
|
32
|
+
"""Number of iterations."""
|
|
33
|
+
custom: Optional[
|
|
34
|
+
Union[
|
|
35
|
+
Any,
|
|
36
|
+
Dict[str, Any],
|
|
37
|
+
]
|
|
38
|
+
] = None
|
|
39
|
+
"""Custom statistics created by the user. Can normally expect a `Dict[str,
|
|
40
|
+
Any]`."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ResultStatistics(BaseModel):
|
|
44
|
+
"""
|
|
45
|
+
Statistics about a specific result.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
duration : float, optional
|
|
50
|
+
Duration of the run in seconds.
|
|
51
|
+
value : float, optional
|
|
52
|
+
Value of the result.
|
|
53
|
+
custom : Union[Any, Dict[str, Any]], optional
|
|
54
|
+
Custom statistics created by the user. Can normally expect a `Dict[str,
|
|
55
|
+
Any]`.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
duration: Optional[float] = None
|
|
59
|
+
"""Duration of the run in seconds."""
|
|
60
|
+
value: Optional[float] = None
|
|
61
|
+
"""Value of the result."""
|
|
62
|
+
custom: Optional[
|
|
63
|
+
Union[
|
|
64
|
+
Any,
|
|
65
|
+
Dict[str, Any],
|
|
66
|
+
]
|
|
67
|
+
] = None
|
|
68
|
+
"""Custom statistics created by the user. Can normally expect a `Dict[str,
|
|
69
|
+
Any]`."""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class DataPoint(BaseModel):
|
|
73
|
+
"""
|
|
74
|
+
A data point.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
x : float
|
|
79
|
+
X coordinate of the data point.
|
|
80
|
+
y : float
|
|
81
|
+
Y coordinate of the data point.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
x: float
|
|
85
|
+
"""X coordinate of the data point."""
|
|
86
|
+
y: float
|
|
87
|
+
"""Y coordinate of the data point."""
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Series(BaseModel):
|
|
91
|
+
"""
|
|
92
|
+
A series of data points.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
name : str, optional
|
|
97
|
+
Name of the series.
|
|
98
|
+
data_points : List[DataPoint], optional
|
|
99
|
+
Data of the series.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
name: Optional[str] = None
|
|
103
|
+
"""Name of the series."""
|
|
104
|
+
data_points: Optional[List[DataPoint]] = None
|
|
105
|
+
"""Data of the series."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SeriesData(BaseModel):
|
|
109
|
+
"""
|
|
110
|
+
Data of a series.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
value : Series, optional
|
|
115
|
+
A series for the value of the solution.
|
|
116
|
+
custom : List[Series], optional
|
|
117
|
+
A list of series for custom statistics.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
value: Optional[Series] = None
|
|
121
|
+
"""A series for the value of the solution."""
|
|
122
|
+
custom: Optional[List[Series]] = None
|
|
123
|
+
"""A list of series for custom statistics."""
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class Statistics(BaseModel):
|
|
127
|
+
"""
|
|
128
|
+
Statistics of a solution.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
run : RunStatistics, optional
|
|
133
|
+
Statistics about the run.
|
|
134
|
+
result : ResultStatistics, optional
|
|
135
|
+
Statistics about the last result.
|
|
136
|
+
series_data : SeriesData, optional
|
|
137
|
+
Series data about some metric.
|
|
138
|
+
statistics_schema : str, optional
|
|
139
|
+
Schema (version). This class only supports `v1`.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
run: Optional[RunStatistics] = None
|
|
143
|
+
"""Statistics about the run."""
|
|
144
|
+
result: Optional[ResultStatistics] = None
|
|
145
|
+
"""Statistics about the last result."""
|
|
146
|
+
series_data: Optional[SeriesData] = None
|
|
147
|
+
"""Data of the series."""
|
|
148
|
+
statistics_schema: Optional[str] = Field(alias="schema", default="v1")
|
|
149
|
+
"""Schema (version). This class only supports `v1`."""
|
nextroute/schema/stop.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Defines the stop class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, List, Optional
|
|
9
|
+
|
|
10
|
+
from nextroute.base_model import BaseModel
|
|
11
|
+
from nextroute.schema.location import Location
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StopDefaults(BaseModel):
|
|
15
|
+
"""Default values for a stop."""
|
|
16
|
+
|
|
17
|
+
compatibility_attributes: Optional[List[str]] = None
|
|
18
|
+
"""Attributes that the stop is compatible with."""
|
|
19
|
+
duration: Optional[int] = None
|
|
20
|
+
"""Duration of the stop in seconds."""
|
|
21
|
+
early_arrival_time_penalty: Optional[float] = None
|
|
22
|
+
"""Penalty per second for arriving at the stop before the target arrival time."""
|
|
23
|
+
late_arrival_time_penalty: Optional[float] = None
|
|
24
|
+
"""Penalty per second for arriving at the stop after the target arrival time."""
|
|
25
|
+
max_wait: Optional[int] = None
|
|
26
|
+
"""Maximum waiting duration in seconds at the stop."""
|
|
27
|
+
quantity: Optional[Any] = None
|
|
28
|
+
"""Quantity of the stop."""
|
|
29
|
+
start_time_window: Optional[Any] = None
|
|
30
|
+
"""Time window in which the stop can start service."""
|
|
31
|
+
target_arrival_time: Optional[datetime] = None
|
|
32
|
+
"""Target arrival time at the stop."""
|
|
33
|
+
unplanned_penalty: Optional[int] = None
|
|
34
|
+
"""Penalty for not planning a stop."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Stop(StopDefaults):
|
|
38
|
+
"""Stop is a location that must be visited by a vehicle in a Vehicle
|
|
39
|
+
Routing Problem (VRP.)"""
|
|
40
|
+
|
|
41
|
+
id: str
|
|
42
|
+
"""Unique identifier for the stop."""
|
|
43
|
+
location: Location
|
|
44
|
+
"""Location of the stop."""
|
|
45
|
+
|
|
46
|
+
custom_data: Optional[Any] = None
|
|
47
|
+
"""Arbitrary data associated with the stop."""
|
|
48
|
+
mixing_items: Optional[Any] = None
|
|
49
|
+
"""Defines the items that are inserted or removed from the vehicle when visiting the stop."""
|
|
50
|
+
precedes: Optional[Any] = None
|
|
51
|
+
"""Stops that must be visited after this one on the same route."""
|
|
52
|
+
succeeds: Optional[Any] = None
|
|
53
|
+
"""Stops that must be visited before this one on the same route."""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AlternateStop(StopDefaults):
|
|
57
|
+
"""An alternate stop can be serviced instead of another stop."""
|
|
58
|
+
|
|
59
|
+
id: str
|
|
60
|
+
"""Unique identifier for the stop."""
|
|
61
|
+
location: Location
|
|
62
|
+
"""Location of the stop."""
|
|
63
|
+
|
|
64
|
+
custom_data: Optional[Any] = None
|
|
65
|
+
"""Arbitrary data associated with the stop."""
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Defines the vehicle class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, List, Optional
|
|
9
|
+
|
|
10
|
+
from nextroute.base_model import BaseModel
|
|
11
|
+
from nextroute.schema.location import Location
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InitialStop(BaseModel):
|
|
15
|
+
"""Represents a stop that is already planned on a vehicle."""
|
|
16
|
+
|
|
17
|
+
id: str
|
|
18
|
+
"""Unique identifier of the stop."""
|
|
19
|
+
|
|
20
|
+
fixed: Optional[bool] = None
|
|
21
|
+
"""Whether the stop is fixed on the route."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class VehicleDefaults(BaseModel):
|
|
25
|
+
"""Default values for vehicles."""
|
|
26
|
+
|
|
27
|
+
activation_penalty: Optional[int] = None
|
|
28
|
+
"""Penalty of using the vehicle."""
|
|
29
|
+
alternate_stops: Optional[List[str]] = None
|
|
30
|
+
"""A set of alternate stops for which only one should be serviced."""
|
|
31
|
+
capacity: Optional[Any] = None
|
|
32
|
+
"""Capacity of the vehicle."""
|
|
33
|
+
compatibility_attributes: Optional[List[str]] = None
|
|
34
|
+
"""Attributes that the vehicle is compatible with."""
|
|
35
|
+
end_location: Optional[Location] = None
|
|
36
|
+
"""Location where the vehicle ends."""
|
|
37
|
+
end_time: Optional[datetime] = None
|
|
38
|
+
"""Latest time at which the vehicle ends its route."""
|
|
39
|
+
max_distance: Optional[int] = None
|
|
40
|
+
"""Maximum distance in meters that the vehicle can travel."""
|
|
41
|
+
max_duration: Optional[int] = None
|
|
42
|
+
"""Maximum duration in seconds that the vehicle can travel."""
|
|
43
|
+
max_stops: Optional[int] = None
|
|
44
|
+
"""Maximum number of stops that the vehicle can visit."""
|
|
45
|
+
max_wait: Optional[int] = None
|
|
46
|
+
"""Maximum aggregated waiting time that the vehicle can wait across route stops."""
|
|
47
|
+
min_stops: Optional[int] = None
|
|
48
|
+
"""Minimum stops that a vehicle should visit."""
|
|
49
|
+
min_stops_penalty: Optional[float] = None
|
|
50
|
+
"""Penalty for not visiting the minimum number of stops."""
|
|
51
|
+
speed: Optional[float] = None
|
|
52
|
+
"""Speed of the vehicle in meters per second."""
|
|
53
|
+
start_level: Optional[Any] = None
|
|
54
|
+
"""Initial level of the vehicle."""
|
|
55
|
+
start_location: Optional[Location] = None
|
|
56
|
+
"""Location where the vehicle starts."""
|
|
57
|
+
start_time: Optional[datetime] = None
|
|
58
|
+
"""Time when the vehicle starts its route."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Vehicle(VehicleDefaults):
|
|
62
|
+
"""A vehicle services stops in a Vehicle Routing Problem (VRP)."""
|
|
63
|
+
|
|
64
|
+
id: str
|
|
65
|
+
"""Unique identifier of the vehicle."""
|
|
66
|
+
|
|
67
|
+
custom_data: Optional[Any] = None
|
|
68
|
+
"""Arbitrary custom data."""
|
|
69
|
+
initial_stops: Optional[List[InitialStop]] = None
|
|
70
|
+
"""Initial stops planned on the vehicle."""
|
|
71
|
+
stop_duration_multiplier: Optional[float] = None
|
|
72
|
+
"""Multiplier for the duration of stops."""
|
nextroute/solve.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Methods for solving a Vehicle Routing Problem with Nextroute.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import subprocess
|
|
11
|
+
from typing import Any, Dict, Union
|
|
12
|
+
|
|
13
|
+
from nextroute.options import Options
|
|
14
|
+
from nextroute.schema.input import Input
|
|
15
|
+
from nextroute.schema.output import Output
|
|
16
|
+
|
|
17
|
+
SUPPORTED_OS = ["linux", "windows", "darwin"]
|
|
18
|
+
"""The operating systems supported by the Nextroute engine."""
|
|
19
|
+
SUPPORTED_ARCHITECTURES = ["amd64", "x86_64", "arm64", "aarch64"]
|
|
20
|
+
"""The architectures supported by the Nextroute engine."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def solve(
|
|
24
|
+
input: Union[Input, Dict[str, Any]],
|
|
25
|
+
options: Union[Options, Dict[str, Any]],
|
|
26
|
+
) -> Output:
|
|
27
|
+
"""
|
|
28
|
+
Solve a Vehicle Routing Problem (VRP) using the Nextroute engine. The input
|
|
29
|
+
and options are passed to the engine, and the output is returned. The input
|
|
30
|
+
and options can be provided as dictionaries or as objects, although the
|
|
31
|
+
recommended way is to use the classes, as they provide validation.
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
--------
|
|
35
|
+
|
|
36
|
+
* Using default options to load an input from a file.
|
|
37
|
+
```python
|
|
38
|
+
import json
|
|
39
|
+
|
|
40
|
+
import nextroute
|
|
41
|
+
|
|
42
|
+
with open("input.json") as f:
|
|
43
|
+
data = json.load(f)
|
|
44
|
+
|
|
45
|
+
input = nextroute.schema.Input.from_dict(data)
|
|
46
|
+
options = nextroute.Options()
|
|
47
|
+
output = nextroute.solve(input, options)
|
|
48
|
+
print(output)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
* Using custom options to load an input from a file.
|
|
52
|
+
```python
|
|
53
|
+
import json
|
|
54
|
+
|
|
55
|
+
import nextroute
|
|
56
|
+
|
|
57
|
+
with open("input.json") as f:
|
|
58
|
+
data = json.load(f)
|
|
59
|
+
|
|
60
|
+
input = nextroute.schema.Input.from_dict(data)
|
|
61
|
+
options = nextroute.Options(
|
|
62
|
+
solve=nextroute.ParallelSolveOptions(duration=2),
|
|
63
|
+
)
|
|
64
|
+
output = nextroute.solve(input, options)
|
|
65
|
+
print(output)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
* Using custom dict options to load an input from a file.
|
|
69
|
+
```python
|
|
70
|
+
import json
|
|
71
|
+
|
|
72
|
+
import nextroute
|
|
73
|
+
|
|
74
|
+
with open("input.json") as f:
|
|
75
|
+
data = json.load(f)
|
|
76
|
+
|
|
77
|
+
input = nextroute.schema.Input.from_dict(data)
|
|
78
|
+
options = {
|
|
79
|
+
"solve": {
|
|
80
|
+
"duration": 2,
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
output = nextroute.solve(input, options)
|
|
84
|
+
print(output)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
input : Union[schema.Input, Dict[str, Any]]
|
|
91
|
+
The input to the Nextroute engine. If a dictionary is provided, it will
|
|
92
|
+
be converted to an Input object to validate it.
|
|
93
|
+
options : Union[Options, Dict[str, Any]]
|
|
94
|
+
The options for the Nextroute engine. If a dictionary is provided, it
|
|
95
|
+
will be converted to an Options object.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
schema.Output
|
|
100
|
+
The output of the Nextroute engine. You can call the `to_dict` method
|
|
101
|
+
on this object to get a dictionary representation of the output.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
if isinstance(input, dict):
|
|
105
|
+
input = Input.from_dict(input)
|
|
106
|
+
|
|
107
|
+
input_stream = json.dumps(input.to_dict())
|
|
108
|
+
|
|
109
|
+
if isinstance(options, dict):
|
|
110
|
+
options = Options.from_dict(options)
|
|
111
|
+
|
|
112
|
+
os_name = platform.system().lower()
|
|
113
|
+
if os_name not in SUPPORTED_OS:
|
|
114
|
+
raise Exception(f'unsupported operating system: "{os_name}", supported os are: {", ".join(SUPPORTED_OS)}')
|
|
115
|
+
|
|
116
|
+
architecture = platform.machine().lower()
|
|
117
|
+
if architecture not in SUPPORTED_ARCHITECTURES:
|
|
118
|
+
raise Exception(
|
|
119
|
+
f'unsupported architecture: "{architecture}", supported arch are: {", ".join(SUPPORTED_ARCHITECTURES)}'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
executable = os.path.join(os.path.dirname(__file__), "bin", "nextroute.exe")
|
|
123
|
+
if not os.path.exists(executable):
|
|
124
|
+
raise Exception(f"missing Nextroute binary: {executable}")
|
|
125
|
+
|
|
126
|
+
option_args = options.to_args()
|
|
127
|
+
args = [executable] + option_args
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
result = subprocess.run(
|
|
131
|
+
args,
|
|
132
|
+
env=os.environ,
|
|
133
|
+
check=True,
|
|
134
|
+
text=True,
|
|
135
|
+
capture_output=True,
|
|
136
|
+
input=input_stream,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
except subprocess.CalledProcessError as e:
|
|
140
|
+
raise Exception(f"error running Nextroute binary: {e.stderr}") from e
|
|
141
|
+
|
|
142
|
+
raw_output = result.stdout
|
|
143
|
+
output = Output.from_dict(json.loads(raw_output))
|
|
144
|
+
|
|
145
|
+
return output
|
nextroute/version.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# © 2019-present nextmv.io inc
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def nextroute_version() -> str:
|
|
8
|
+
"""
|
|
9
|
+
Get the version of the embedded Nextroute binary.
|
|
10
|
+
"""
|
|
11
|
+
executable = os.path.join(os.path.dirname(__file__), "bin", "nextroute.exe")
|
|
12
|
+
return subprocess.check_output([executable, "--version"]).decode().strip()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# LICENSE
|
|
2
|
+
|
|
3
|
+
Business Source License 1.1
|
|
4
|
+
|
|
5
|
+
Parameters
|
|
6
|
+
|
|
7
|
+
Licensor: nextmv.io inc
|
|
8
|
+
Licensed Work: nextroute
|
|
9
|
+
|
|
10
|
+
Change Date: Four years from the date the Licensed Work is published.
|
|
11
|
+
Change License: GPLv3
|
|
12
|
+
|
|
13
|
+
For information about alternative licensing arrangements for the Software,
|
|
14
|
+
please email info@nextmv.io.
|
|
15
|
+
|
|
16
|
+
Notice
|
|
17
|
+
|
|
18
|
+
The Business Source License (this document, or the “License”) is not an Open
|
|
19
|
+
Source license. However, the Licensed Work will eventually be made available
|
|
20
|
+
under an Open Source License, as stated in this License.
|
|
21
|
+
|
|
22
|
+
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source
|
|
23
|
+
License” is a trademark of MariaDB plc.
|
|
24
|
+
|
|
25
|
+
-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
## Terms
|
|
28
|
+
|
|
29
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
30
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
31
|
+
Licensor may make an Additional Use Grant, above, permitting limited production
|
|
32
|
+
use.
|
|
33
|
+
|
|
34
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
35
|
+
available distribution of a specific version of the Licensed Work under this
|
|
36
|
+
License, whichever comes first, the Licensor hereby grants you rights under the
|
|
37
|
+
terms of the Change License, and the rights granted in the paragraph above
|
|
38
|
+
terminate.
|
|
39
|
+
|
|
40
|
+
If your use of the Licensed Work does not comply with the requirements currently
|
|
41
|
+
in effect as described in this License, you must purchase a commercial license
|
|
42
|
+
from the Licensor, its affiliated entities, or authorized resellers, or you must
|
|
43
|
+
refrain from using the Licensed Work.
|
|
44
|
+
|
|
45
|
+
All copies of the original and modified Licensed Work, and derivative works of
|
|
46
|
+
the Licensed Work, are subject to this License. This License applies separately
|
|
47
|
+
for each version of the Licensed Work and the Change Date may vary for each
|
|
48
|
+
version of the Licensed Work released by Licensor.
|
|
49
|
+
|
|
50
|
+
You must conspicuously display this License on each original or modified copy of
|
|
51
|
+
the Licensed Work. If you receive the Licensed Work in original or modified form
|
|
52
|
+
from a third party, the terms and conditions set forth in this License apply to
|
|
53
|
+
your use of that work.
|
|
54
|
+
|
|
55
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
56
|
+
terminate your rights under this License for the current and all other versions
|
|
57
|
+
of the Licensed Work.
|
|
58
|
+
|
|
59
|
+
This License does not grant you any right in any trademark or logo of Licensor
|
|
60
|
+
or its affiliates (provided that you may use a trademark or logo of Licensor as
|
|
61
|
+
expressly required by this License).TO THE EXTENT PERMITTED BY APPLICABLE LAW,
|
|
62
|
+
THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL
|
|
63
|
+
WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
|
|
64
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
65
|
+
NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this
|
|
66
|
+
License’s text to license your works, and to refer to it using the trademark
|
|
67
|
+
“Business Source License”, as long as you comply with the Covenants of Licensor
|
|
68
|
+
below.
|
|
69
|
+
|
|
70
|
+
## Covenants of Licensor
|
|
71
|
+
|
|
72
|
+
In consideration of the right to use this License’s text and the “Business
|
|
73
|
+
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
|
74
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
75
|
+
|
|
76
|
+
To specify as the Change License the GPL Version 2.0 or any later version, or a
|
|
77
|
+
license that is compatible with GPL Version 2.0 or a later version, where
|
|
78
|
+
“compatible” means that software provided under the Change License can be
|
|
79
|
+
included in a program with software provided under GPL Version 2.0 or a later
|
|
80
|
+
version. Licensor may specify additional Change Licenses without limitation. To
|
|
81
|
+
either: (a) specify an additional grant of rights to use that does not impose
|
|
82
|
+
any additional restriction on the right granted in this License, as the
|
|
83
|
+
Additional Use Grant; or (b) insert the text “None” to specify a Change Date.
|
|
84
|
+
Not to modify this License in any other way.
|
|
85
|
+
|
|
86
|
+
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source
|
|
87
|
+
License” is a trademark of MariaDB plc.
|