"""
The instance of the Luna Tomato Logistics beginner problem.
>>> inst1 = Instance("matching-i")
>>> inst1.n
25000
>>> inst1.shape
(25000, 4)
>>> list(map(int, inst1[0, :]))
[3390, 4454, 3664, 267]
>>> inst1[0, 1]
np.int64(4454)
>>> inst1.penalty
125526621
>>> inst1.lengths
(5000, 5000, 5000)
>>> inst1.name
'matching-i'
>>> inst1 is Instance("matching-i")
True
>>> inst2 = Instance("matching-ii")
>>> inst2.n
92103
>>> inst2.shape
(92103, 4)
>>> list(map(int, inst2[0, :]))
[5559, 5444, 3794, 9723]
>>> inst2[0, 1]
np.int64(5444)
>>> inst2.penalty
460191177
>>> inst2.lengths
(10000, 10000, 10000)
>>> inst2.name
'matching-ii'
"""
from typing import Any, Callable, Final, Generator, cast
import numpy as np
from moptipy.api.component import Component
from moptipy.utils.logger import KeyValueLogSection
from moptipy.utils.nputils import int_range_to_dtype
from pycommons.types import check_to_int_range
from moptipyapps.spoc.spoc_4.challenge_1.beginner.data import (
open_resource_stream,
)
[docs]
class Instance(np.ndarray, Component):
"""The instances of the Luna Tomato Logistics beginner problem."""
#: set the result name
name: str
#: set the penalty
penalty: int
#: the number of orbit pairs
n: int
#: the number of values per column
lengths: tuple[int, int, int]
def __new__(cls, name: str) -> "Instance": # noqa: PYI034
"""Create the single instance."""
name = str.strip(name)
if str.__len__(name) <= 0:
raise ValueError("Invalid name.")
meth: Final = Instance.__new__
attr: Final[str] = f"__{name}_data"
if hasattr(meth, attr):
return cast("Instance", getattr(meth, attr))
data: list[list] = []
power: int = 0
with open_resource_stream(f"{name}.txt") as stream:
for oline in stream:
line = str.strip(oline)
if str.__len__(line) <= 0:
continue
splt: list[str] = line.split()
if list.__len__(splt) != 4:
raise ValueError(f"Invalid format: {oline!r}.")
cap: str = splt[3]
data.append([
check_to_int_range(splt[0], "e") - 1,
check_to_int_range(splt[1], "l") - 1,
check_to_int_range(splt[2], "d") - 1,
cap])
li: int = cap.rfind(".")
if li >= 0:
power = max(power, len(cap) - li)
power -= 1
if power != 3:
raise ValueError(f"Power must be 3, but is {power}.")
power = 10 ** power
for row in data:
row[-1] = round(float(row[-1]) * power)
if any(r < 0 for r in row):
raise ValueError(f"Invalid row: {row}.")
penalty: Final[int] = sum(r[3] for r in data) + 1
if penalty <= 1:
raise ValueError(
f"Maximum must be positive, but is {penalty - 1}.")
lengths: Final[tuple[int, int, int]] = (
max(r[0] for r in data) + 1,
max(r[1] for r in data) + 1,
max(r[2] for r in data) + 1)
use_shape: tuple[int, int] = (len(data), 4)
dt: np.dtype = int_range_to_dtype(
0, (3 * penalty * use_shape[0]) + penalty - 1)
result: Instance = super().__new__(Instance, use_shape, dt)
for i, row in enumerate(data):
for j, val in enumerate(row):
result[i, j] = val
if val != result[i, j]:
raise ValueError("Error during conversion.")
#: set the result name
result.name = name
#: set the penalty
result.penalty = penalty
#: the number of orbit pairs
result.n = use_shape[0]
#: the number of values per column
result.lengths = lengths
setattr(meth, attr, result)
return result
def __str__(self) -> str:
"""
Get the name of this instance.
:returns: the name of this instance.
"""
return self.name
[docs]
def log_parameters_to(self, logger: KeyValueLogSection) -> None:
"""
Log all parameters of this component as key-value pairs.
:param logger: the logger for the parameters
"""
super().log_parameters_to(logger)
logger.key_value("penalty", self.penalty)
logger.key_value("lengths", ";".join(map(str, self.lengths)))
[docs]
@staticmethod
def list_resources() -> tuple[str, str]:
"""
Get all the beginner problem instances.
:return: the problem instance names
>>> Instance.list_resources()
('matching-i', 'matching-ii')
>>> for ix in Instance.list_resources():
... print(Instance(ix).name)
matching-i
matching-ii
"""
return ("matching-i", "matching-ii")
[docs]
@staticmethod
def list_instances() -> Generator[Callable[[], Any], None, None]:
"""
Get an iterable of all instances.
:return: the iterable
>>> for ix in Instance.list_instances():
... print(ix().name)
matching-i
matching-ii
"""
yield lambda: Instance("matching-i")
yield lambda: Instance("matching-ii")