MATE-runtime 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. MATE/__init__.py +22 -0
  2. MATE/__main__.py +7 -0
  3. MATE/cli/__init__.py +181 -0
  4. MATE/cli/commands.py +573 -0
  5. MATE/cli/workdir.py +571 -0
  6. MATE/compat/__init__.py +1 -0
  7. MATE/compat/interface.py +248 -0
  8. MATE/compat/sizes.py +144 -0
  9. MATE/config/__init__.py +1 -0
  10. MATE/config/axes.py +310 -0
  11. MATE/config/builders.py +498 -0
  12. MATE/config/calendar.py +371 -0
  13. MATE/config/canonical.py +543 -0
  14. MATE/config/criteria.py +396 -0
  15. MATE/config/events.py +183 -0
  16. MATE/config/invariance.py +227 -0
  17. MATE/config/loader.py +498 -0
  18. MATE/config/model.py +883 -0
  19. MATE/config/resolve.py +943 -0
  20. MATE/config/runtable.py +342 -0
  21. MATE/config/seeds.py +344 -0
  22. MATE/config/stages.py +632 -0
  23. MATE/config/structure.py +635 -0
  24. MATE/config/toml.py +237 -0
  25. MATE/conformance/__init__.py +14 -0
  26. MATE/conformance/baseline.py +250 -0
  27. MATE/conformance/checks.py +1604 -0
  28. MATE/conformance/sampler.py +179 -0
  29. MATE/errors.py +236 -0
  30. MATE/evaluation/__init__.py +1 -0
  31. MATE/evaluation/budget.py +160 -0
  32. MATE/evaluation/invalids.py +181 -0
  33. MATE/evaluation/requests.py +148 -0
  34. MATE/execution/__init__.py +1 -0
  35. MATE/execution/accelerator.py +60 -0
  36. MATE/execution/base.py +643 -0
  37. MATE/execution/choose.py +152 -0
  38. MATE/execution/devices.py +108 -0
  39. MATE/execution/process.py +377 -0
  40. MATE/execution/serial.py +37 -0
  41. MATE/execution/thread.py +106 -0
  42. MATE/execution/vectorised.py +32 -0
  43. MATE/launch.py +267 -0
  44. MATE/loop/__init__.py +6 -0
  45. MATE/loop/continuous.py +172 -0
  46. MATE/loop/driver.py +783 -0
  47. MATE/loop/events.py +266 -0
  48. MATE/loop/generational.py +140 -0
  49. MATE/loop/owns_loop.py +333 -0
  50. MATE/loop/subpops.py +508 -0
  51. MATE/modules/__init__.py +1 -0
  52. MATE/modules/construct.py +198 -0
  53. MATE/modules/discovery.py +238 -0
  54. MATE/modules/registry.py +970 -0
  55. MATE/modules/software.py +593 -0
  56. MATE/modules/versions.py +72 -0
  57. MATE/numerics.py +159 -0
  58. MATE/observation/__init__.py +10 -0
  59. MATE/observation/control.py +199 -0
  60. MATE/observation/hub.py +451 -0
  61. MATE/observation/records.py +125 -0
  62. MATE/observation/sinks.py +313 -0
  63. MATE/provenance.py +396 -0
  64. MATE/readout/__init__.py +10 -0
  65. MATE/readout/catalogue.py +282 -0
  66. MATE/readout/front.py +151 -0
  67. MATE/readout/readout.py +927 -0
  68. MATE/readout/series.py +155 -0
  69. MATE/readout/statistics.py +202 -0
  70. MATE/run/__init__.py +1 -0
  71. MATE/run/checkpointing.py +266 -0
  72. MATE/run/contexts.py +553 -0
  73. MATE/run/declarations.py +240 -0
  74. MATE/run/plan.py +464 -0
  75. MATE/run/preconditions.py +165 -0
  76. MATE/run/run.py +991 -0
  77. MATE/run/specs.py +319 -0
  78. MATE/run/times.py +103 -0
  79. MATE/scheduling/__init__.py +214 -0
  80. MATE/scheduling/local.py +119 -0
  81. MATE/scheduling/slurm.py +380 -0
  82. MATE/storage/__init__.py +1 -0
  83. MATE/storage/checkpoint.py +414 -0
  84. MATE/storage/codec.py +315 -0
  85. MATE/storage/database.py +359 -0
  86. MATE/storage/lease.py +397 -0
  87. MATE/storage/rundir.py +248 -0
  88. mate_runtime-1.0.0.dist-info/METADATA +58 -0
  89. mate_runtime-1.0.0.dist-info/RECORD +93 -0
  90. mate_runtime-1.0.0.dist-info/WHEEL +4 -0
  91. mate_runtime-1.0.0.dist-info/entry_points.txt +2 -0
  92. mate_runtime-1.0.0.dist-info/licenses/COPYING +674 -0
  93. mate_runtime-1.0.0.dist-info/licenses/COPYING.LESSER +165 -0
