nextroute 1.8.0.dev24__cp312-cp312-macosx_13_0_x86_64.whl → 1.12.3__cp312-cp312-macosx_13_0_x86_64.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  # © 2019-present nextmv.io inc
2
2
 
3
- __version__ = "v1.8.0.dev24"
3
+ __version__ = "v1.12.3"
nextroute/__init__.py CHANGED
@@ -9,10 +9,8 @@ to it.
9
9
  """
10
10
 
11
11
  from .__about__ import __version__
12
- from .options import DisableFormatOptions as DisableFormatOptions
13
- from .options import FormatOptions as FormatOptions
14
12
  from .options import Options as Options
15
- from .options import ParallelSolveOptions as ParallelSolveOptions
13
+ from .options import Verbosity as Verbosity
16
14
  from .solve import solve as solve
17
15
  from .version import nextroute_version as nextroute_version
18
16
 
Binary file
@@ -19,8 +19,6 @@ solution. If the check is invoked on a solution, it is executed on the
19
19
  unplanned plan units of the solution.
20
20
  """
21
21
 
22
- from .options import Options as Options
23
- from .options import Verbosity as Verbosity
24
22
  from .schema import Objective as Objective
25
23
  from .schema import ObjectiveTerm as ObjectiveTerm
26
24
  from .schema import Output as Output
nextroute/options.py CHANGED
@@ -5,69 +5,181 @@ Options for working with the Nextroute engine.
5
5
  """
6
6
 
7
7
  import json
8
+ from enum import Enum
8
9
  from typing import Any, Dict, List
9
10
 
10
11
  from pydantic import Field
11
12
 
12
13
  from nextroute.base_model import BaseModel
13
- from nextroute.check.options import Options as CheckOptions
14
- from nextroute.factory.options import Options as FactoryOptions
15
14
 
15
+ # Arguments that require a duration suffix.
16
16
  _DURATIONS_ARGS = [
17
17
  "-check.duration",
18
18
  "-solve.duration",
19
+ "-solve.plateau.duration",
20
+ "-solve.plateau.delay",
19
21
  ]
20
22
 
23
+ # Arguments that require a string enum.
24
+ _STR_ENUM_ARGS = [
25
+ "CHECK_VERBOSITY",
26
+ ]
27
+
28
+
29
+ class Verbosity(str, Enum):
30
+ """Format of an `Input`."""
21
31
 
22
- class ParallelSolveOptions(BaseModel):
23
- """Options for the parallel solver."""
32
+ OFF = "off"
33
+ """The check engine is not run."""
34
+ LOW = "low"
35
+ """Low verbosity for the check engine."""
36
+ MEDIUM = "medium"
37
+ """Medium verbosity for the check engine."""
38
+ HIGH = "high"
39
+ """High verbosity for the check engine."""
24
40
 
25
- iterations: int = -1
41
+
42
+ class Options(BaseModel):
43
+ """Options for using Nextroute."""
44
+
45
+ CHECK_DURATION: float = 30
46
+ """Maximum duration of the check, in seconds."""
47
+ CHECK_VERBOSITY: Verbosity = Verbosity.OFF
48
+ """Verbosity of the check engine."""
49
+ FORMAT_DISABLE_PROGRESSION: bool = False
50
+ """Whether to disable the progression series."""
51
+ MODEL_CONSTRAINTS_DISABLE_ATTRIBUTES: bool = False
52
+ """Ignore the compatibility attributes constraint."""
53
+ MODEL_CONSTRAINTS_DISABLE_CAPACITIES: List[str] = Field(default_factory=list)
54
+ """Ignore the capacity constraint for the given resource names."""
55
+ MODEL_CONSTRAINTS_DISABLE_CAPACITY: bool = False
56
+ """Ignore the capacity constraint for all resources."""
57
+ MODEL_CONSTRAINTS_DISABLE_DISTANCELIMIT: bool = False
58
+ """Ignore the distance limit constraint."""
59
+ MODEL_CONSTRAINTS_DISABLE_GROUPS: bool = False
60
+ """Ignore the groups constraint."""
61
+ MODEL_CONSTRAINTS_DISABLE_MAXIMUMDURATION: bool = False
62
+ """Ignore the maximum duration constraint."""
63
+ MODEL_CONSTRAINTS_DISABLE_MAXIMUMSTOPS: bool = False
64
+ """Ignore the maximum stops constraint."""
65
+ MODEL_CONSTRAINTS_DISABLE_MAXIMUMWAITSTOP: bool = False
66
+ """Ignore the maximum stop wait constraint."""
67
+ MODEL_CONSTRAINTS_DISABLE_MAXIMUMWAITVEHICLE: bool = False
68
+ """Ignore the maximum vehicle wait constraint."""
69
+ MODEL_CONSTRAINTS_DISABLE_MIXINGITEMS: bool = False
70
+ """Ignore the do not mix items constraint."""
71
+ MODEL_CONSTRAINTS_DISABLE_PRECEDENCE: bool = False
72
+ """Ignore the precedence (pickups & deliveries) constraint."""
73
+ MODEL_CONSTRAINTS_DISABLE_STARTTIMEWINDOWS: bool = False
74
+ """Ignore the start time windows constraint."""
75
+ MODEL_CONSTRAINTS_DISABLE_VEHICLEENDTIME: bool = False
76
+ """Ignore the vehicle end time constraint."""
77
+ MODEL_CONSTRAINTS_DISABLE_VEHICLESTARTTIME: bool = False
78
+ """Ignore the vehicle start time constraint."""
79
+ MODEL_CONSTRAINTS_ENABLE_CLUSTER: bool = False
80
+ """Enable the cluster constraint."""
81
+ MODEL_OBJECTIVES_CAPACITIES: str = ""
82
+ """
83
+ Capacity objective, provide triple for each resource
84
+ `name=default;factor=1.0;offset=0.0`.
85
+ """
86
+ MODEL_OBJECTIVES_CLUSTER: float = 0.0
87
+ """Factor to weigh the cluster objective."""
88
+ MODEL_OBJECTIVES_DISTANCE: float = 0.0
89
+ """Factor to weigh the distance objective."""
90
+ MODEL_OBJECTIVES_EARLYARRIVALPENALTY: float = 1.0
91
+ """Factor to weigh the early arrival objective."""
92
+ MODEL_OBJECTIVES_LATEARRIVALPENALTY: float = 1.0
93
+ """Factor to weigh the late arrival objective."""
94
+ MODEL_OBJECTIVES_MINSTOPS: float = 1.0
95
+ """Factor to weigh the min stops objective."""
96
+ MODEL_OBJECTIVES_STOPBALANCE: float = 0.0
97
+ """Factor to weigh the stop balance objective."""
98
+ MODEL_OBJECTIVES_TRAVELDURATION: float = 0.0
99
+ """Factor to weigh the travel duration objective."""
100
+ MODEL_OBJECTIVES_UNPLANNEDPENALTY: float = 1.0
101
+ """Factor to weigh the unplanned objective."""
102
+ MODEL_OBJECTIVES_VEHICLEACTIVATIONPENALTY: float = 1.0
103
+ """Factor to weigh the vehicle activation objective."""
104
+ MODEL_OBJECTIVES_VEHICLESDURATION: float = 1.0
105
+ """Factor to weigh the vehicles duration objective."""
106
+ MODEL_PROPERTIES_DISABLE_DURATIONGROUPS: bool = False
107
+ """Ignore the durations groups of stops."""
108
+ MODEL_PROPERTIES_DISABLE_DURATIONS: bool = False
109
+ """Ignore the durations of stops."""
110
+ MODEL_PROPERTIES_DISABLE_INITIALSOLUTION: bool = False
111
+ """Ignore the initial solution."""
112
+ MODEL_PROPERTIES_DISABLE_STOPDURATIONMULTIPLIERS: bool = False
113
+ """Ignore the stop duration multipliers defined on vehicles."""
114
+ MODEL_PROPERTIES_MAXIMUMTIMEHORIZON: int = 15552000
115
+ """Maximum time horizon for the model in seconds."""
116
+ MODEL_VALIDATE_DISABLE_RESOURCES: bool = False
117
+ """Disable the resources validation."""
118
+ MODEL_VALIDATE_DISABLE_STARTTIME: bool = False
119
+ """Disable the start time validation."""
120
+ MODEL_VALIDATE_ENABLE_MATRIX: bool = False
121
+ """Enable matrix validation."""
122
+ MODEL_VALIDATE_ENABLE_MATRIXASYMMETRYTOLERANCE: int = 20
123
+ """Percentage of acceptable matrix asymmetry, requires matrix validation enabled."""
124
+ SOLVE_DURATION: float = 5
125
+ """Maximum duration, in seconds, of the solver."""
126
+ SOLVE_ITERATIONS: int = -1
26
127
  """
