Coverage for moptipy / examples / jssp / gantt.py: 84%
64 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""A class for representing Gantt charts as objects."""
2from typing import Final
4import numpy as np
5from pycommons.io.path import Path
6from pycommons.types import type_error
8from moptipy.api.logging import SECTION_RESULT_Y, SECTION_SETUP
9from moptipy.evaluation.log_parser import LogParser
10from moptipy.examples.jssp.instance import Instance
13# start book
14class Gantt(np.ndarray):
15 """
16 A class representing Gantt charts.
18 A Gantt chart is a diagram that visualizes when a job on a given
19 machine begins or ends. We here represent it as a three-dimensional
20 matrix. This matrix has one row for each machine and one column for
21 each operation on the machine.
22 In each cell, it holds three values: the job ID, the start, and the
23 end time of the job on the machine. The Gantt chart has the
24 additional attribute `instance` which references the JSSP instance
25 for which the chart is constructed.
26 Gantt charts must only be created by an instance of
27 :class:`moptipy.examples.jssp.gantt_space.GanttSpace`.
28 """
30# end book
32 #: the JSSP instance for which the Gantt chart is created
33 instance: Instance
35 def __new__(cls, space) -> "Gantt":
36 """
37 Create the Gantt chart.
39 :param space: the Gantt space for which the instance is created.
40 """
41 gnt: Final[Gantt] = np.ndarray.__new__(Gantt, space.shape, space.dtype)
42 #: the JSSP instance for which the Gantt chart is created
43 gnt.instance = space.instance
44 return gnt
46 @staticmethod
47 def from_log(file: str,
48 instance: Instance | None = None) -> "Gantt":
49 """
50 Load a Gantt chart from a log file.
52 :param file: the log file path
53 :param instance: the optional JSSP instance: if `None` is provided,
54 we try to load it from the resources
55 :returns: the Gantt chart
56 """
57 return _GanttParser(instance).parse_file(file)
60class _GanttParser(LogParser[Gantt]):
61 """The log parser for loading Gantt charts."""
63 def __init__(self, instance: Instance | None = None):
64 """
65 Create the gantt parser.
67 :param instance: the optional JSSP instance: if `None` is provided,
68 we try to load it from the resources
69 """
70 super().__init__()
71 if (instance is not None) and (not isinstance(instance, Instance)):
72 raise type_error(instance, "instance", Instance)
73 #: the internal instance
74 self.__instance: Instance | None = instance
75 #: the internal section mode: 0=none, 1=setup, 2=y
76 self.__sec_mode: int = 0
77 #: the gantt string
78 self.__gantt_str: str | None = None
79 #: the result Gantt chart
80 self._result: Gantt | None = None
82 def _start_section(self, title: str) -> bool:
83 """Start a section."""
84 super()._start_section(title)
85 self.__sec_mode = 0
86 if title == SECTION_SETUP:
87 if self.__instance is None:
88 self.__sec_mode = 1
89 return True
90 return False
91 if title == SECTION_RESULT_Y:
92 self.__sec_mode = 2
93 return True
94 return False
96 def _lines(self, lines: list[str]) -> bool:
97 """Parse the lines."""
98 if self.__sec_mode == 1:
99 if self.__instance is not None:
100 raise ValueError(
101 f"instance is already set to {self.__instance}.")
102 key: Final[str] = "y.inst.name: "
103 for line in lines:
104 if line.startswith(key):
105 self.__instance = Instance.from_resource(
106 line[len(key):].strip())
107 if self.__instance is None:
108 raise ValueError(f"Did not find instance key {key!r} "
109 f"in section {SECTION_SETUP}!")
110 elif self.__sec_mode == 2:
111 self.__gantt_str = " ".join(lines).strip()
112 else:
113 raise ValueError("Should not be in section?")
114 return (self.__instance is None) or (self.__gantt_str is None)
116 def _parse_file(self, file: Path) -> Gantt:
117 """End the file."""
118 super()._parse_file(file)
119 if self.__gantt_str is None:
120 raise ValueError(f"Section {SECTION_RESULT_Y} missing!")
121 if self.__instance is None:
122 raise ValueError(f"Section {SECTION_SETUP} missing or empty!")
123 if self._result is not None:
124 raise ValueError("Applied parser to more than one log file?")
125 # pylint: disable=C0415,R0401
126 from moptipy.examples.jssp.gantt_space import ( # noqa: PLC0415
127 GanttSpace, # pylint: disable=C0415,R0401
128 )
129 return GanttSpace(self.__instance).from_str(self.__gantt_str)
131 def _end_parse_file(self, file: Path) -> None:
132 """Cleanup."""
133 self.__gantt_str = None
134 super()._end_parse_file(file)