Skip to content

Commit

Permalink
Add the output class to nextroute
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-quintero committed Dec 21, 2023
1 parent 5bc56e8 commit 8e576a2
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
2 changes: 1 addition & 1 deletion nextmv/nextroute/schema/input.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Defines the input class"""
"""Defines the input class."""

from typing import Any

Expand Down
95 changes: 95 additions & 0 deletions nextmv/nextroute/schema/output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Defines the output class."""

from typing import Any

from nextmv.base_model import BaseModel


class Version(BaseModel):
"""A version used for solving."""

sdk: str
"""Nextmv SDK."""


class Solution(BaseModel):
"""Solution to a Vehicle Routing Problem (VRP)."""


class RunStatistics(BaseModel):
"""Statistics about a general run."""

duration: float | None = None
"""Duration of the run in seconds."""
iterations: int | None = None
"""Number of iterations."""
custom: Any | None = None
"""Custom statistics created by the user. Can normally expect a `dict[str,
Any]`."""


class ResultStatistics(BaseModel):
"""Statistics about a specific result."""

duration: float | None = None
"""Duration of the run in seconds."""
value: float | None = None
"""Value of the result."""
custom: Any | None = None
"""Custom statistics created by the user. Can normally expect a `dict[str,
Any]`."""


class DataPoint(BaseModel):
"""A data point."""

x: float
"""X coordinate of the data point."""
y: float
"""Y coordinate of the data point."""


class Series(BaseModel):
"""A series of data points."""

name: str | None = None
"""Name of the series."""
data_points: list[DataPoint] | None = None
"""Data of the series."""


class SeriesData(BaseModel):
"""Data of a series."""

value: Series | None = None
"""A series for the value of the solution."""
custom: list[Series] | None = None
"""A list of series for custom statistics."""


class Statistics(BaseModel):
"""Statistics of a solution."""

schema: str
"""Schema (version)."""

run: RunStatistics | None = None
"""Statistics about the run."""
result: ResultStatistics | None = None
"""Statistics about the last result."""
series_data: SeriesData | None = None
"""Data of the series."""


class Output(BaseModel):
"""Output schema for Nextroute."""

options: dict[str, Any]
"""Options used to obtain this output."""
version: Version
"""Versions used for the solution."""

solutions: list[Solution] | None = None
"""Solutions to the problem."""
statistics: Statistics | None = None
"""Statistics of the solution."""
63 changes: 63 additions & 0 deletions tests/nextroute/schema/test_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import math
import unittest

from nextmv.nextroute.schema.output import ResultStatistics


class TestOutput(unittest.TestCase):
def test_result_statistics_decoding(self):
test_cases = [
{
"name": "value is float",
"json_stats": '{"duration": 0.1, "value": 1.23}',
},
{
"name": "value is nan",
"json_stats": '{"duration": 0.1, "value": "nan"}',
},
{
"name": "value is infinity",
"json_stats": '{"duration": 0.1, "value": "inf"}',
},
{
"name": "value is infinity 2",
"json_stats": '{"duration": 0.1, "value": "+inf"}',
},
{
"name": "value is -infinity",
"json_stats": '{"duration": 0.1, "value": "-inf"}',
},
]

for test in test_cases:
dict_stats = json.loads(test["json_stats"])
stats = ResultStatistics.from_dict(dict_stats)
self.assertTrue(isinstance(stats, ResultStatistics))
self.assertTrue(isinstance(stats.value, float))

def test_result_statistics_encoding(self):
test_cases = [
{
"name": "value is float",
"stats": ResultStatistics(duration=0.1, value=1.23),
},
{
"name": "value is nan",
"stats": ResultStatistics(duration=0.1, value=math.nan),
},
{
"name": "value is infinity",
"stats": ResultStatistics(duration=0.1, value=math.inf),
},
{
"name": "value is -infinity",
"stats": ResultStatistics(duration=0.1, value=-1 * math.inf),
},
]

for test in test_cases:
stats = test["stats"]
dict_stats = stats.to_dict()
self.assertTrue(isinstance(dict_stats, dict))
self.assertTrue(isinstance(dict_stats["value"], float))

0 comments on commit 8e576a2

Please sign in to comment.