27
128
  Maximum number of iterations, -1 assumes no limit; iterations are counted
28
129
  after start solutions are generated.
29
130
  """
30
- duration: float = 5
31
- """Maximum duration, in seconds, of the solver."""
32
- parallel_runs: int = -1
131
+ SOLVE_PARALLELRUNS: int = -1
33
132
  """
34
133
  Maximum number of parallel runs, -1 results in using all available
35
134
  resources.
36
135
  """
37
- start_solutions: int = -1
136
+ SOLVE_PLATEAU_DELAY: float = 0.0
137
+ """Delay before starting to monitor for a plateau."""
138
+ SOLVE_PLATEAU_ABSOLUTETHRESHOLD: float = -1
139
+ """Absolute threshold for significant improvement."""
140
+ SOLVE_PLATEAU_DURATION: float = 0.0
141
+ """Maximum duration, in seconds, without (significant) improvement."""
142
+ SOLVE_PLATEAU_ITERATIONS: int = 0
143
+ """Maximum number of iterations without (significant) improvement."""
144
+ SOLVE_PLATEAU_RELATIVETHRESHOLD: float = 0.0
145
+ """Relative threshold for significant improvement."""
146
+ SOLVE_RUNDETERMINISTICALLY: bool = False
147
+ """Run the parallel solver deterministically."""
148
+ SOLVE_SOLVER_PLANGROUPSIZE_DELTA: int = 0
149
+ """Delta for the plan group size parameter."""
150
+ SOLVE_SOLVER_PLANGROUPSIZE_DELTAAFTERITERATIONS: int = 1000000000
151
+ """Delta after each iteration for the plan group size parameter."""
152
+ SOLVE_SOLVER_PLANGROUPSIZE_MAXVALUE: int = 2
153
+ """Maximum value for the plan group size parameter."""
154
+ SOLVE_SOLVER_PLANGROUPSIZE_MINVALUE: int = 2
155
+ """Minimum value for the plan group size parameter."""
156
+ SOLVE_SOLVER_PLANGROUPSIZE_SNAPBACKAFTERIMPROVEMENT: bool = True
157
+ """Snap back to start value after improvement of best solution for the plan group size parameter."""
158
+ SOLVE_SOLVER_PLANGROUPSIZE_STARTVALUE: int = 2
159
+ """Start value for the plan group size parameter."""
160
+ SOLVE_SOLVER_PLANGROUPSIZE_ZIGZAG: bool = True
161
+ """Zigzag between min and max value like a jig saw for the plan group size parameter."""
162
+ SOLVE_SOLVER_UNPLANUNITS_DELTA: int = 2
163
+ """Delta for the unplan units parameter."""
164
+ SOLVE_SOLVER_UNPLANUNITS_DELTAAFTERITERATIONS: int = 125
165
+ """Delta after each iteration for the unplan units parameter."""
166
+ SOLVE_SOLVER_UNPLANUNITS_MAXVALUE: int = -1
167
+ """Maximum value for the unplan units parameter."""
168
+ SOLVE_SOLVER_UNPLANUNITS_MINVALUE: int = 2
169
+ """Minimum value for the unplan units parameter."""
170
+ SOLVE_SOLVER_UNPLANUNITS_SNAPBACKAFTERIMPROVEMENT: bool = True
171
+ """Snap back to start value after improvement of best solution for the unplan units parameter."""
172
+ SOLVE_SOLVER_UNPLANUNITS_STARTVALUE: int = 2
173
+ """Start value for the unplan units parameter."""
174
+ SOLVE_SOLVER_UNPLANUNITS_ZIGZAG: bool = True
175
+ """Zigzag between min and max value like a jig saw for the unplan units parameter."""
176
+ SOLVE_SOLVER_UNPLANWEIGHTS: str = "Vehicle:3,Island:1,Location:293"
177
+ """Unplan heuristic weights parameter."""
178
+ SOLVE_STARTSOLUTIONS: int = -1
38
179
  """
