Coverage for moptipyapps / prodsched / multistatistics.py: 43%
58 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
1"""
2A statistics record for multiple simulations.
4We use this record as the solution space when optimizing for the MFC scenario.
5It stores the statistics of several simulation runs.
6The objective functions can then access these statistics.
7A space instance is provided that can create, copy, and serialize these
8objects to text, so that they can appear in the log files.
9"""
11from dataclasses import dataclass
12from typing import Final, Generator, Iterable
14from moptipy.api.space import Space
15from moptipy.utils.logger import (
16 KeyValueLogSection,
17)
18from pycommons.types import type_error
20from moptipyapps.prodsched.instance import Instance
21from moptipyapps.prodsched.statistics import Statistics
22from moptipyapps.prodsched.statistics import to_stream as stat_to_stream
25@dataclass(order=False, frozen=True)
26class MultiStatistics:
27 """A set of statistics gathered over multiple instances."""
29 #: the per-instance statistics
30 per_instance: tuple[Statistics, ...]
31 #: the instance names
32 inst_names: tuple[str, ...]
34 def __init__(self, instances: Iterable[Instance]) -> None:
35 """
36 Create the multi-statistics object.
38 :param instances: the instances for which we create the statistics
39 """
40 object.__setattr__(self, "per_instance", tuple(
41 Statistics(inst.n_products) for inst in instances))
42 object.__setattr__(self, "inst_names", tuple(
43 inst.name for inst in instances))
46def to_stream(multi: MultiStatistics) -> Generator[str, None, None]:
47 """
48 Convert a multi-statistics object to a stream.
50 :param multi: the multi-statistics object
51 :return: the stream of strings
52 """
53 if not isinstance(multi, MultiStatistics):
54 raise type_error(multi, "multi", MultiStatistics)
55 for i, ss in enumerate(multi.per_instance):
56 yield f"-------- Instance {i}: {multi.inst_names[i]!r} -------"
57 yield from stat_to_stream(ss)
60class MultiStatisticsSpace(Space):
61 """An implementation of the `Space` API of for multiple statistics."""
63 def __init__(self, instances: tuple[Instance, ...]) -> None:
64 """
65 Create a multi-statistics space.
67 :param instances: the instances
68 """
69 if not isinstance(instances, tuple):
70 raise type_error(instances, "instances", tuple)
71 for inst in instances:
72 if not isinstance(inst, Instance):
73 raise type_error(inst, "instance", Instance)
74 #: The instance to which the packings apply.
75 self.instances: Final[tuple[Instance, ...]] = instances
77 def copy(self, dest: MultiStatistics, source: MultiStatistics) -> None:
78 """
79 Copy one multi-statistics to another one.
81 :param dest: the destination multi-statistics
82 :param source: the source multi-statistics
83 """
84 for i, d in enumerate(dest.per_instance):
85 d.copy_from(source.per_instance[i])
87 def create(self) -> MultiStatistics:
88 """
89 Create an empty multi-statistics record.
91 :return: the empty multi-statistics record
92 """
93 return MultiStatistics(self.instances)
95 def to_str(self, x: MultiStatistics) -> str:
96 """
97 Convert a multi-statistics to a string.
99 :param x: the packing
100 :return: a string corresponding to the multi-statistics
101 """
102 return "\n".join(to_stream(x))
104 def is_equal(self, x1: MultiStatistics, x2: MultiStatistics) -> bool:
105 """
106 Check if two multi-statistics have the same contents.
108 :param x1: the first multi-statistics
109 :param x2: the second multi-statistics
110 :return: `True` if both multi-statistics have the same content
111 """
112 return (x1 is x2) or (x1.per_instance == x2.per_instance)
114 def from_str(self, text: str) -> MultiStatistics:
115 """
116 Convert a string to a multi-statistics.
118 :param text: the string
119 :return: the multi-statistics
120 """
121 if not isinstance(text, str):
122 raise type_error(text, "text", str)
123 raise NotImplementedError
125 def validate(self, x: MultiStatistics) -> None:
126 """
127 Check if a multi-statistics is valid.
129 :param x: the multi-statistics
130 :raises TypeError: if any component of the multi-statistics is of the
131 wrong type
132 :raises ValueError: if the multi-statistics is not feasible
133 """
134 if not isinstance(x, MultiStatistics):
135 raise type_error(x, "x", MultiStatistics)
136 if not isinstance(x.per_instance, tuple):
137 raise type_error(x.per_instance, "x.per_instance", tuple)
138 for s in x.per_instance:
139 if not isinstance(s, Statistics):
140 raise type_error(s, "x.per_instance[i]", Statistics)
142 def n_points(self) -> int:
143 """
144 Get the number of possible multi-statistics.
146 :return: just some arbitrary number
147 """
148 return 100 ** tuple.__len__(self.instances)
150 def __str__(self) -> str:
151 """
152 Get the name of the multi-statistics space.
154 :return: the name
155 """
156 return f"multistats_{tuple.__len__(self.instances)}"
158 def log_parameters_to(self, logger: KeyValueLogSection) -> None:
159 """
160 Log the parameters of the space to the given logger.
162 :param logger: the logger for the parameters
163 """
164 super().log_parameters_to(logger)
165 for i, inst in enumerate(self.instances):
166 logger.key_value(f"inst_{i}", inst.name)