@@ -0,0 +1,248 @@
1
+ """The interface compatibility test between a system's specs and a method's requirements.
2
+
3
+ The test runs before ``configure``, blueprint by blueprint, for every subpopulation and the
4
+ roles it holds ([X] I2, I9). It has three outcomes: compatible, compatible under condition (a
5
+ size bound the method may exceed), incompatible (a required dimension diverges) ([X] I1).
6
+ """
7
+
8
+ from collections.abc import Mapping, Sequence
9
+ from dataclasses import dataclass
10
+ from enum import Enum
11
+ from typing import Literal
12
+
13
+ from MATE_contract import (
14
+ Capabilities,
15
+ Integer,
16
+ NetworkSpec,
17
+ ParamsSpec,
18
+ PerformanceSpec,
19
+ PhenotypeSpec,
20
+ ProgramSpec,
21
+ Requirements,
22
+ )
23
+
24
+ from MATE.compat.sizes import bounded_sizes
25
+
26
+ __all__ = ["Outcome", "Mismatch", "ConditionalBound", "InterfaceReport", "check_interface"]
27
+
28
+ # Organ types are keyed by the class name of the organ ([X] §5.2 stage 1).
29
+ _ORGAN_TYPES: tuple[tuple[type, str], ...] = (
30
+ (ParamsSpec, "Params"),
31
+ (NetworkSpec, "Network"),
32
+ (ProgramSpec, "Program"),
33
+ )
34
+
35
+
36
+ class Outcome(Enum):
37
+ """The three outcomes of the interface compatibility test ([X] I1)."""
38
+
39
+ COMPATIBLE = "compatible"
40
+ CONDITIONAL = "compatible under condition"
41
+ INCOMPATIBLE = "incompatible"
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class Mismatch:
46
+ """A required dimension on which the system's specs and the method's requirements diverge.
47
+
48
+ Parameters
49
+ ----------
50
+ stage : {"organs", "ports", "performance", "capabilities"}
51
+ The stage of the test that found it.
52
+ role : str
53
+ The role concerned.
54
+ organ : str or None
55
+ The failing organ(s), comma-separated, when the stage has organs.
56
+ dimension : str
57
+ ``"organs.<type>"``, ``"ports.<attribute>"``, ``"objectives"``, ``"descriptors"``,
58
+ ``"constraints"`` or ``"interruptible"``.
59
+ required : str
60
+ What the system's specs require.
61
+ offered : str
62
+ What the method's requirements declare.
63
+ """
64
+
65
+ stage: Literal["organs", "ports", "performance", "capabilities"]
66
+ role: str
67
+ organ: str | None
68
+ dimension: str
69
+ required: str
70
+ offered: str
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class ConditionalBound:
75
+ """A size bound the method may exceed: individuals are checked one by one.
76
+
77
+ Parameters
78
+ ----------
79
+ role : str
80
+ The role.
81
+ organ : str
82
+ The organ name.
83
+ bound : str
84
+ The bound name, e.g. ``"number_of_hidden_nodes"``.
85
+ domain : Integer
86
+ The interface spec's bound.
87
+ declared : int or "any"
88
+ What the method declared (``"any"`` when it declared nothing).
89
+ """
90
+
91
+ role: str
92
+ organ: str
93
+ bound: str
94
+ domain: Integer
95
+ declared: int | Literal["any"]
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class InterfaceReport:
100
+ """The result of the interface compatibility test.
101
+
102
+ Parameters
103
+ ----------
104
+ outcome : Outcome
105
+ ``INCOMPATIBLE`` if any mismatch, else ``CONDITIONAL`` if any conditional bound,
106
+ else ``COMPATIBLE``.
107
+ mismatches : tuple of Mismatch
108
+ Every mismatch, ordered by stage, then role.
109
+ conditional : tuple of ConditionalBound
110
+ Every bound the method may exceed.
111
+ """
112
+
113
+ outcome: Outcome
114
+ mismatches: tuple[Mismatch, ...]
115
+ conditional: tuple[ConditionalBound, ...]
116
+
117
+
118
+ def _organ_type(organ_spec: object) -> str:
119
+ """Return the organ type name of an organ spec."""
120
+ for cls, name in _ORGAN_TYPES:
121
+ if isinstance(organ_spec, cls):
122
+ return name
123
+ return type(organ_spec).__name__
124
+
125
+
126
+ def _range(bounds: tuple[int, int | str]) -> str:
127
+ """Render a ``(min, max | "any")`` interval."""
128
+ return f"({bounds[0]}, {bounds[1]!r})"
129
+
130
+
131
+ def _within(n: int, bounds: tuple[int, int | str]) -> bool:
132
+ """Tell whether ``n`` lies in a ``(min, max | "any")`` interval, inclusive."""
133
+ lo, hi = bounds
134
+ return n >= lo and (hi == "any" or n <= hi)
135
+
136
+
137
+ def _organs(role: str, spec: PhenotypeSpec, req: Requirements) -> list[Mismatch]:
138
+ """Stage 1: organ composition against the maxima ([X] §5.2, [K] RQ1)."""
139
+ by_type: dict[str, list[str]] = {}
140
+ for organ, organ_spec in spec.organs.items():
141
+ by_type.setdefault(_organ_type(organ_spec), []).append(organ)
142
+ out = []
143
+ for t, organs in by_type.items():
144
+ m = req.organs.get(t, 0)
145
+ if m != "any" and len(organs) > m:
146
+ out.append(Mismatch("organs", role, ", ".join(organs), f"organs.{t}",
147
+ f"{len(organs)} {t} organ(s)", f"organs['{t}'] = {m}"))
148
+ return out
149
+
150
+
151
+ def _ports(role: str, spec: PhenotypeSpec, req: Requirements) -> list[Mismatch]:
152
+ """Stage 2: required port attributes on both ends of every network ([X] §5.2, [K] RQ3)."""
153
+ out = []
154
+ for port in req.ports:
155
+ for organ, organ_spec in spec.organs.items():
156
+ if not isinstance(organ_spec, NetworkSpec):
157
+ continue
158
+ ins, outs = tuple(organ_spec.input_attributes), tuple(organ_spec.output_attributes)
159
+ if port not in ins or port not in outs:
160
+ out.append(Mismatch("ports", role, organ, f"ports.{port}",
161
+ f"input attributes {ins}, output attributes {outs}",
162
+ f"ports {tuple(req.ports)}"))
163
+ return out
164
+
165
+
166
+ def _performance(role: str, perf: PerformanceSpec, req: Requirements) -> list[Mismatch]:
167
+ """Stage 3: numbers of objectives, descriptors and constraints ([X] §5.2, [K] RQ4, RQ5)."""
168
+ out = []
169
+ k, d, c = len(perf.objectives), len(perf.descriptors), len(perf.constraints)
170
+ if not _within(k, req.objectives):
171
+ out.append(Mismatch("performance", role, None, "objectives", f"{k} objective(s)",
172
+ f"objectives = {_range(req.objectives)}"))
173
+ if not _within(d, req.descriptors):
174
+ out.append(Mismatch("performance", role, None, "descriptors", f"{d} descriptor(s)",
175
+ f"descriptors = {_range(req.descriptors)}"))
176
+ if c >= 1 and not req.constraints:
177
+ out.append(Mismatch("performance", role, None, "constraints", f"{c} constraint(s)",
178
+ "constraints = False"))
179
+ return out
180
+
181
+
182
+ def _sizes(role: str, spec: PhenotypeSpec, req: Requirements) -> list[ConditionalBound]:
183
+ """Size bounds: compatible under ``"spec"`` or a maximum within the bound ([X] I5-I7)."""
184
+ out = []
185
+ for organ, organ_spec in spec.organs.items():
186
+ for bound, domain in bounded_sizes(organ_spec).items():
187
+ declared = req.sizes.get(bound, "any")
188
+ if declared == "spec":
189
+ continue
190
+ if isinstance(declared, int) and domain.hi is not None and declared <= domain.hi:
191
+ continue
192
+ out.append(ConditionalBound(role, organ, bound, domain, declared))
193
+ return out
194
+
195
+
196
+ def check_interface(
197
+ specs: Mapping[str, PhenotypeSpec],
198
+ performance: Mapping[str, PerformanceSpec],
199
+ roles: Sequence[str],
200
+ requirements: Requirements,
201
+ system_capabilities: Capabilities,
202
+ ) -> InterfaceReport:
203
+ """Test a method's requirements against the specs of the roles it holds.
204
+
205
+ Parameters
206
+ ----------
207
+ specs : mapping of str to PhenotypeSpec
208
+ Role -> normalised interface spec.
209
+ performance : mapping of str to PerformanceSpec
210
+ Role -> performance spec.
211
+ roles : sequence of str
212
+ The roles the subpopulation holds.
213
+ requirements : Requirements
214
+ The method's requirements, computed at its construction.
215
+ system_capabilities : Capabilities
216
+ The system's capabilities.
217
+
218
+ Returns
219
+ -------
220
+ InterfaceReport
221
+ Every stage is evaluated; the mismatches of all stages are reported together, in the
222
+ stage order organs, ports, performance, capabilities.
223
+
224
+ Notes
225
+ -----
226
+ [X] §5, I1-I7; [K] RQ1-RQ7. Content values are never tested (I3).
227
+ """
228
+ stages: dict[str, list[Mismatch]] = {s: [] for s in ("organs", "ports", "performance",
229
+ "capabilities")}
230
+ conditional: list[ConditionalBound] = []
231
+ for role in roles:
232
+ spec = specs[role]
233
+ stages["organs"] += _organs(role, spec, requirements)
234
+ stages["ports"] += _ports(role, spec, requirements)
235
+ stages["performance"] += _performance(role, performance[role], requirements)
236
+ if requirements.interruptible and not system_capabilities.interruptible:
237
+ stages["capabilities"].append(
238
+ Mismatch("capabilities", role, None, "interruptible", "interruptible = False",
239
+ "interruptible = True"))
240
+ conditional += _sizes(role, spec, requirements)
241
+ mismatches = tuple(m for stage in stages.values() for m in stage)
242
+ if mismatches:
243
+ outcome = Outcome.INCOMPATIBLE
244
+ elif conditional:
245
+ outcome = Outcome.CONDITIONAL
246
+ else:
247
+ outcome = Outcome.COMPATIBLE
248
+ return InterfaceReport(outcome, mismatches, tuple(conditional))
MATE/compat/sizes.py ADDED
@@ -0,0 +1,144 @@
1
+ """Bounded sizes of organ specs, and the per-individual size checks they call for.
2
+
3
+ A size bound is a field of an organ spec that bounds the size of the organs a method may
4
+ produce. In MATE 1.0.0 the catalogue holds one bound, ``NetworkSpec.number_of_hidden_nodes``
5
+ ([X] I8); a fixed size is the bound on a single-element domain ([X] I7).
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from typing import TYPE_CHECKING
10
+
11
+ from MATE_contract import Integer, Network, NetworkSpec, PhenotypeSpec
12
+
13
+ if TYPE_CHECKING:
14
+ from MATE.compat.interface import InterfaceReport
15
+
16
+ __all__ = ["SizeCheck", "bounded_sizes", "measure_size", "size_checks"]
17
+
18
+ HIDDEN = "number_of_hidden_nodes"
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class SizeCheck:
23
+ """One bounded size to check on every decoded individual.
24
+
25
+ Parameters
26
+ ----------
27
+ organ : str
28
+ Organ name in the interface spec.
29
+ bound : str
30
+ Bound name, e.g. ``"number_of_hidden_nodes"``.
31
+ domain : Integer
32
+ The interface spec's bound.
33
+ conditional : bool
34
+ ``True`` when the pair is compatible under condition: a violation becomes
35
+ ``Invalid(DECODE)``. ``False``: the method committed to the bound and a violation is
36
+ a method error.
37
+
38
+ Notes
39
+ -----
40
+ [X] SZ1, SZ2.
41
+ """
42
+
43
+ organ: str
44
+ bound: str
45
+ domain: Integer
46
+ conditional: bool
47
+
48
+
49
+ def bounded_sizes(organ_spec: object) -> dict[str, Integer]:
50
+ """Return the bounded sizes of an organ spec.
51
+
52
+ Parameters
53
+ ----------
54
+ organ_spec : object
55
+ A ``ParamsSpec``, ``NetworkSpec`` or any other organ spec.
56
+
57
+ Returns
58
+ -------
59
+ dict of str to Integer
60
+ Bound name -> domain. A fixed size ``n`` is ``Integer(n, n)``; an organ spec without
61
+ bounded size gives ``{}``.
62
+
63
+ Notes
64
+ -----
65
+ [X] I7, I8.
66
+ """
67
+ if isinstance(organ_spec, NetworkSpec):
68
+ hidden = organ_spec.number_of_hidden_nodes
69
+ if isinstance(hidden, Integer):
70
+ return {HIDDEN: hidden}
71
+ if isinstance(hidden, int):
72
+ return {HIDDEN: Integer(hidden, hidden)}
73
+ return {}
74
+
75
+
76
+ def measure_size(organ: object, organ_spec: object, bound: str) -> int:
77
+ """Return the size of one organ for one bound.
78
+
79
+ Parameters
80
+ ----------
81
+ organ : object
82
+ The decoded organ.
83
+ organ_spec : object
84
+ Its organ spec.
85
+ bound : str
86
+ A bound of :func:`bounded_sizes` of ``organ_spec``.
87
+
88
+ Returns
89
+ -------
90
+ int
91
+ For ``number_of_hidden_nodes``: ``n_nodes`` minus the organ spec's input and output
92
+ port counts, the nodes beyond the ports being hidden ([K] §5.2).
93
+
94
+ Raises
95
+ ------
96
+ ValueError
97
+ For a bound the organ spec does not have, or an organ of another type.
98
+
99
+ Notes
100
+ -----
101
+ [X] I8.
102
+ """
103
+ if bound not in bounded_sizes(organ_spec):
104
+ raise ValueError(
105
+ f"size bound '{bound}' is not a bounded size of {type(organ_spec).__name__}"
106
+ )
107
+ if not isinstance(organ, Network):
108
+ raise ValueError(
109
+ f"size bound '{bound}': expected a Network organ, got {type(organ).__name__}"
110
+ )
111
+ assert isinstance(organ_spec, NetworkSpec)
112
+ n_in = organ_spec.number_of_input_nodes or 0
113
+ n_out = organ_spec.number_of_output_nodes or 0
114
+ return int(organ.n_nodes) - n_in - n_out
115
+
116
+
117
+ def size_checks(spec: PhenotypeSpec, report: InterfaceReport, role: str) -> tuple[SizeCheck, ...]:
118
+ """Return the size checks to apply to the individuals of one role.
119
+
120
+ Parameters
121
+ ----------
122
+ spec : PhenotypeSpec
123
+ The role's interface spec.
124
+ report : InterfaceReport
125
+ The interface compatibility report of the method holding the role.
126
+ role : str
127
+ The role.
128
+
129
+ Returns
130
+ -------
131
+ tuple of SizeCheck
132
+ One per bounded size of every organ, in organ order; ``conditional`` is ``True`` iff
133
+ the report lists the bound as conditional for this role and organ.
134
+
135
+ Notes
136
+ -----
137
+ [X] SZ2: every bounded size is checked, whatever the outcome of the test.
138
+ """
139
+ conditional = {(c.organ, c.bound) for c in report.conditional if c.role == role}
140
+ return tuple(
141
+ SizeCheck(organ, bound, domain, (organ, bound) in conditional)
142
+ for organ, organ_spec in spec.organs.items()
143
+ for bound, domain in bounded_sizes(organ_spec).items()
144
+ )
@@ -0,0 +1 @@
1
+ """Configuration of the runtime: the blueprint model, its canonical form and the seeds."""