39
180
  Number of solutions to generate on top of those passed in; one solution
40
181
  generated with sweep algorithm, the rest generated randomly.
41
182
  """
42
- run_deterministically: bool = False
43
- """Run the parallel solver deterministically."""
44
-
45
-
46
- class DisableFormatOptions(BaseModel):
47
- """Options for disabling/enabling the progression series."""
48
-
49
- progression: bool = False
50
- """Whether to disable the progression series."""
51
-
52
-
53
- class FormatOptions(BaseModel):
54
- """Options for formatting the output of the solver."""
55
-
56
- disable: DisableFormatOptions = Field(default_factory=DisableFormatOptions)
57
- """Options for disabling/enabling the progression series."""
58
-
59
-
60
- class Options(BaseModel):
61
- """Options for using Nextroute."""
62
-
63
- check: CheckOptions = Field(default_factory=CheckOptions)
64
- """Options for enabling the check engine."""
65
- format: FormatOptions = Field(default_factory=FormatOptions)
66
- """Options for the output format."""
67
- model: FactoryOptions = Field(default_factory=FactoryOptions)
68
- """Options for the ready-to-go model."""
69
- solve: ParallelSolveOptions = Field(default_factory=ParallelSolveOptions)
70
- """Options for the parallel solver."""
71
183
 
72
184
  def to_args(self) -> List[str]:
73
185
  """
@@ -80,20 +192,18 @@ class Options(BaseModel):
80
192
  """
81
193
 
82
194
  opt_dict = self.to_dict()
83
- flattened = _flatten(opt_dict)
84
195
 
85
196
  default_options = Options()
86
197
  default_options_dict = default_options.to_dict()
87
- default_flattened = _flatten(default_options_dict)
88
198
 
89
199
  args = []
90
- for key, value in flattened.items():
200
+ for key, value in opt_dict.items():
91
201
  # We only care about custom options, so we skip the default ones.
92
- default_value = default_flattened.get(key)
202
+ default_value = default_options_dict.get(key)
93
203
  if value == default_value:
94
204
  continue
95
205
 
96
- key = key.replace("_", "")
206
+ key = f"-{key.replace('_', '.').lower()}"
97
207
 
98
208
  str_value = json.dumps(value)
99
209
  if key in _DURATIONS_ARGS:
@@ -118,34 +228,33 @@ class Options(BaseModel):
118
228
 
119
229
  return args
120
230
 
231
+ @classmethod
232
+ def extract_from_dict(cls, data: Dict[str, Any]) -> "Options":
233
+ """
234
+ Extracts options from a dictionary. This dictionary may contain more
235
+ keys that are not part of the Nextroute options.
121
236
 
122
- def _flatten(nested: Dict[str, Any]) -> Dict[str, Any]:
123
- """Flatten a nested dict."""
124
-
125
- flattened = {}
126
- for child_key, child_value in nested.items():
127
- root_key = f"-{child_key}"
128
- __set_children(flattened, root_key, child_value)
129
-
130
- return flattened
131
-
237
+ Parameters
238
+ ----------
239
+ data : Dict[str, Any]
240
+ The dictionary to extract options from.
132
241
 
133
- def __set_children(flattened: Dict[str, Any], parent_key: str, parent_value: Any):
134
- """Helper function for `__flatten`. it is invoked recursively on a child
135
- value. If the child is not a dict, then the value is simply set on the
136
- flattened dict. If the child is a dict, then the function is invoked
137
- recursively on the child’s values, unitl a non-dict values is hit."""
242
+ Returns
243
+ ----------
244
+ Options
245
+ The Nextroute options.
246
+ """
138
247
 
139
- new_key = parent_key
248
+ options = cls()
249
+ for key, value in data.items():
250
+ key = key.upper()
251
+ if not hasattr(options, key):
252
+ continue
140
253
 
141
- if parent_value is None:
142
- flattened[new_key] = parent_value
143
- return
254
+ # Enums need to be handled manually.
255
+ if key == "CHECK_VERBOSITY":
256
+ value = Verbosity(value)
144
257
 
145
- if isinstance(parent_value, dict):
146
- for child_key, child_value in parent_value.items():
147
- new_key = f"{parent_key}.{child_key}"
148
- __set_children(flattened, new_key, child_value)
149
- return
258
+ setattr(options, key, value)
150
259
 
151
- flattened[new_key] = parent_value
260
+ return options
nextroute/schema/input.py CHANGED
@@ -4,7 +4,8 @@
4
4
  Defines the input class.
5
5
  """
6
6
 
7
- from typing import Any, List, Optional
7
+ from datetime import datetime
8
+ from typing import Any, List, Optional, Union
8
9
 
9
10
  from nextroute.base_model import BaseModel
10
11
  from nextroute.schema.stop import AlternateStop, Stop, StopDefaults
