nextroute 1.12.0__cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.

Potentially problematic release.


This version of nextroute might be problematic. Click here for more details.

@@ -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`."""
@@ -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()