MATE-libraries 1.0.0.dev0__tar.gz → 1.0.0rc1__tar.gz

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.

Potentially problematic release.


This version of MATE-libraries might be problematic. Click here for more details.

Files changed (21) hide show
  1. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/.gitignore +2 -0
  2. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/PKG-INFO +7 -4
  3. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/README.md +5 -2
  4. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/pyproject.toml +2 -2
  5. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/__init__.py +67 -0
  6. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_catalogue.py +258 -0
  7. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_compiled.py +463 -0
  8. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_graph.py +139 -0
  9. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_networkx.py +249 -0
  10. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_organ.py +100 -0
  11. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_prepare.py +492 -0
  12. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_propagate.py +173 -0
  13. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_state.py +332 -0
  14. mate_libraries-1.0.0rc1/src/MATE_libraries/networks/_techniques.py +430 -0
  15. mate_libraries-1.0.0rc1/src/MATE_libraries/operators/__init__.py +54 -0
  16. mate_libraries-1.0.0rc1/src/MATE_libraries/operators/_draws.py +121 -0
  17. mate_libraries-1.0.0rc1/src/MATE_libraries/operators/_selection.py +607 -0
  18. mate_libraries-1.0.0rc1/src/MATE_libraries/operators/_variation.py +121 -0
  19. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/COPYING +0 -0
  20. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/COPYING.LESSER +0 -0
  21. {mate_libraries-1.0.0.dev0 → mate_libraries-1.0.0rc1}/src/MATE_libraries/__init__.py +0 -0
@@ -10,3 +10,5 @@ dist/
10
10
  .coverage
11
11
  htmlcov/
12
12
  .project.settings
13
+ public/
14
+ docs/_build/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: MATE-libraries
3
- Version: 1.0.0.dev0
3
+ Version: 1.0.0rc1
4
4
  Summary: Libraries of MATE, the Modular Algorithmic Toolkit for Evolution: network library and standard operators library for MATE modules
5
5
  Project-URL: Homepage, https://gitlab.com/artificial_evolution/mate/mate
6
6
  Project-URL: Issues, https://gitlab.com/artificial_evolution/mate/mate/-/issues
@@ -15,7 +15,7 @@ Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Topic :: Scientific/Engineering
17
17
  Requires-Python: >=3.14
18
- Requires-Dist: mate-contract<2,>=1.0.0.dev0
18
+ Requires-Dist: mate-contract<2,>=1.0.0rc1
19
19
  Description-Content-Type: text/markdown
20
20
 
21
21
  # MATE-libraries
@@ -25,8 +25,11 @@ Modular Algorithmic Toolkit for Evolution: the network library and the standard
25
25
  operators library, which method and system modules may use. It depends on
26
26
  `MATE-contract` only.
27
27
 
28
- **Status: under development.** This release only reserves the name; it
29
- contains no functionality yet.
28
+ The standard operators library (`MATE_libraries.operators`) gives a variation
29
+ operator per domain and the standard selections. The network library
30
+ (`MATE_libraries.networks`) compiles batches of `Network` organs and propagates
31
+ them on the CPU, with results that do not depend on the batch. Each library has
32
+ its own `version`, recorded in every run whose modules import it.
30
33
 
31
34
  ## Installation
32
35
 
@@ -5,8 +5,11 @@ Modular Algorithmic Toolkit for Evolution: the network library and the standard
5
5
  operators library, which method and system modules may use. It depends on
6
6
  `MATE-contract` only.
7
7
 
8
- **Status: under development.** This release only reserves the name; it
9
- contains no functionality yet.
8
+ The standard operators library (`MATE_libraries.operators`) gives a variation
9
+ operator per domain and the standard selections. The network library
10
+ (`MATE_libraries.networks`) compiles batches of `Network` organs and propagates
11
+ them on the CPU, with results that do not depend on the batch. Each library has
12
+ its own `version`, recorded in every run whose modules import it.
10
13
 