@@ -30,6 +31,30 @@ class DurationGroup(BaseModel):
30
31
  """Stop IDs contained in the group."""
31
32
 
32
33
 
34
+ class MatrixTimeFrame(BaseModel):
35
+ """Represents a time-dependent duration matrix or scaling factor."""
36
+
37
+ start_time: datetime
38
+ """Start time of the time frame."""
39
+ end_time: datetime
40
+ """End time of the time frame."""
41
+ matrix: Optional[List[List[float]]] = None
42
+ """Duration matrix for the time frame."""
43
+ scaling_factor: Optional[float] = None
44
+ """Scaling factor for the time frame."""
45
+
46
+
47
+ class TimeDependentMatrix(BaseModel):
48
+ """Represents time-dependent duration matrices."""
49
+
50
+ vehicle_ids: Optional[List[str]] = None
51
+ """Vehicle IDs for which the duration matrix is defined."""
52
+ default_matrix: List[List[float]]
53
+ """Default duration matrix."""
54
+ matrix_time_frames: Optional[List[MatrixTimeFrame]] = None
55
+ """Time-dependent duration matrices."""
56
+
57
+
33
58
  class Input(BaseModel):
34
59
  """Input schema for Nextroute."""
35
60
 
@@ -46,10 +71,16 @@ class Input(BaseModel):
46
71
  """Default values for vehicles and stops."""
47
72
  distance_matrix: Optional[List[List[float]]] = None
48
73
  """Matrix of travel distances in meters between stops."""
49
- duratrion_groups: Optional[List[DurationGroup]] = None
74
+ duration_groups: Optional[List[DurationGroup]] = None
50
75
  """Duration in seconds added when approaching the group."""
51
- duration_matrix: Optional[List[List[float]]] = None
52
- """Matrix of travel durations in seconds between stops."""
76
+ duration_matrix: Optional[
77
+ Union[
78
+ List[List[float]],
79
+ TimeDependentMatrix,
80
+ List[TimeDependentMatrix],
81
+ ]
82
+ ] = None
83
+ """Matrix of travel durations in seconds between stops as a single matrix or duration matrices."""
53
84
  options: Optional[Any] = None
54
85
  """Arbitrary options."""
55
86
  stop_groups: Optional[List[List[str]]] = None
nextroute/solve.py CHANGED
@@ -6,6 +6,7 @@ Methods for solving a Vehicle Routing Problem with Nextroute.
6
6
 
7
7
  import json
8
8
  import os
9
+ import platform
9
10
  import subprocess
10
11
  from typing import Any, Dict, Union
11
12
 
@@ -15,15 +16,9 @@ from nextroute.schema.output import Output
15
16
 
16
17
  SUPPORTED_OS = ["linux", "windows", "darwin"]
17
18
  """The operating systems supported by the Nextroute engine."""
18
- SUPPORTED_ARCHITECTURES = ["x86_64", "arm64", "aarch64"]
19
+ SUPPORTED_ARCHITECTURES = ["amd64", "x86_64", "arm64", "aarch64"]
19
20
  """The architectures supported by the Nextroute engine."""
20
21
 
21
- _ARCHITECTURE_TRANSLATION = {
22
- "x86_64": "amd64",
23
- "arm64": "arm64",
24
- "aarch64": "arm64",
25
- }
26
-
27
22
 
