nextmv 0.1.0.dev2__tar.gz → 0.1.0.dev4__tar.gz
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.
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/PKG-INFO +2 -1
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/application.py +11 -10
- nextmv-0.1.0.dev4/nextmv/nextroute/check/__init__.py +26 -0
- nextmv-0.1.0.dev4/nextmv/nextroute/check/schema.py +140 -0
- nextmv-0.1.0.dev4/nextmv/nextroute/schema/__init__.py +25 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/nextroute/schema/input.py +1 -1
- nextmv-0.1.0.dev4/nextmv/nextroute/schema/output.py +202 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/pyproject.toml +4 -1
- nextmv-0.1.0.dev4/tests/nextroute/schema/output.json +851 -0
- nextmv-0.1.0.dev4/tests/nextroute/schema/output_with_check.json +954 -0
- nextmv-0.1.0.dev4/tests/nextroute/schema/test_output.py +316 -0
- nextmv-0.1.0.dev2/nextmv/nextroute/schema/__init__.py +0 -12
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/.github/workflows/publish.yml +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/.github/workflows/python-lint.yml +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/.github/workflows/python-test.yml +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/.gitignore +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/LICENSE +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/README.md +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/base_model.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/client.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/cloud/input_set.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/nextroute/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/nextroute/schema/location.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/nextroute/schema/stop.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv/nextroute/schema/vehicle.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/nextmv-py.code-workspace +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/requirements.txt +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/nextroute/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/nextroute/schema/__init__.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/nextroute/schema/input.json +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/nextroute/schema/test_input.py +0 -0
- {nextmv-0.1.0.dev2 → nextmv-0.1.0.dev4}/tests/test_base_model.py +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nextmv
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev4
|
|
4
4
|
Summary: The Python SDK for Nextmv
|
|
5
5
|
Project-URL: Homepage, https://www.nextmv.io
|
|
6
6
|
Project-URL: Documentation, https://www.nextmv.io/docs
|
|
7
7
|
Project-URL: Repository, https://github.com/nextmv-io/nextmv-py
|
|
8
8
|
Author-email: Nextmv <tech@nextmv.io>
|
|
9
|
+
Maintainer-email: Nextmv <tech@nextmv.io>
|
|
9
10
|
License: Apache License
|
|
10
11
|
Version 2.0, January 2004
|
|
11
12
|
http://www.apache.org/licenses/
|
|
@@ -78,6 +78,10 @@ class PollingOptions(BaseModel):
|
|
|
78
78
|
"""Maximum number of tries to use."""
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
_DEFAULT_POLLING_OPTIONS: PollingOptions = PollingOptions()
|
|
82
|
+
"""Default polling options to use when polling for a run result."""
|
|
83
|
+
|
|
84
|
+
|
|
81
85
|
class RunInformation(BaseModel):
|
|
82
86
|
"""Information of a run."""
|
|
83
87
|
|
|
@@ -473,7 +477,7 @@ class Application:
|
|
|
473
477
|
upload_id_used = upload_id is not None
|
|
474
478
|
if not upload_id_used and input_size > _MAX_RUN_SIZE:
|
|
475
479
|
upload_url = self.upload_url()
|
|
476
|
-
self.upload_large_input(
|
|
480
|
+
self.upload_large_input(input=input, upload_url=upload_url)
|
|
477
481
|
upload_id = upload_url.upload_id
|
|
478
482
|
upload_id_used = True
|
|
479
483
|
|
|
@@ -510,7 +514,7 @@ class Application:
|
|
|
510
514
|
description: str | None = None,
|
|
511
515
|
upload_id: str | None = None,
|
|
512
516
|
run_options: dict[str, Any] | None = None,
|
|
513
|
-
polling_options: PollingOptions =
|
|
517
|
+
polling_options: PollingOptions = _DEFAULT_POLLING_OPTIONS,
|
|
514
518
|
) -> RunResult:
|
|
515
519
|
"""
|
|
516
520
|
Submit an input to start a new run of the application and poll for the
|
|
@@ -526,8 +530,7 @@ class Application:
|
|
|
526
530
|
description: Description of the run.
|
|
527
531
|
upload_id: ID to use when running a large input.
|
|
528
532
|
run_options: Options to use for the run.
|
|
529
|
-
polling_options: Options to use when polling for the run result.
|
|
530
|
-
not provided, the default options will be used.
|
|
533
|
+
polling_options: Options to use when polling for the run result.
|
|
531
534
|
|
|
532
535
|
Returns:
|
|
533
536
|
Result of the run.
|
|
@@ -596,7 +599,7 @@ class Application:
|
|
|
596
599
|
def run_result_with_polling(
|
|
597
600
|
self,
|
|
598
601
|
run_id: str,
|
|
599
|
-
polling_options: PollingOptions,
|
|
602
|
+
polling_options: PollingOptions = _DEFAULT_POLLING_OPTIONS,
|
|
600
603
|
) -> RunResult:
|
|
601
604
|
"""
|
|
602
605
|
Get the result of a run. The result includes the run output. This
|
|
@@ -605,6 +608,7 @@ class Application:
|
|
|
605
608
|
|
|
606
609
|
Args:
|
|
607
610
|
run_id: ID of the run.
|
|
611
|
+
polling_options: Options to use when polling for the run result.
|
|
608
612
|
|
|
609
613
|
Returns:
|
|
610
614
|
Result of the run.
|
|
@@ -613,9 +617,6 @@ class Application:
|
|
|
613
617
|
requests.HTTPError: If the response status code is not 2xx.
|
|
614
618
|
"""
|
|
615
619
|
|
|
616
|
-
if polling_options is None:
|
|
617
|
-
polling_options = PollingOptions()
|
|
618
|
-
|
|
619
620
|
time.sleep(polling_options.initial_delay)
|
|
620
621
|
delay = polling_options.delay
|
|
621
622
|
polling_ok = False
|
|
@@ -643,15 +644,15 @@ class Application:
|
|
|
643
644
|
|
|
644
645
|
def upload_large_input(
|
|
645
646
|
self,
|
|
646
|
-
upload_url: UploadURL,
|
|
647
647
|
input: dict[str, Any],
|
|
648
|
+
upload_url: UploadURL,
|
|
648
649
|
) -> None:
|
|
649
650
|
"""
|
|
650
651
|
Upload the file located at the given path to the provided upload URL.
|
|
651
652
|
|
|
652
653
|
Args:
|
|
653
654
|
upload_url: Upload URL to use for uploading the file.
|
|
654
|
-
|
|
655
|
+
input: Input to use for the run.
|
|
655
656
|
|
|
656
657
|
Raises:
|
|
657
658
|
requests.HTTPError: If the response status code is not 2xx.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check provides a plugin that allows you check models and solutions.
|
|
3
|
+
|
|
4
|
+
Checking a model or a solution checks the unplanned plan units. It checks each
|
|
5
|
+
individual plan unit if it can be added to the solution. If the plan unit can
|
|
6
|
+
be added to the solution, the report will include on how many vehicles and
|
|
7
|
+
what the impact would be on the objective value. If the plan unit cannot be
|
|
8
|
+
added to the solution, the report will include the reason why it cannot be
|
|
9
|
+
added to the solution.
|
|
10
|
+
|
|
11
|
+
The check can be invoked on a nextroute.Model or a nextroute.Solution. If the
|
|
12
|
+
check is invoked on a model, an empty solution is created and the check is
|
|
13
|
+
executed on this empty solution. An empty solution is a solution with all the
|
|
14
|
+
initial stops that are fixed, initial stops that are not fixed are not added
|
|
15
|
+
to the solution. The check is executed on the unplanned plan units of the
|
|
16
|
+
solution. If the check is invoked on a solution, it is executed on the
|
|
17
|
+
unplanned plan units of the solution.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .schema import Objective as Objective
|
|
21
|
+
from .schema import ObjectiveTerm as ObjectiveTerm
|
|
22
|
+
from .schema import Output as Output
|
|
23
|
+
from .schema import PlanUnit as PlanUnit
|
|
24
|
+
from .schema import Solution as Solution
|
|
25
|
+
from .schema import Summary as Summary
|
|
26
|
+
from .schema import Vehicle as Vehicle
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""This module contains definitions for the schema in the Nextroute check."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from nextmv.base_model import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ObjectiveTerm(BaseModel):
|
|
8
|
+
"""Check of the individual terms of the objective for a move."""
|
|
9
|
+
|
|
10
|
+
base: float | None = None
|
|
11
|
+
"""Base of the objective term."""
|
|
12
|
+
factor: float | None = None
|
|
13
|
+
"""Factor of the objective term."""
|
|
14
|
+
name: str | None = None
|
|
15
|
+
"""Name of the objective term."""
|
|
16
|
+
value: float | None = None
|
|
17
|
+
"""Value of the objective term, which is equivalent to `self.base *
|
|
18
|
+
self.factor`."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Objective(BaseModel):
|
|
22
|
+
"""Estimate of an objective of a move."""
|
|
23
|
+
|
|
24
|
+
terms: list[ObjectiveTerm] | None = None
|
|
25
|
+
"""Check of the individual terms of the objective."""
|
|
26
|
+
value: float | None = None
|
|
27
|
+
"""Value of the objective."""
|
|
28
|
+
vehicle: str | None = None
|
|
29
|
+
"""ID of the vehicle for which it reports the objective."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Solution(BaseModel):
|
|
33
|
+
"""Solution that the check has been executed on."""
|
|
34
|
+
|
|
35
|
+
objective: Objective | None = None
|
|
36
|
+
"""Objective of the start solution."""
|
|
37
|
+
plan_units_planned: int | None = None
|
|
38
|
+
"""Number of plan units planned in the start solution."""
|
|
39
|
+
plan_units_unplanned: int | None = None
|
|
40
|
+
"""Number of plan units unplanned in the start solution."""
|
|
41
|
+
stops_planned: int | None = None
|
|
42
|
+
"""Number of stops planned in the start solution."""
|
|
43
|
+
vehicles_not_used: int | None = None
|
|
44
|
+
"""Number of vehicles not used in the start solution."""
|
|
45
|
+
vehicles_used: int | None = None
|
|
46
|
+
"""Number of vehicles used in the start solution."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Summary(BaseModel):
|
|
50
|
+
"""Summary of the check."""
|
|
51
|
+
|
|
52
|
+
moves_failed: int | None = None
|
|
53
|
+
"""number of moves that failed. A move can fail if the estimate of a
|
|
54
|
+
constraint is incorrect. A constraint is incorrect if `ModelConstraint.
|
|
55
|
+
EstimateIsViolated` returns true and one of the violation checks returns
|
|
56
|
+
false. Violation checks are implementations of one or more of the
|
|
57
|
+
interfaces [SolutionStopViolationCheck], [SolutionVehicleViolationCheck] or
|
|
58
|
+
[SolutionViolationCheck] on the same constraint. Most constraints do not
|
|
59
|
+
need and do not have violation checks as the estimate is perfect. The
|
|
60
|
+
number of moves failed can be more than one per plan unit as we continue to
|
|
61
|
+
try moves on different vehicles until we find a move that is executable or
|
|
62
|
+
all vehicles have been visited."""
|
|
63
|
+
plan_units_best_move_failed: int | None = None
|
|
64
|
+
"""Number of plan units for which the best move can not be planned. This
|
|
65
|
+
should not happen if all the constraints are implemented correct."""
|
|
66
|
+
plan_units_best_move_found: int | None = None
|
|
67
|
+
"""Number of plan units for which at least one move has been found and the
|
|
68
|
+
move is executable."""
|
|
69
|
+
plan_units_best_move_increases_objective: int | None = None
|
|
70
|
+
"""Number of plan units for which the best move is executable but would
|
|
71
|
+
increase the objective value instead of decreasing it."""
|
|
72
|
+
plan_units_checked: int | None = None
|
|
73
|
+
"""Number of plan units that have been checked. If this is less than
|
|
74
|
+
`self.plan_units_to_be_checked` the check timed out."""
|
|
75
|
+
plan_units_have_no_move: int | None = None
|
|
76
|
+
"""Number of plan units for which no feasible move has been found. This
|
|
77
|
+
implies there is no move that can be executed without violating a
|
|
78
|
+
constraint."""
|
|
79
|
+
plan_units_to_be_checked: int | None = None
|
|
80
|
+
"""Number of plan units to be checked."""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class PlanUnit(BaseModel):
|
|
84
|
+
"""Check of a plan unit."""
|
|
85
|
+
|
|
86
|
+
best_move_failed: bool | None = None
|
|
87
|
+
"""True if the plan unit's best move failed to execute."""
|
|
88
|
+
best_move_increases_objective: bool | None = None
|
|
89
|
+
"""True if the best move for the plan unit increases the objective."""
|
|
90
|
+
best_move_objective: Objective | None = None
|
|
91
|
+
"""Estimate of the objective of the best move if the plan unit has a best
|
|
92
|
+
move."""
|
|
93
|
+
constraints: dict[str, int] | None = None
|
|
94
|
+
"""Constraints that are violated for the plan unit."""
|
|
95
|
+
has_best_move: bool | None = None
|
|
96
|
+
"""True if a move is found for the plan unit. A plan unit has no move found
|
|
97
|
+
if the plan unit is over-constrained or the move found is too expensive."""
|
|
98
|
+
stops: list[str] | None = None
|
|
99
|
+
"""IDs of the sops in the plan unit."""
|
|
100
|
+
vehicles_have_moves: int | None = None
|
|
101
|
+
"""Number of vehicles that have moves for the plan unit. Only calculated if
|
|
102
|
+
the verbosity is very high."""
|
|
103
|
+
vehicles_with_moves: list[str] | None = None
|
|
104
|
+
"""IDs of the vehicles that have moves for the plan unit. Only calculated
|
|
105
|
+
if the verbosity is very high."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Vehicle(BaseModel):
|
|
109
|
+
"""Check of a vehicle."""
|
|
110
|
+
|
|
111
|
+
id: str
|
|
112
|
+
"""ID of the vehicle."""
|
|
113
|
+
|
|
114
|
+
plan_units_have_moves: int | None = None
|
|
115
|
+
"""Number of plan units that have moves for the vehicle. Only calculated if
|
|
116
|
+
the depth is medium."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Output(BaseModel):
|
|
120
|
+
"""Output of a feasibility check."""
|
|
121
|
+
|
|
122
|
+
duration_maximum: float | None = None
|
|
123
|
+
"""Maximum duration of the check, in seconds."""
|
|
124
|
+
duration_used: float | None = None
|
|
125
|
+
"""Duration used by the check, in seconds."""
|
|
126
|
+
error: str | None = None
|
|
127
|
+
"""Error raised during the check."""
|
|
128
|
+
plan_units: list[PlanUnit] | None = None
|
|
129
|
+
"""Check of the individual plan units."""
|
|
130
|
+
remark: str | None = None
|
|
131
|
+
"""Remark of the check. It can be "ok", "timeout" or anything else that
|
|
132
|
+
should explain itself."""
|
|
133
|
+
solution: Solution | None = None
|
|
134
|
+
"""Start soltuion of the check."""
|
|
135
|
+
summary: Summary | None = None
|
|
136
|
+
"""Summary of the check."""
|
|
137
|
+
vehicles: list[Vehicle] | None = None
|
|
138
|
+
"""Check of the vehicles."""
|
|
139
|
+
verbosity: str | None = None
|
|
140
|
+
"""Verbosity level of the check."""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Schema (class) definitions for the entities in Nextroute."""
|
|
2
|
+
|
|
3
|
+
from .input import Defaults as Defaults
|
|
4
|
+
from .input import DurationGroup as DurationGroup
|
|
5
|
+
from .input import Input as Input
|
|
6
|
+
from .location import Location as Location
|
|
7
|
+
from .output import DataPoint as DataPoint
|
|
8
|
+
from .output import ObjectiveOutput as ObjectiveOutput
|
|
9
|
+
from .output import Output as Output
|
|
10
|
+
from .output import PlannedStopOutput as PlannedStopOutput
|
|
11
|
+
from .output import ResultStatistics as ResultStatistics
|
|
12
|
+
from .output import RunStatistics as RunStatistics
|
|
13
|
+
from .output import Series as Series
|
|
14
|
+
from .output import SeriesData as SeriesData
|
|
15
|
+
from .output import Solution as Solution
|
|
16
|
+
from .output import Statistics as Statistics
|
|
17
|
+
from .output import StopOutput as StopOutput
|
|
18
|
+
from .output import VehicleOutput as VehicleOutput
|
|
19
|
+
from .output import Version as Version
|
|
20
|
+
from .stop import AlternateStop as AlternateStop
|
|
21
|
+
from .stop import Stop as Stop
|
|
22
|
+
from .stop import StopDefaults as StopDefaults
|
|
23
|
+
from .vehicle import InitialStop as InitialStop
|
|
24
|
+
from .vehicle import Vehicle as Vehicle
|
|
25
|
+
from .vehicle import VehicleDefaults as VehicleDefaults
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Defines the output class."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from nextmv.base_model import BaseModel
|
|
10
|
+
from nextmv.nextroute.check import Output as checkOutput
|
|
11
|
+
from nextmv.nextroute.schema.location import Location
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Version(BaseModel):
|
|
15
|
+
"""A version used for solving."""
|
|
16
|
+
|
|
17
|
+
sdk: str
|
|
18
|
+
"""Nextmv SDK."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StopOutput(BaseModel):
|
|
22
|
+
"""Basic structure for the output of a stop."""
|
|
23
|
+
|
|
24
|
+
id: str
|
|
25
|
+
"""ID of the stop."""
|
|
26
|
+
location: Location
|
|
27
|
+
"""Location of the stop."""
|
|
28
|
+
|
|
29
|
+
custom_data: Any | None = None
|
|
30
|
+
"""Custom data of the stop."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PlannedStopOutput(BaseModel):
|
|
34
|
+
"""Output of a stop planned in the solution."""
|
|
35
|
+
|
|
36
|
+
stop: StopOutput
|
|
37
|
+
"""Basic information on the stop."""
|
|
38
|
+
|
|
39
|
+
arrival_time: datetime | None = None
|
|
40
|
+
"""Actual arrival time at this stop."""
|
|
41
|
+
cumulative_travel_distance: float | None = None
|
|
42
|
+
"""Cumulative distance to travel from the first stop to this one, in meters."""
|
|
43
|
+
cumulative_travel_duration: float | None = None
|
|
44
|
+
"""Cumulative duration to travel from the first stop to this one, in seconds."""
|
|
45
|
+
custom_data: Any | None = None
|
|
46
|
+
"""Custom data of the stop."""
|
|
47
|
+
duration: float | None = None
|
|
48
|
+
"""Duration of the service at the stop, in seconds."""
|
|
49
|
+
early_arrival_duration: float | None = None
|
|
50
|
+
"""Duration of early arrival at the stop, in seconds."""
|
|
51
|
+
end_time: datetime | None = None
|
|
52
|
+
"""End time of the service at the stop."""
|
|
53
|
+
late_arrival_duration: float | None = None
|
|
54
|
+
"""Duration of late arrival at the stop, in seconds."""
|
|
55
|
+
mix_items: Any | None = None
|
|
56
|
+
"""Mix items at the stop."""
|
|
57
|
+
start_time: datetime | None = None
|
|
58
|
+
"""Start time of the service at the stop."""
|
|
59
|
+
target_arrival_time: datetime | None = None
|
|
60
|
+
"""Target arrival time at this stop."""
|
|
61
|
+
travel_distance: float | None = None
|
|
62
|
+
"""Distance to travel from the previous stop to this one, in meters."""
|
|
63
|
+
travel_duration: float | None = None
|
|
64
|
+
"""Duration to travel from the previous stop to this one, in seconds."""
|
|
65
|
+
waiting_duration: float | None = None
|
|
66
|
+
"""Waiting duratino at the stop, in seconds."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class VehicleOutput(BaseModel):
|
|
70
|
+
"""Output of a vehicle in the solution."""
|
|
71
|
+
|
|
72
|
+
id: str
|
|
73
|
+
"""ID of the vehicle."""
|
|
74
|
+
|
|
75
|
+
alternate_stops: list[str] | None = None
|
|
76
|
+
"""List of alternate stops that were planned on the vehicle."""
|
|
77
|
+
custom_data: Any | None = None
|
|
78
|
+
"""Custom data of the vehicle."""
|
|
79
|
+
route: list[PlannedStopOutput] | None = None
|
|
80
|
+
"""Route of the vehicle, which is a list of stops that were planned on
|
|
81
|
+
it."""
|
|
82
|
+
route_duration: float | None = None
|
|
83
|
+
"""Total duration of the vehicle's route, in seconds."""
|
|
84
|
+
route_stops_duration: float | None = None
|
|
85
|
+
"""Total duration of the stops of the vehicle, in seconds."""
|
|
86
|
+
route_travel_distance: float | None = None
|
|
87
|
+
"""Total travel distance of the vehicle, in meters."""
|
|
88
|
+
route_travel_duration: float | None = None
|
|
89
|
+
"""Total travel duration of the vehicle, in seconds."""
|
|
90
|
+
route_waiting_duration: float | None = None
|
|
91
|
+
"""Total waiting duration of the vehicle, in seconds."""
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ObjectiveOutput(BaseModel):
|
|
95
|
+
"""Information of the objective (value function)."""
|
|
96
|
+
|
|
97
|
+
name: str
|
|
98
|
+
"""Name of the objective."""
|
|
99
|
+
|
|
100
|
+
base: float | None = None
|
|
101
|
+
"""Base of the objective."""
|
|
102
|
+
custom_data: Any | None = None
|
|
103
|
+
"""Custom data of the objective."""
|
|
104
|
+
factor: float | None = None
|
|
105
|
+
"""Factor of the objective."""
|
|
106
|
+
objectives: list[dict[str, Any]] | None = None
|
|
107
|
+
"""List of objectives. Each list is actually of the same class
|
|
108
|
+
`ObjectiveOutput`, but we avoid a recursive definition here."""
|
|
109
|
+
value: float | None = None
|
|
110
|
+
"""Value of the objective, which is equivalent to `self.base *
|
|
111
|
+
self.factor`."""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Solution(BaseModel):
|
|
115
|
+
"""Solution to a Vehicle Routing Problem (VRP)."""
|
|
116
|
+
|
|
117
|
+
unplanned: list[StopOutput] | None = None
|
|
118
|
+
"""List of stops that were not planned in the solution."""
|
|
119
|
+
vehicles: list[VehicleOutput] | None = None
|
|
120
|
+
"""List of vehicles in the solution."""
|
|
121
|
+
objective: ObjectiveOutput | None = None
|
|
122
|
+
"""Information of the objective (value function)."""
|
|
123
|
+
check: checkOutput | None = None
|
|
124
|
+
"""Check of the solution, if enabled."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class RunStatistics(BaseModel):
|
|
128
|
+
"""Statistics about a general run."""
|
|
129
|
+
|
|
130
|
+
duration: float | None = None
|
|
131
|
+
"""Duration of the run in seconds."""
|
|
132
|
+
iterations: int | None = None
|
|
133
|
+
"""Number of iterations."""
|
|
134
|
+
custom: Any | None = None
|
|
135
|
+
"""Custom statistics created by the user. Can normally expect a `dict[str,
|
|
136
|
+
Any]`."""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ResultStatistics(BaseModel):
|
|
140
|
+
"""Statistics about a specific result."""
|
|
141
|
+
|
|
142
|
+
duration: float | None = None
|
|
143
|
+
"""Duration of the run in seconds."""
|
|
144
|
+
value: float | None = None
|
|
145
|
+
"""Value of the result."""
|
|
146
|
+
custom: Any | None = None
|
|
147
|
+
"""Custom statistics created by the user. Can normally expect a `dict[str,
|
|
148
|
+
Any]`."""
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class DataPoint(BaseModel):
|
|
152
|
+
"""A data point."""
|
|
153
|
+
|
|
154
|
+
x: float
|
|
155
|
+
"""X coordinate of the data point."""
|
|
156
|
+
y: float
|
|
157
|
+
"""Y coordinate of the data point."""
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Series(BaseModel):
|
|
161
|
+
"""A series of data points."""
|
|
162
|
+
|
|
163
|
+
name: str | None = None
|
|
164
|
+
"""Name of the series."""
|
|
165
|
+
data_points: list[DataPoint] | None = None
|
|
166
|
+
"""Data of the series."""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class SeriesData(BaseModel):
|
|
170
|
+
"""Data of a series."""
|
|
171
|
+
|
|
172
|
+
value: Series | None = None
|
|
173
|
+
"""A series for the value of the solution."""
|
|
174
|
+
custom: list[Series] | None = None
|
|
175
|
+
"""A list of series for custom statistics."""
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class Statistics(BaseModel):
|
|
179
|
+
"""Statistics of a solution."""
|
|
180
|
+
|
|
181
|
+
run: RunStatistics | None = None
|
|
182
|
+
"""Statistics about the run."""
|
|
183
|
+
result: ResultStatistics | None = None
|
|
184
|
+
"""Statistics about the last result."""
|
|
185
|
+
series_data: SeriesData | None = None
|
|
186
|
+
"""Data of the series."""
|
|
187
|
+
statistics_schema: str | None = Field(alias="schema")
|
|
188
|
+
"""Schema (version)."""
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class Output(BaseModel):
|
|
192
|
+
"""Output schema for Nextroute."""
|
|
193
|
+
|
|
194
|
+
options: dict[str, Any]
|
|
195
|
+
"""Options used to obtain this output."""
|
|
196
|
+
version: Version
|
|
197
|
+
"""Versions used for the solution."""
|
|
198
|
+
|
|
199
|
+
solutions: list[Solution] | None = None
|
|
200
|
+
"""Solutions to the problem."""
|
|
201
|
+
statistics: Statistics | None = None
|
|
202
|
+
"""Statistics of the solution."""
|
|
@@ -28,10 +28,13 @@ keywords = [
|
|
|
28
28
|
"vehicle routing problem",
|
|
29
29
|
]
|
|
30
30
|
license = { file = "LICENSE" }
|
|
31
|
+
maintainers = [
|
|
32
|
+
{ email = "tech@nextmv.io", name = "Nextmv" }
|
|
33
|
+
]
|
|
31
34
|
name = "nextmv"
|
|
32
35
|
readme = "README.md"
|
|
33
36
|
requires-python = ">=3.10"
|
|
34
|
-
version = "0.1.0.
|
|
37
|
+
version = "0.1.0.dev4"
|
|
35
38
|
|
|
36
39
|
[project.urls]
|
|
37
40
|
Homepage = "https://www.nextmv.io"
|