11
14
  ## Installation
12
15
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "MATE-libraries"
7
- version = "1.0.0.dev0"
7
+ version = "1.0.0rc1"
8
8
  description = "Libraries of MATE, the Modular Algorithmic Toolkit for Evolution: network library and standard operators library for MATE modules"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -21,7 +21,7 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3",
22
22
  "Topic :: Scientific/Engineering",
23
23
  ]
24
- dependencies = ["MATE-contract>=1.0.0.dev0,<2"]
24
+ dependencies = ["MATE-contract>=1.0.0rc1,<2"]
25
25
 
26
26
  [project.urls]
27
27
  Homepage = "https://gitlab.com/artificial_evolution/mate/mate"
@@ -0,0 +1,67 @@
1
+ """The network library: compile and propagate batches of ``Network`` organs.
2
+
3
+ Three objects are never fused: the organ (frozen arrays, one per individual), the compiled
4
+ network (immutable, batched, shared between threads, never serialised) and the activation
5
+ state (the node values of individual x episode rows, mutable and serialised by copy). The
6
+ caller chooses what changes the result (propagation mode, refresh rate, activation functions,
7
+ aggregation, fate of unfilled cells, compute dtype); the library chooses only what does not
8
+ (batching technique, shortcuts derived from the graph). Each individual's results are
9
+ bit-identical whether it is compiled alone or in any batch, under any technique.
10
+ ``from_networkx`` builds an organ from a networkx graph, for hand-designed networks.
11
+
12
+ Attributes
13
+ ----------
14
+ version : int
15
+ What the library computes; changes whenever a definition, a default, a convergence rule
16
+ or the set of accepted inputs changes (rules V1, V4 of the libraries specification). A
17
+ protocol may hold it fixed under the key ``networks.version``.
18
+ TECHNIQUES : tuple of str
19
+ The batching techniques: ``"dense"``, ``"padded"``, ``"block_diagonal"``.
20
+ ACTIVATIONS : Mapping[str, Activation]
21
+ Read-only catalogue of activation functions: ``id``, ``tanh``, ``relu``, ``sigmoid``,
22
+ ``neat_sigmoid``, ``sin``, ``gauss``, ``abs``, ``step``.
23
+ AGGREGATIONS : Mapping[str, Aggregation]
24
+ Read-only catalogue of aggregations: ``sum`` (the default), ``product``, ``max``,
25
+ ``min``, ``mean``.
26
+
27
+ Notes
28
+ -----
29
+ Section 4 of the libraries specification. The device path is deferred in MATE-libraries
30
+ 1.0.0: ``Device("opencl")`` is refused, any other non-CPU device runs on the CPU, and
31
+ ``CompiledNetwork.kernels()`` raises.
32
+
33
+ Examples
34
+ --------
35
+ >>> from MATE_libraries import networks as nets
36
+ >>> compiled = nets.compile(organs, spec, unfilled={"activation": "id"},
37
+ ... device=ctx.device) # doctest: +SKIP
38
+ >>> act = compiled.state(10) # doctest: +SKIP
39
+ >>> y = compiled.step(act, x[t], refresh_rate=1) # doctest: +SKIP
40
+ >>> res = compiled.settle(u, max_steps=50, tol=1e-6) # doctest: +SKIP
41
+ """
42
+
43
+ from typing import Final
44
+
45
+ from ._catalogue import ACTIVATIONS, AGGREGATIONS, VERSION, Activation, Aggregation
46
+ from ._compiled import TECHNIQUES, CompileDiagnostics, CompiledNetwork, compile, validate
47
+ from ._networkx import from_networkx
48
+ from ._state import ActivationState, IndividualActivation, SettleResult
49
+
50
+ version: Final[int] = VERSION
51
+
52
+ __all__ = [
53
+ "version",
54
+ "TECHNIQUES",
55
+ "Activation",
56
+ "Aggregation",
57
+ "ACTIVATIONS",
58
+ "AGGREGATIONS",
59
+ "compile",
60
+ "validate",
61
+ "from_networkx",
62
+ "CompileDiagnostics",
63
+ "CompiledNetwork",
64
+ "ActivationState",
65
+ "IndividualActivation",
66
+ "SettleResult",
67
+ ]
@@ -0,0 +1,258 @@
1
+ """Catalogues of activation functions and aggregations, and name resolution ([L] §4.5)."""
2
+
3
+ from collections.abc import Callable, Mapping
4
+ from dataclasses import dataclass, field
5
+ from types import MappingProxyType
6
+ from typing import Any, Final
7
+
8
+ import numpy as np
9
+
10
+ VERSION: Final[int] = 1
11
+ """What the network library computes (rules V1, V4); exposed as ``networks.version``."""
12
+
13
+
14
+ def _kernels(value: Mapping[str, str], where: str) -> Mapping[str, str]:
15
+ """Return a read-only copy of a backend-to-kernel-source mapping."""
16
+ if not isinstance(value, Mapping):
17
+ raise TypeError(f"{where}.kernels: expected a mapping, got {type(value).__name__}")
18
+ for backend, source in value.items():
19
+ if not isinstance(backend, str) or not isinstance(source, str):
20
+ raise TypeError(f"{where}.kernels: keys and values are str (backend -> source)")
21
+ return MappingProxyType(dict(value))
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class Activation:
26
+ """An activation function of the network library.
27
+
28
+ Parameters
29
+ ----------
30
+ function : callable
31
+ Elementwise, pure and dtype-preserving map of an array; its result for an element
32
+ must not depend on the element's position, the array length, alignment or strides.
33
+ kernels : mapping of str to str, default {}
34
+ Device kernel source per backend name; without one, the device path is lost.
35
+
36
+ Raises
37
+ ------
38
+ TypeError
39
+ If ``function`` is not callable or ``kernels`` is not a mapping of str to str.
40
+
41
+ Notes
42
+ -----
43
+ Rules K2, K5 of the libraries specification. Pickles through its constructor, so that a
44
+ module keeping it can be sent to a worker process.
45
+ """
46
+
47
+ function: Callable[[np.ndarray], np.ndarray]
48
+ kernels: Mapping[str, str] = field(default_factory=dict)
49
+
50
+ def __post_init__(self) -> None:
51
+ if not callable(self.function):
52
+ raise TypeError(
53
+ f"Activation.function: expected a callable, got {type(self.function).__name__}"
54
+ )
55
+ object.__setattr__(self, "kernels", _kernels(self.kernels, "Activation"))
56
+
57
+ def __reduce__(self) -> tuple[Any, ...]:
58
+ return (Activation, (self.function, dict(self.kernels)))
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class Aggregation:
63
+ """How a node combines the weighted terms of its present incoming edges.
64
+
65
+ Parameters
66
+ ----------
67
+ combine : callable
68
+ ``(accumulator, term) -> accumulator``, elementwise; the fold starts from the first
69
+ term and follows the canonical edge order.
70
+ empty : float
71
+ Value of a node without any present incoming edge.
72
+ finalize : callable, optional
73
+ ``(accumulator, count) -> value``, applied after the fold.
74
+ kernels : mapping of str to str, default {}
75
+ Device kernel source per backend name.
76
+
77
+ Raises
78
+ ------
79
+ TypeError
80
+ If ``combine`` or ``finalize`` is not callable, ``empty`` is not a real number, or
81
+ ``kernels`` is not a mapping of str to str.
82
+
83
+ Notes
84
+ -----
85
+ Rules K2, K4, K5 and S9a of the libraries specification. Pickles through its constructor,
86
+ so that a module keeping it can be sent to a worker process.
87
+ """
88
+
89
+ combine: Callable[[np.ndarray, np.ndarray], np.ndarray]
90
+ empty: float
91
+ finalize: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None
92
+ kernels: Mapping[str, str] = field(default_factory=dict)
93
+
94
+ def __post_init__(self) -> None:
95
+ if not callable(self.combine):
96
+ raise TypeError(
97
+ f"Aggregation.combine: expected a callable, got {type(self.combine).__name__}"
98
+ )
99
+ if self.finalize is not None and not callable(self.finalize):
100
+ raise TypeError(
101
+ "Aggregation.finalize: expected a callable or None, got "
102
+ f"{type(self.finalize).__name__}"
103
+ )
104
+ if isinstance(self.empty, bool) or not isinstance(self.empty, (int, float, np.number)):
105
+ raise TypeError(
106
+ f"Aggregation.empty: expected a real number, got {type(self.empty).__name__}"
107
+ )
108
+ object.__setattr__(self, "empty", float(self.empty))
109
+ object.__setattr__(self, "kernels", _kernels(self.kernels, "Aggregation"))
110
+
111
+ def __reduce__(self) -> tuple[Any, ...]:
112
+ return (Aggregation, (self.combine, self.empty, self.finalize, dict(self.kernels)))
113
+
114
+
115
+ # Catalogue functions: built from numpy ufuncs evaluated in the array's own dtype, so that
116
+ # each element goes through one code path whatever its position (K5).
117
+
118
+
119
+ def _identity(x: np.ndarray) -> np.ndarray:
120
+ """x (a new array, so that callers never alias their input)."""
121
+ return np.positive(x)
122
+
123
+
124
+ def _tanh(x: np.ndarray) -> np.ndarray:
125
+ """tanh(x)."""
126
+ return np.tanh(x)
127
+
128
+
129
+ def _relu(x: np.ndarray) -> np.ndarray:
130
+ """max(x, 0)."""
131
+ return np.maximum(x, x.dtype.type(0))
132
+
133
+
134
+ def _sigmoid(x: np.ndarray) -> np.ndarray:
135
+ """1 / (1 + exp(-x))."""
136
+ one = x.dtype.type(1)
137
+ return one / (one + np.exp(-x))
138
+
139
+
140
+ def _neat_sigmoid(x: np.ndarray) -> np.ndarray:
141
+ """1 / (1 + exp(-4.9 x)), the steepened sigmoid of the original NEAT paper."""
142
+ one = x.dtype.type(1)
143
+ return one / (one + np.exp(x.dtype.type(-4.9) * x))
144
+
145
+
146
+ def _sin(x: np.ndarray) -> np.ndarray:
147
+ """sin(x)."""
148
+ return np.sin(x)
149
+
150
+
151
+ def _gauss(x: np.ndarray) -> np.ndarray:
152
+ """exp(-x²)."""
153
+ return np.exp(-(x * x))
154
+
155
+
156
+ def _abs(x: np.ndarray) -> np.ndarray:
157
+ """|x|."""
158
+ return np.abs(x)
159
+
160
+
161
+ def _step(x: np.ndarray) -> np.ndarray:
162
+ """1 if x > 0 else 0."""
163
+ return (x > 0).astype(x.dtype)
164
+
165
+
166
+ def _mean(acc: np.ndarray, count: np.ndarray) -> np.ndarray:
167
+ """Sum divided by the term count (0 where there is no term)."""
168
+ count = np.asarray(count).astype(acc.dtype)
169
+ return np.divide(acc, count, out=np.zeros_like(acc), where=count > 0)
170
+
171
+
172
+ ACTIVATIONS: Final[Mapping[str, Activation]] = MappingProxyType(
173
+ {
174
+ "id": Activation(_identity),
175
+ "tanh": Activation(_tanh),
176
+ "relu": Activation(_relu),
177
+ "sigmoid": Activation(_sigmoid),
178
+ "neat_sigmoid": Activation(_neat_sigmoid),
179
+ "sin": Activation(_sin),
180
+ "gauss": Activation(_gauss),
181
+ "abs": Activation(_abs),
182
+ "step": Activation(_step),
183
+ }
184
+ )
185
+ """Read-only catalogue of activation functions, dated by ``VERSION`` (rule K3)."""
186
+
187
+ AGGREGATIONS: Final[Mapping[str, Aggregation]] = MappingProxyType(
188
+ {
189
+ "sum": Aggregation(np.add, 0.0),
190
+ "product": Aggregation(np.multiply, 1.0),
191
+ "max": Aggregation(np.maximum, 0.0),
192
+ "min": Aggregation(np.minimum, 0.0),
193
+ "mean": Aggregation(np.add, 0.0, finalize=_mean),
194
+ }
195
+ )
196
+ """Read-only catalogue of aggregations, dated by ``VERSION`` (rule K4)."""
197
+
198
+ _KINDS: Final[Mapping[str, tuple[Mapping[str, object], type, str]]] = MappingProxyType(
199
+ {
200
+ "activation": (ACTIVATIONS, Activation, "activations"),
201
+ "aggregation": (AGGREGATIONS, Aggregation, "aggregations"),
202
+ }
203
+ )
204
+
205
+
206
+ def caller_definitions[T](
207
+ given: Mapping[str, T] | None, kind: str
208
+ ) -> Mapping[str, T]:
209
+ """Check the definitions a caller supplies for ``kind`` and return them (R2, K2, K6)."""
210
+ catalogue, cls, keyword = _KINDS[kind]
211
+ if given is None:
212
+ return MappingProxyType({})
213
+ if not isinstance(given, Mapping):
214
+ raise TypeError(f"{keyword}=: expected a mapping of str to {cls.__name__}, "
215
+ f"got {type(given).__name__}")
216
+ for name, definition in given.items():
217
+ if not isinstance(name, str):
218
+ raise TypeError(f"{keyword}=: names are str, got {type(name).__name__}")
219
+ if not isinstance(definition, cls):
220
+ raise TypeError(
221
+ f"{keyword}['{name}']: expected an {cls.__name__}, got "
222
+ f"{type(definition).__name__}"
223
+ )
224
+ if name in catalogue:
225
+ raise ValueError(
226
+ f"'{name}' is a catalogue name of the network library (version {VERSION}); "
227
+ "give your variant another name"
228
+ )
229
+ return MappingProxyType(dict(given))
230
+
231
+
232
+ def missing_names_message(kind: str, missing: Mapping[str, list[str]]) -> str:
233
+ """Build the K6 message for names of ``kind`` found nowhere.
234
+
235
+ ``missing`` maps each unknown name to the places it was read from, in order of appearance.
236
+ """
237
+ catalogue, cls, keyword = _KINDS[kind]
238
+ names = list(missing)
239
+ places: list[str] = []
240
+ for where in missing.values():
241
+ places.extend(w for w in where if w not in places)
242
+ quoted = ", ".join(f"'{n}'" for n in names)
243
+ supply = ", ".join(f"'{n}': {cls.__name__}(...)" for n in names)
244
+ noun = kind if len(names) == 1 else f"{kind}s"
245
+ pronoun = "it" if len(names) == 1 else "them"
246
+ return (
247
+ f"network library (version {VERSION}): unknown {noun} {quoted} in "
248
+ f"{' and '.join(places)}; the catalogue offers {list(catalogue)}; "
249
+ f"pass {keyword}={{{supply}}} to supply {pronoun}"
250
+ )
251
+
252
+
253
+ def lookup[T](name: str, kind: str, given: Mapping[str, T]) -> T | None:
254
+ """Resolve ``name``: the catalogue first, then the caller's definitions (K6)."""
255
+ catalogue = _KINDS[kind][0]
256
+ if name in catalogue:
257
+ return catalogue[name] # type: ignore[return-value]
258
+ return given.get(name)