Source code for moptipy.examples.jssp.gantt
"""A class for representing Gantt charts as objects."""
from typing import Final
import numpy as np
from pycommons.types import type_error
from moptipy.api.logging import SECTION_RESULT_Y, SECTION_SETUP
from moptipy.evaluation.log_parser import LogParser
from moptipy.examples.jssp.instance import Instance
# start book
[docs]
class Gantt(np.ndarray):
"""
A class representing Gantt charts.
A Gantt chart is a diagram that visualizes when a job on a given
machine begins or ends. We here represent it as a three-dimensional
matrix. This matrix has one row for each machine and one column for
each operation on the machine.
In each cell, it holds three values: the job ID, the start, and the
end time of the job on the machine. The Gantt chart has the
additional attribute `instance` which references the JSSP instance
for which the chart is constructed.
Gantt charts must only be created by an instance of
:class:`moptipy.examples.jssp.gantt_space.GanttSpace`.
"""
# end book
#: the JSSP instance for which the Gantt chart is created
instance: Instance
def __new__(cls, space) -> "Gantt":
"""
Create the Gantt chart.
:param space: the Gantt space for which the instance is created.
"""
gnt: Final[Gantt] = np.ndarray.__new__(Gantt, space.shape, space.dtype)
#: the JSSP instance for which the Gantt chart is created
gnt.instance = space.instance
return gnt
[docs]
@staticmethod
def from_log(file: str,
instance: Instance | None = None) -> "Gantt":
"""
Load a Gantt chart from a log file.
:param file: the log file path
:param instance: the optional JSSP instance: if `None` is provided,
we try to load it from the resources
:returns: the Gantt chart
"""
parser: Final[_GanttParser] = _GanttParser(instance)
parser.parse_file(file)
# noinspection PyProtectedMember
res = parser._result
if res is None:
raise ValueError("Failed to load Gantt chart.")
return res
class _GanttParser(LogParser):
"""The log parser for loading Gantt charts."""
def __init__(self, instance: Instance | None = None):
"""
Create the gantt parser.
:param instance: the optional JSSP instance: if `None` is provided,
we try to load it from the resources
"""
super().__init__()
if (instance is not None) and (not isinstance(instance, Instance)):
raise type_error(instance, "instance", Instance)
#: the internal instance
self.__instance: Instance | None = instance
#: the internal section mode: 0=none, 1=setup, 2=y
self.__sec_mode: int = 0
#: the gantt string
self.__gantt_str: str | None = None
#: the result Gantt chart
self._result: Gantt | None = None
def start_section(self, title: str) -> bool:
"""Start a section."""
super().start_section(title)
self.__sec_mode = 0
if title == SECTION_SETUP:
if self.__instance is None:
self.__sec_mode = 1
return True
return False
if title == SECTION_RESULT_Y:
self.__sec_mode = 2
return True
return False
def lines(self, lines: list[str]) -> bool:
"""Parse the lines."""
if self.__sec_mode == 1:
if self.__instance is not None:
raise ValueError(
f"instance is already set to {self.__instance}.")
key: Final[str] = "y.inst.name: "
for line in lines:
if line.startswith(key):
self.__instance = Instance.from_resource(
line[len(key):].strip())
if self.__instance is None:
raise ValueError(f"Did not find instance key {key!r} "
f"in section {SECTION_SETUP}!")
elif self.__sec_mode == 2:
self.__gantt_str = " ".join(lines).strip()
else:
raise ValueError("Should not be in section?")
return (self.__instance is None) or (self.__gantt_str is None)
def end_file(self) -> bool:
"""End the file."""
if self.__gantt_str is None:
raise ValueError(f"Section {SECTION_RESULT_Y} missing!")
if self.__instance is None:
raise ValueError(f"Section {SECTION_SETUP} missing or empty!")
if self._result is not None:
raise ValueError("Applied parser to more than one log file?")
# pylint: disable=C0415,R0401
from moptipy.examples.jssp.gantt_space import (
GanttSpace, # pylint: disable=C0415,R0401
)
self._result = GanttSpace(self.__instance).from_str(self.__gantt_str)
self.__gantt_str = None
return False