28
23
  def solve(
29
24
  input: Union[Input, Dict[str, Any]],
@@ -114,21 +109,17 @@ def solve(
114
109
  if isinstance(options, dict):
115
110
  options = Options.from_dict(options)
116
111
 
117
- os_name = os.uname().sysname.lower()
112
+ os_name = platform.system().lower()
118
113
  if os_name not in SUPPORTED_OS:
119
114
  raise Exception(f'unsupported operating system: "{os_name}", supported os are: {", ".join(SUPPORTED_OS)}')
120
115
 
121
- architecture = os.uname().machine.lower()
116
+ architecture = platform.machine().lower()
122
117
  if architecture not in SUPPORTED_ARCHITECTURES:
123
118
  raise Exception(
124
119
  f'unsupported architecture: "{architecture}", supported arch are: {", ".join(SUPPORTED_ARCHITECTURES)}'
125
120
  )
126
121
 
127
- binary_name = f"nextroute-{os_name}-{_ARCHITECTURE_TRANSLATION[architecture]}"
128
- if os_name == "windows":
129
- binary_name += ".exe"
130
-
131
- executable = os.path.join(os.path.dirname(__file__), "bin", binary_name)
122
+ executable = os.path.join(os.path.dirname(__file__), "bin", "nextroute.exe")
132
123
  if not os.path.exists(executable):
133
124
  raise Exception(f"missing Nextroute binary: {executable}")
134
125
 
nextroute/version.py CHANGED
@@ -1,3 +1,5 @@
1
+ # © 2019-present nextmv.io inc
2
+
1
3
  import os
2
4
  import subprocess
3
5
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: nextroute
3
- Version: 1.8.0.dev24
3
+ Version: 1.12.3
4
4
  Summary: Nextroute is an engine for solving Vehicle Routing Problems (VRPs).
5
5
  Author-email: Nextmv <tech@nextmv.io>
6
6
  Maintainer-email: Nextmv <tech@nextmv.io>
@@ -104,10 +104,12 @@ Classifier: Programming Language :: Python :: 3.9
104
104
  Classifier: Programming Language :: Python :: 3.10
105
105
  Classifier: Programming Language :: Python :: 3.11
106
106
  Classifier: Programming Language :: Python :: 3.12
107
+ Classifier: Programming Language :: Python :: 3.13
107
108
  Requires-Python: >=3.8
108
109
  Description-Content-Type: text/markdown
109
110
  License-File: LICENSE
110
111
  Requires-Dist: pydantic>=2.5.2
112
+ Dynamic: license-file
111
113
 
112
114
  # Nextroute
113
115
 
@@ -197,7 +199,7 @@ file.
197
199
 
198
200
  For further information on how to get started, features, deployment, etc.,
199
201
  please refer to the [official
200
- documentation](https://www.nextmv.io/docs/vehicle-routing).
202
+ documentation](https://www.nextmv.io/docs/vehicle-routing/get-started).
201
203
 
202
204
  ### Go
203
205
 
@@ -233,12 +235,7 @@ with open("cmd/input.json") as f:
233
235
  data = json.load(f)
234
236
 
235
237
  input = nextroute.schema.Input.from_dict(data)
236
- options = nextroute.Options(
237
- solve=nextroute.ParallelSolveOptions(
238
- duration=5,
239
- ),
240
- )
241
-
238
+ options = nextroute.Options(SOLVE_DURATION=5)
242
239
  output = nextroute.solve(input, options)
243
240
  print(json.dumps(output.to_dict(), indent=2))
244
241
  ```
@@ -0,0 +1,26 @@
1
+ nextroute-1.12.3.dist-info/RECORD,,
2
+ nextroute-1.12.3.dist-info/WHEEL,sha256=entZNN85s-JxqcmXHTt39Ax198pJrOYoKFOQaLspAVk,110
3
+ nextroute-1.12.3.dist-info/top_level.txt,sha256=KNsuo1j7tr-QvBFS_ELmo68OmJ-orEZiOXqstGLJDXA,16
4
+ nextroute-1.12.3.dist-info/METADATA,sha256=IWx8wIDz9Eq2z-3bNW431o7hYu2q3HX0iJLH6A1MOn4,15612
5
+ nextroute-1.12.3.dist-info/licenses/LICENSE,sha256=ElKAl6B15yj4LTFL_Dh1hje431kMHut2urxo5nheRWs,4107
6
+ tests/solve_golden/__init__.py,sha256=MRZbyX5fYnxsXV0YP6lf32c5YYMo3NsEFqPwdwf5W18,32
7
+ tests/solve_golden/main.py,sha256=VScx0kPD8ihGR5bSIpLrPTIxRh7mkk9YQ9g0eg8XZMc,1738
8
+ tests/schema/test_input.py,sha256=kNIlUFqCkvvQieuJIo7-6C_NB9PZqZ72NpGwUWtDZcA,1851
9
+ tests/schema/__init__.py,sha256=MRZbyX5fYnxsXV0YP6lf32c5YYMo3NsEFqPwdwf5W18,32
10
+ tests/schema/test_output.py,sha256=i0VreVBXamC-Pk-z7X3W9APjmjfG8iOPAt2jxP4hDQA,11579
11
+ nextroute/options.py,sha256=tZcTLIjsx-6fq9Q9Y-Z8syKbPqGyR3GV6vfpWvVmzLQ,10280
12
+ nextroute/version.py,sha256=aA_wmIifNRCzxBigX4cSz1Y2CdXvuBNIiY7Nem5UqRA,325
13
+ nextroute/__init__.py,sha256=wBHS73T_Mm2MfcEx4chZCI1QMbV-a1D7bw7megFLgb4,529
14
+ nextroute/base_model.py,sha256=Jl205oJZvndkY8rIomZD2k5YhlGgRBn_2I1BKwQ8Vkk,541
15
+ nextroute/__about__.py,sha256=H71OIfyHAXJOiG4wuGcoP506V3in6aScHaOMWCJknuM,57
16
+ nextroute/solve.py,sha256=It1kDEdFk_AXX-gEUjvQbc7C-0ma9bEPRbnC_bHMsUU,4192
17
+ nextroute/bin/nextroute.exe,sha256=uqL63K4vXMiKLTpZqa8APeh7kmutF7frc4EGSCMm4Eo,14990208
18
+ nextroute/schema/stop.py,sha256=leKhNC0rcEo64NMNofDuVYnfDNLaqfLZ-mSxUEAwoZk,2199
19
+ nextroute/schema/vehicle.py,sha256=0W3q-upCn5yEUDX2BBeq5TkOCsrlLsk3Wao-T74nDmU,2535
20
+ nextroute/schema/__init__.py,sha256=KSlnpJQMnCDq5IXyiltt9CiafcxUH40B3FdE1fc5wdI,1167
21
+ nextroute/schema/location.py,sha256=hihllMU3IXxQHugGC4Wn_TzDPr5zm4xqVqRsKanwrmA,301
22
+ nextroute/schema/input.py,sha256=uidXX_hpv1KpiIUCc_XhiiQf69vxiav9DsXfiLtpD2s,2856
23
+ nextroute/schema/statistics.py,sha256=lNlb1mcpR6xWQclG-fkDMnFoy-sEKpXKk80zU1TKrpQ,3600
24
+ nextroute/schema/output.py,sha256=rJv_PpP0X0MdLTRa2tbDsLLbGhTRi43hGmMNmzvNZEw,4810
25
+ nextroute/check/__init__.py,sha256=Hrw5LUqdMeaBEsoIrJmWDhmAKAzGuzDnoBExwNWJvlU,1303
26
+ nextroute/check/schema.py,sha256=hBC0Usw4_rEJHsWDxDtIi0-d6byXt8f9eYqBE9hqiCc,5853
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.44.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-macosx_13_0_x86_64
5
5
 
@@ -0,0 +1,50 @@
1
+ # This script is copied to the `src` root so that the `nextroute` import is
2
+ # resolved. It is fed an input via stdin and is meant to write the output to
3
+ # stdout.
4
+ import nextmv
5
+
6
+ import nextroute
7
+
8
+
9
+ def main() -> None:
10
+ """Entry point for the program."""
11
+
12
+ parameters = [
13
+ nextmv.Parameter("input", str, "", "Path to input file. Default is stdin.", False),
14
+ nextmv.Parameter("output", str, "", "Path to output file. Default is stdout.", False),
15
+ ]
16
+
17
+ default_options = nextroute.Options()
18
+ for name, default_value in default_options.to_dict().items():
19
+ parameters.append(nextmv.Parameter(name.lower(), type(default_value), default_value, name, False))
20
+
21
+ options = nextmv.Options(*parameters)
22
+
23
+ input = nextmv.load_local(options=options, path=options.input)
24
+
25
+ nextmv.log("Solving vehicle routing problem:")
26
+ nextmv.log(f" - stops: {len(input.data.get('stops', []))}")
27
+ nextmv.log(f" - vehicles: {len(input.data.get('vehicles', []))}")
28
+
29
+ model = DecisionModel()
30
+ output = model.solve(input)
31
+ nextmv.write_local(output, path=options.output)
32
+
33
+
34
+ class DecisionModel(nextmv.Model):
35
+ def solve(self, input: nextmv.Input) -> nextmv.Output:
36
+ """Solves the given problem and returns the solution."""
37
+
38
+ nextroute_input = nextroute.schema.Input.from_dict(input.data)
39
+ nextroute_options = nextroute.Options.extract_from_dict(input.options.to_dict())
40
+ nextroute_output = nextroute.solve(nextroute_input, nextroute_options)
41
+
42
+ return nextmv.Output(
43
+ options=input.options,
44
+ solution=nextroute_output.solutions[0].to_dict(),
45
+ statistics=nextroute_output.statistics.to_dict(),
46
+ )
47
+
48
+
49
+ if __name__ == "__main__":
50
+ main()
@@ -1,31 +0,0 @@
1
- # © 2019-present nextmv.io inc
2
-
3
- """
4
- Options for the Nextroute check engine.
5
- """
6
-
7
- from enum import Enum
8
-
9
- from nextroute.base_model import BaseModel
10
-
11
-
12
- class Verbosity(str, Enum):
13
- """Format of an `Input`."""
14
-
15
- OFF = "off"
16
- """The check engine is not run."""
17
- LOW = "low"
18
- """Low verbosity for the check engine."""
19
- MEDIUM = "medium"
20
- """Medium verbosity for the check engine."""
21
- HIGH = "high"
22
- """High verbosity for the check engine."""
23
-
24
-
25
- class Options(BaseModel):
26
- """Options for the Nextroute check engine."""
27
-
28
- duration: float = 30
29
- """Maximum duration of the check, in seconds."""
30
- verbosity: Verbosity = Verbosity.OFF
31
- """Verbosity of the check engine."""
@@ -1,16 +0,0 @@
1
- # © 2019-present nextmv.io inc
2
-
3
- """
4
- Functionality for creating a ready-to-go Nextroute model.
5
- """
6
-
7
- from .options import Constraints as Constraints
8
- from .options import DisableConstraints as DisableConstraints
9
- from .options import DisableProperties as DisableProperties
10
- from .options import DisableValidate as DisableValidate
11
- from .options import EnableConstraints as EnableConstraints
12
- from .options import EnableValidate as EnableValidate
13
- from .options import Objectives as Objectives
14
- from .options import Options as Options
15
- from .options import Properties as Properties
16
- from .options import Validate as Validate
@@ -1,146 +0,0 @@
1
- # © 2019-present nextmv.io inc
2
-
3
- """
4
- Options for the Nextroute factory.
5
- """
6
-
7
- from typing import List
8
-
9
- from pydantic import Field
10
-
11
- from nextroute.base_model import BaseModel
12
-
13
-
14
- class DisableConstraints(BaseModel):
15
- """Options for disabling specific constraints."""
16
-
17
- attributes: bool = False
18
- """Ignore the compatibility attributes constraint."""
19
- capacity: bool = False
20
- """Ignore the capacity constraint for all resources."""
21
- capacities: List[str] = Field(default_factory=list)
22
- """Ignore the capacity constraint for the given resource names."""
23
- distance_limit: bool = False
24
- """Ignore the distance limit constraint."""
25
- groups: bool = False
26
- """Ignore the groups constraint."""
27
- maximum_duration: bool = False
28
- """Ignore the maximum duration constraint."""
29
- maximum_stops: bool = False
30
- """Ignore the maximum stops constraint."""
31
- maximum_wait_stop: bool = False
32
- """Ignore the maximum stop wait constraint."""
33
- maximum_wait_vehicle: bool = False
34
- """Ignore the maximum vehicle wait constraint."""
35
- mixing_items: bool = False
36
- """Ignore the do not mix items constraint."""
37
- precedence: bool = False
38
- """Ignore the precedence (pickups & deliveries) constraint."""
39
- vehicle_start_time: bool = False
40
- """Ignore the vehicle start time constraint."""
41
- vehicle_end_time: bool = False
42
- """Ignore the vehicle end time constraint."""
43
- start_time_windows: bool = False
44
- """Ignore the start time windows constraint."""
45
-
46
-
47
- class EnableConstraints(BaseModel):
48
- """Options for enabling specific constraints."""
49
-
50
- cluster: bool = False
51
- """Enable the cluster constraint."""
52
-
53
-
54
- class Constraints(BaseModel):
55
- """Options for configuring constraints."""
56
-
57
- disable: DisableConstraints = Field(default_factory=DisableConstraints)
58
- """Options for disabling specific constraints."""
59
- enable: EnableConstraints = Field(default_factory=EnableConstraints)
60
- """Options for enabling specific constraints."""
61
-
62
-
63
- class Objectives(BaseModel):
64
- """Options for configuring objectives."""
65
-
66
- capacities: str = ""
67
- """
68
- Capacity objective, provide triple for each resource
69
- `name:default;factor:1.0;offset;0.0`.
70
- """
71
- min_stops: float = 1.0
72
- """Factor to weigh the min stops objective."""
73
- early_arrival_penalty: float = 1.0
74
- """Factor to weigh the early arrival objective."""
75
- late_arrival_penalty: float = 1.0
76
- """Factor to weigh the late arrival objective."""
77
- vehicle_activation_penalty: float = 1.0
78
- """Factor to weigh the vehicle activation objective."""
79
- travel_duration: float = 0.0
80
- """Factor to weigh the travel duration objective."""
81
- vehicles_duration: float = 1.0
82
- """Factor to weigh the vehicles duration objective."""
83
- unplanned_penalty: float = 1.0
84
- """Factor to weigh the unplanned objective."""
85
- cluster: float = 0.0
86
- """Factor to weigh the cluster objective."""
87
-
88
-
89
- class DisableProperties(BaseModel):
90
- """Options for disabling specific properties."""
91
-
92
- durations: bool = False
93
- """Ignore the durations of stops."""
94
- stop_duration_multipliers: bool = False
95
- """Ignore the stop duration multipliers defined on vehicles."""
96
- duration_groups: bool = False
97
- """Ignore the durations groups of stops."""
98
- initial_solution: bool = False
99
- """Ignore the initial solution."""
100
-
101
-
102
- class Properties(BaseModel):
103
- """Options for configuring properties."""
104
-
105
- disable: DisableProperties = Field(default_factory=DisableProperties)
106
- """Options for disabling specific properties."""
107
-
108
-
109
- class DisableValidate(BaseModel):
110
- """Options for disabling specific validations."""
111
-
112
- start_time: bool = False
113
- """Disable the start time validation."""
114
- resources: bool = False
115
- """Disable the resources validation."""
116
-
117
-
118
- class EnableValidate(BaseModel):
119
- """Options for enabling specific validations."""
120
-
121
- matrix: bool = False
122
- """Enable matrix validation."""
123
- matrix_asymmetry_tolerance: int = 20
124
- """Percentage of acceptable matrix asymmetry, requires matrix validation enabled."""
125
-
126
-
127
- class Validate(BaseModel):
128
- """Options for configuring validations."""
129
-
130
- disable: DisableValidate = Field(default_factory=DisableValidate)
131
- """Options for disabling specific validations"""
132
- enable: EnableValidate = Field(default_factory=EnableValidate)
133
- """Options for enabling specific validations"""
134
-
135
-
136
- class Options(BaseModel):
137
- """Options that configure how a Nextroute model is built."""
138
-
139
- constraints: Constraints = Field(default_factory=Constraints)
140
- """Options for configuring constraints."""
141
- objectives: Objectives = Field(default_factory=Objectives)
142
- """Options for configuring objectives."""
143
- properties: Properties = Field(default_factory=Properties)
144
- """Options for configuring properties."""
145
- validate_options: Validate = Field(default_factory=Validate, alias="validate")
146
- """Options for configuring validations."""
@@ -1,29 +0,0 @@
1
- tests/nextroute_python/test_options.py,sha256=8MJM05tF12M2kUjasA0kStrMFoIC1m9jDlydybpSUmI,4960
2
- tests/nextroute_python/__init__.py,sha256=MRZbyX5fYnxsXV0YP6lf32c5YYMo3NsEFqPwdwf5W18,32
3
- tests/nextroute_python/schema/test_input.py,sha256=kNIlUFqCkvvQieuJIo7-6C_NB9PZqZ72NpGwUWtDZcA,1851
4
- tests/nextroute_python/schema/__init__.py,sha256=MRZbyX5fYnxsXV0YP6lf32c5YYMo3NsEFqPwdwf5W18,32
5
- tests/nextroute_python/schema/test_output.py,sha256=i0VreVBXamC-Pk-z7X3W9APjmjfG8iOPAt2jxP4hDQA,11579
6
- nextroute/options.py,sha256=swxZjQxkZ1-YbZ4RodaVsxZLc-6Ii5N6dp6veCPPl28,4647
7
- nextroute/version.py,sha256=LIMuiAulNxiYKzNKUC6Qc6R67OgxI-54dtlvVHBFto8,292
8
- nextroute/__init__.py,sha256=1NRMAxEvJKZ6JRwPGRK2C7LbfDH7SFqjSCGGLeqdFlM,669
9
- nextroute/base_model.py,sha256=Jl205oJZvndkY8rIomZD2k5YhlGgRBn_2I1BKwQ8Vkk,541
10
- nextroute/__about__.py,sha256=OdrvvA4KQR6CdtHBxVgVsNdTuVWgCqdn-eK4jSa7GJk,62
11
- nextroute/solve.py,sha256=Fdo35yPnz_54CyqVUy46uGQ6WjTBZy3alJKz8yI6Iok,4409
12
- nextroute/bin/nextroute.exe,sha256=U5ohy1yYxPwUmct_Pd6SuO78Tg_7kybZDsbxfgsySDk,13296048
13
- nextroute/schema/stop.py,sha256=leKhNC0rcEo64NMNofDuVYnfDNLaqfLZ-mSxUEAwoZk,2199
14
- nextroute/schema/vehicle.py,sha256=0W3q-upCn5yEUDX2BBeq5TkOCsrlLsk3Wao-T74nDmU,2535
15
- nextroute/schema/__init__.py,sha256=KSlnpJQMnCDq5IXyiltt9CiafcxUH40B3FdE1fc5wdI,1167
16
- nextroute/schema/location.py,sha256=hihllMU3IXxQHugGC4Wn_TzDPr5zm4xqVqRsKanwrmA,301
17
- nextroute/schema/input.py,sha256=8_-xi-P2t-A6mObLscKavT-Ro0td9VECp2pZOQ1dWmo,1863
18
- nextroute/schema/statistics.py,sha256=lNlb1mcpR6xWQclG-fkDMnFoy-sEKpXKk80zU1TKrpQ,3600
19
- nextroute/schema/output.py,sha256=rJv_PpP0X0MdLTRa2tbDsLLbGhTRi43hGmMNmzvNZEw,4810
20
- nextroute/check/options.py,sha256=6fMFJvnd84OPmpGiFtSZmtVJmn0FdmX0PH71dZeQdt4,703
21
- nextroute/check/__init__.py,sha256=cYCx73rR1gOysg1XHvothsdsN6sP-1jAiVIjxtNzaVg,1387
22
- nextroute/check/schema.py,sha256=hBC0Usw4_rEJHsWDxDtIi0-d6byXt8f9eYqBE9hqiCc,5853
23
- nextroute/factory/options.py,sha256=XMvfev4K5RGclMxkX5tCu5JhV5GchBWeOpptn3cdL9I,4948
24
- nextroute/factory/__init__.py,sha256=kqi5fdunYqwBP3uOsBu-m0vHQANjOiyJpnKZ1xluER4,614
25
- nextroute-1.8.0.dev24.dist-info/RECORD,,
26
- nextroute-1.8.0.dev24.dist-info/LICENSE,sha256=ElKAl6B15yj4LTFL_Dh1hje431kMHut2urxo5nheRWs,4107
27
- nextroute-1.8.0.dev24.dist-info/WHEEL,sha256=kEDwEAybh1yMTwMF4usCWe2WGZj5bJzouP4ddPUefKc,111
28
- nextroute-1.8.0.dev24.dist-info/top_level.txt,sha256=KNsuo1j7tr-QvBFS_ELmo68OmJ-orEZiOXqstGLJDXA,16
29
- nextroute-1.8.0.dev24.dist-info/METADATA,sha256=5wRe6kY7cqQuvwgGortFEhJ0Uj_VRe0uy7D5EnKj2hY,15587
@@ -1,141 +0,0 @@
1
- import unittest
2
-
3
- import nextroute
4
- import nextroute.check
5
- import nextroute.factory
6
- from nextroute import options
7
-
8
-
9
- class TestOptions(unittest.TestCase):
10
- def test_options_default_values(self):
11
- opt = nextroute.Options()
12
- options_dict = opt.to_dict()
13
- self.assertDictEqual(
14
- options_dict,
15
- {
16
- "check": {"duration": 30.0, "verbosity": "off"},
17
- "format": {"disable": {"progression": False}},
18
- "model": {
19
- "constraints": {
20
- "disable": {
21
- "attributes": False,
22
- "capacity": False,
23
- "capacities": [],
24
- "distance_limit": False,
25
- "groups": False,
26
- "maximum_duration": False,
27
- "maximum_stops": False,
28
- "maximum_wait_stop": False,
29
- "maximum_wait_vehicle": False,
30
- "mixing_items": False,
31
- "precedence": False,
32
- "vehicle_start_time": False,
33
- "vehicle_end_time": False,
34
- "start_time_windows": False,
35
- },
36
- "enable": {"cluster": False},
37
- },
38
- "objectives": {
39
- "capacities": "",
40
- "min_stops": 1.0,
41
- "early_arrival_penalty": 1.0,
42
- "late_arrival_penalty": 1.0,
43
- "vehicle_activation_penalty": 1.0,
44
- "travel_duration": 0.0,
45
- "vehicles_duration": 1.0,
46
- "unplanned_penalty": 1.0,
47
- "cluster": 0.0,
48
- },
49
- "properties": {
50
- "disable": {
51
- "durations": False,
52
- "stop_duration_multipliers": False,
53
- "duration_groups": False,
54
- "initial_solution": False,
55
- }
56
- },
57
- "validate": {
58
- "disable": {"start_time": False, "resources": False},
59
- "enable": {"matrix": False, "matrix_asymmetry_tolerance": 20},
60
- },
61
- },
62
- "solve": {
63
- "iterations": -1,
64
- "duration": 5.0,
65
- "parallel_runs": -1,
66
- "start_solutions": -1,
67
- "run_deterministically": False,
68
- },
69
- },
70
- )
71
-
72
- def test_flatten(self):
73
- nested = {
74
- "foo": {
75
- "bar": False,
76
- "baz": 1,
77
- "roh": "doh",
78
- },
79
- "bar": {
80
- "baz": {
81
- "bah": "roh",
82
- }
83
- },
84
- "baz": False,
85
- "bah": 1,
86
- }
87
- flat = options._flatten(nested)
88
- self.assertDictEqual(
89
- flat,
90
- {
91
- "-foo.bar": False,
92
- "-foo.baz": 1,
93
- "-foo.roh": "doh",
94
- "-bar.baz.bah": "roh",
95
- "-baz": False,
96
- "-bah": 1,
97
- },
98
- )
99
-
100
- def test_options_to_args(self):
101
- # Default options should not produce any arguments.
102
- opt = nextroute.Options()
103
- args = opt.to_args()
104
- self.assertListEqual(args, [])
105
-
106
- # Only options that are not default should produce arguments.
107
- opt2 = nextroute.Options(
108
- check=nextroute.check.Options(
109
- duration=4,
110
- verbosity=nextroute.check.Verbosity.MEDIUM,
111
- ),
112
- solve=nextroute.ParallelSolveOptions(
113
- duration=4,
114
- iterations=-1, # Default value should be skipped.
115
- ),
116
- model=nextroute.factory.Options(
117
- constraints=nextroute.factory.Constraints(
118
- disable=nextroute.factory.DisableConstraints(
119
- attributes=True,
120
- ),
121
- ),
122
- validate=nextroute.factory.Validate(
123
- enable=nextroute.factory.EnableValidate(
124
- matrix=False, # This option should be skipped because it is bool False.
125
- ),
126
- ),
127
- ),
128
- )
129
- args2 = opt2.to_args()
130
- self.assertListEqual(
131
- args2,
132
- [
133
- "-check.duration",
134
- "4.0s",
135
- "-check.verbosity",
136
- "medium",
137
- "-model.constraints.disable.attributes", # Bool flags do not have values.
138
- "-solve.duration",
139
- "4.0s",
140
- ],
141
- )
File without changes
File without changes