Skip to content

Systems

Ready-made systems of every science, built from the core and organized by field and subfield.

SYSTEMS = SystemCatalog(Path(str(files('skcomplex.systems')))) module-attribute

The catalog of the systems of the library and of the systems registered at run time.

SystemCatalog(root, package='skcomplex.systems')

Catalog of the systems of a package: their listings read from source, and their entries once registered.

The catalog lists every factory that a module of the package decorates with register_system, reading the registrations from the sources without importing them. Asking for the entry of a listed system imports its module, whose registration adds the entry. Systems registered at run time outside the package are listed too.

Parameters:

Name Type Description Default
root Path

The directory of the system package, such as the directory of skcomplex.systems.

required
package str

The dotted name of the system package.

'skcomplex.systems'

Examples:

>>> from importlib.resources import files
>>> from pathlib import Path
>>> from skcomplex.systems import SystemCatalog
>>> catalog = SystemCatalog(Path(str(files('skcomplex.systems'))))
>>> catalog.listings(field='biology/ecology')[0].field
'biology/ecology'
Source code in src/skcomplex/systems/_system_catalog.py
43
44
45
46
47
def __init__(self, root: Path, package: str = 'skcomplex.systems') -> None:
    self.root = root
    self.package = package
    self._entries: dict[str, SystemEntry] = {}
    self._scanned: dict[str, SystemListing] | None = None

entry(name)

Return a system by name, importing the module of a listed system so that it registers.

Parameters:

Name Type Description Default
name str

The name of the system.

required

Returns:

Name Type Description
entry SystemEntry

The registered system.

Raises:

Type Description
RegistryError

No system has the name, with the closest names in the message.

Source code in src/skcomplex/systems/_system_catalog.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def entry(self, name: str) -> SystemEntry:
    """Return a system by name, importing the module of a listed system so that it registers.

    Args:
        name:
            The name of the system.

    Returns:
        entry:
            The registered system.

    Raises:
        RegistryError:
            No system has the name, with the closest names in the message.
    """
    if name not in self._entries:
        listing = self._listed().get(name)
        if listing is not None:
            importlib.import_module(listing.module)
    if name not in self._entries:
        known = sorted({*self._listed(), *self._entries})
        close = difflib.get_close_matches(name, known, n=3)
        hint = f' Did you mean {", ".join(close)}?' if close else ' List the systems with `catalog()`.'
        error_msg = f'No system is registered as {name!r}.{hint}'
        raise RegistryError(error_msg)
    return self._entries[name]

listings(field=None, tag=None)

Return the listed systems, optionally of one field or with one tag, sorted by field and name.

Parameters:

Name Type Description Default
field str | None

The field, such as biology, or the field path, such as biology/ecology, to keep, or None for every field.

None
tag str | None

The tag to keep, or None for every system.

None

Returns:

Name Type Description
listings list[SystemListing]

The listed systems.

Raises:

Type Description
RegistryError

A system registers its name, field, description, tags, or references with a value computed at run time.

Source code in src/skcomplex/systems/_system_catalog.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def listings(self, field: str | None = None, tag: str | None = None) -> list[SystemListing]:
    """Return the listed systems, optionally of one field or with one tag, sorted by field and name.

    Args:
        field:
            The field, such as `biology`, or the field path, such as `biology/ecology`, to keep, or `None` for
            every field.

        tag:
            The tag to keep, or `None` for every system.

    Returns:
        listings:
            The listed systems.

    Raises:
        RegistryError:
            A system registers its name, field, description, tags, or references with a value computed at run time.
    """
    listings = dict(self._listed())
    for name, entry in self._entries.items():
        listings.setdefault(
            name,
            SystemListing(
                entry.name,
                entry.field,
                entry.description,
                entry.tags,
                entry.references,
                entry.factory.__module__,
            ),
        )
    return sorted(
        (
            listing
            for listing in listings.values()
            if (field is None or listing.field == field or listing.field.startswith(f'{field}/'))
            and (tag is None or tag in listing.tags)
        ),
        key=lambda listing: (listing.field, listing.name),
    )

register(entry)

Register a system under its name, replacing any previous one.

An entry without a description takes the first line of its factory's docstring, without a leading Return.

Parameters:

Name Type Description Default
entry SystemEntry

The registered system.

required
Source code in src/skcomplex/systems/_system_catalog.py
49
50
51
52
53
54
55
56
57
58
59
60
def register(self, entry: SystemEntry) -> None:
    """Register a system under its name, replacing any previous one.

    An entry without a description takes the first line of its factory's docstring, without a leading `Return`.

    Args:
        entry:
            The registered system.
    """
    if not entry.description:
        entry = dataclasses.replace(entry, description=self._summarize(inspect.getdoc(entry.factory) or ''))
    self._entries[entry.name] = entry

SystemEntry(name, factory, field, description, tags=(), references=(), defaults=dict(), observables=None, initial_state=None, settings=None) dataclass

Registered system: a factory with its metadata and default run settings.

Parameters:

Name Type Description Default
name str

The name the system is created by.

required
factory Factory

The factory that builds the system.

required
field str

The field path of the system, its science and subfield, such as biology/ecology.

required
description str

One sentence on what the system shows.

required
tags tuple[str, ...]

Keywords for filtering.

()
references tuple[str, ...]

The sources of the system.

()
defaults Mapping[str, object]

The default run settings: until and any Simulation argument such as dt or solver.

dict()
observables Callable[[System], Sequence[ObservableSpec]] | None

A function of the built system that returns its standard observables, or None for none.

None
initial_state Callable[..., Mapping[str, object] | None] | None

A function of the factory's arguments that returns the initial values run gives the simulation, by dotted state name, or None for the elements' own.

None
settings Callable[..., Mapping[str, object]] | None

A function of the factory's arguments that returns the run settings that replace defaults for these arguments, or None.

None

formalism property

Mathematical form of the system with its default parameters, inferred by compiling it once.

parameters property

Parameters of the factory with their defaults.

initial_state_of(**parameters)

Return the initial values run gives the simulation for arguments of the factory.

Parameters:

Name Type Description Default
**parameters object

Arguments of the factory that replace its defaults.

{}

Returns:

Name Type Description
initial_state dict[str, object]

The initial values by dotted state name, empty for the elements' own.

Source code in src/skcomplex/systems/_system_entry.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def initial_state_of(self, **parameters: object) -> dict[str, object]:
    """Return the initial values `run` gives the simulation for arguments of the factory.

    Args:
        **parameters:
            Arguments of the factory that replace its defaults.

    Returns:
        initial_state:
            The initial values by dotted state name, empty for the elements' own.
    """
    if self.initial_state is None:
        return {}
    return dict(self.initial_state(**{**self.parameters, **parameters}) or {})

observables_of(system)

Return the standard observables of a system of the system, which run records.

Parameters:

Name Type Description Default
system System

A system the system's factory returned.

required

Returns:

Name Type Description
observables list[ObservableSpec]

The observables, labelled by their path from the root.

Source code in src/skcomplex/systems/_system_entry.py
141
142
143
144
145
146
147
148
149
150
151
152
def observables_of(self, system: System) -> list[ObservableSpec]:
    """Return the standard observables of a system of the system, which `run` records.

    Args:
        system:
            A system the system's factory returned.

    Returns:
        observables:
            The observables, labelled by their path from the root.
    """
    return [] if self.observables is None else list(self.observables(system))

settings_of(**parameters)

Return the default run settings for arguments of the factory.

Parameters:

Name Type Description Default
**parameters object

Arguments of the factory that replace its defaults.

{}

Returns:

Name Type Description
settings dict[str, object]

The run settings, defaults replaced by what settings returns for the arguments.

Source code in src/skcomplex/systems/_system_entry.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def settings_of(self, **parameters: object) -> dict[str, object]:
    """Return the default run settings for arguments of the factory.

    Args:
        **parameters:
            Arguments of the factory that replace its defaults.

    Returns:
        settings:
            The run settings, `defaults` replaced by what `settings` returns for the arguments.
    """
    if self.settings is None:
        return dict(self.defaults)
    return {**self.defaults, **self.settings(**{**self.parameters, **parameters})}

SystemListing(name, field, description, tags, references, module) dataclass

System as the catalog lists it: its name and metadata, and the module that registers it.

Parameters:

Name Type Description Default
name str

The name the system is created by.

required
field str

The field path of the system, such as biology/ecology.

required
description str

One sentence on what the system shows.

required
tags tuple[str, ...]

Keywords for filtering.

required
references tuple[str, ...]

The sources of the system.

required
module str

The dotted name of the module that registers the system.

required

catalog(field=None, tag=None)

Return the listed systems, optionally of one field or with one tag, sorted by field and name.

The listing reads the registration of every system of the library from its source, and imports no system module. Systems registered at run time outside the library are listed too.

Parameters:

Name Type Description Default
field str | None

The field, such as biology, or the field path, such as biology/ecology, to keep, or None for every field.

None
tag str | None

The tag to keep, or None for every system.

None

Returns:

Name Type Description
listings list[SystemListing]

The listed systems.

Raises:

Type Description
RegistryError

A system registers its name, field, description, tags, or references with a value computed at run time.

Examples:

>>> from skcomplex.systems import catalog
>>> 'lotka_volterra' in [listing.name for listing in catalog(field='biology/ecology')]
True
>>> sorted({listing.field for listing in catalog(field='chemistry')})
['chemistry/kinetics', 'chemistry/pattern_formation']
Source code in src/skcomplex/systems/_catalog.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def catalog(field: str | None = None, tag: str | None = None) -> list[SystemListing]:
    """Return the listed systems, optionally of one field or with one tag, sorted by field and name.

    The listing reads the registration of every system of the library from its source, and imports no system module.
    Systems registered at run time outside the library are listed too.

    Args:
        field:
            The field, such as `biology`, or the field path, such as `biology/ecology`, to keep, or `None` for every
            field.

        tag:
            The tag to keep, or `None` for every system.

    Returns:
        listings:
            The listed systems.

    Raises:
        RegistryError:
            A system registers its name, field, description, tags, or references with a value computed at run time.

    Examples:
        >>> from skcomplex.systems import catalog
        >>> 'lotka_volterra' in [listing.name for listing in catalog(field='biology/ecology')]
        True
        >>> sorted({listing.field for listing in catalog(field='chemistry')})
        ['chemistry/kinetics', 'chemistry/pattern_formation']
    """
    return SYSTEMS.listings(field, tag)

create_simulation(name, **parameters)

Create the simulation of a system with its default run settings, ready to prepare and run.

It imports the system's module and nothing else of the library. The simulation holds the system as its system, the system's default until, time step, solver, observables and initial state, and the seed random_state=0. Change any of them with set_params before preparing it.

Parameters:

Name Type Description Default
name str

The name of the system.

required
**parameters object

Arguments of the factory that replace its defaults.

{}

Returns:

Name Type Description
simulation Simulation

The unprepared simulation of the system.

Raises:

Type Description
RegistryError

No system has the name.

ValidationError

The system's setting initial_state is not a mapping.

ArgumentError

A default setting of the system is not an argument of Simulation.

Examples:

>>> from skcomplex.systems import create_simulation
>>> simulation = create_simulation('logistic_growth')
>>> simulation.until
20.0
>>> simulation.prepare().run().time_
20.0
Source code in src/skcomplex/systems/_catalog.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def create_simulation(name: str, **parameters: object) -> Simulation:
    """Create the simulation of a system with its default run settings, ready to prepare and run.

    It imports the system's module and nothing else of the library. The simulation holds the system as its `system`,
    the system's default `until`, time step, solver, observables and initial state, and the seed `random_state=0`.
    Change any of them with `set_params` before preparing it.

    Args:
        name:
            The name of the system.

        **parameters:
            Arguments of the factory that replace its defaults.

    Returns:
        simulation:
            The unprepared simulation of the system.

    Raises:
        RegistryError:
            No system has the name.

        ValidationError:
            The system's setting `initial_state` is not a mapping.

        ArgumentError:
            A default setting of the system is not an argument of `Simulation`.

    Examples:
        >>> from skcomplex.systems import create_simulation
        >>> simulation = create_simulation('logistic_growth')
        >>> simulation.until
        20.0
        >>> simulation.prepare().run().time_
        20.0
    """
    entry = SYSTEMS.entry(name)
    chosen: dict[str, object] = {'random_state': 0, 'until': 10.0, **entry.settings_of(**parameters)}
    initial_state = chosen.pop('initial_state', None) or {}
    if not isinstance(initial_state, Mapping):
        error_msg = f'The run setting `initial_state` of {name} is {initial_state!r}. Give it as a mapping.'
        raise ValidationError(error_msg)
    system = entry.factory(**parameters)
    initial = {**entry.initial_state_of(**parameters), **initial_state}
    simulation = Simulation(system, observables=entry.observables_of(system), initial_state=initial)
    unknown = sorted(set(chosen) - set(simulation.get_params(deep=False)))
    if unknown:
        error_msg = f'The system {name} has a run setting {unknown[0]!r}, which is not an argument of `Simulation`.'
        raise ArgumentError(error_msg)
    return simulation.set_params(**chosen)

create_system(name, **parameters)

Return a ready-made system, built by its factory.

It imports the system's module and nothing else of the library.

Parameters:

Name Type Description Default
name str

The name of the system.

required
**parameters object

Arguments of the factory that replace its defaults.

{}

Returns:

Name Type Description
system System

The built system.

Raises:

Type Description
RegistryError

No system has the name.

Examples:

>>> from skcomplex.systems import create_system
>>> len(create_system('lotka_volterra').elements)
2
Source code in src/skcomplex/systems/_catalog.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def create_system(name: str, **parameters: object) -> System:
    """Return a ready-made system, built by its factory.

    It imports the system's module and nothing else of the library.

    Args:
        name:
            The name of the system.

        **parameters:
            Arguments of the factory that replace its defaults.

    Returns:
        system:
            The built system.

    Raises:
        RegistryError:
            No system has the name.

    Examples:
        >>> from skcomplex.systems import create_system
        >>> len(create_system('lotka_volterra').elements)
        2
    """
    return SYSTEMS.entry(name).factory(**parameters)

describe(name)

Return a system by name, with its factory, parameters, metadata, and default run settings.

It imports the system's module and nothing else of the library.

Parameters:

Name Type Description Default
name str

The name of the system.

required

Returns:

Name Type Description
entry SystemEntry

The registered system.

Raises:

Type Description
RegistryError

No system has the name.

Examples:

>>> from skcomplex.systems import describe
>>> describe('lotka_volterra').field
'biology/ecology'
Source code in src/skcomplex/systems/_catalog.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def describe(name: str) -> SystemEntry:
    """Return a system by name, with its factory, parameters, metadata, and default run settings.

    It imports the system's module and nothing else of the library.

    Args:
        name:
            The name of the system.

    Returns:
        entry:
            The registered system.

    Raises:
        RegistryError:
            No system has the name.

    Examples:
        >>> from skcomplex.systems import describe
        >>> describe('lotka_volterra').field
        'biology/ecology'
    """
    return SYSTEMS.entry(name)

import_extra(module, system, extra)

Return a module of an optional extra, or raise an error naming the system, the extra, and the install command.

Parameters:

Name Type Description Default
module str

The dotted name of the module.

required
system str

The name of the system that needs the module.

required
extra str

The name of the optional extra that holds the module.

required

Returns:

Name Type Description
module ModuleType

The imported module.

Raises:

Type Description
MissingExtraError

The module is not installed.

Examples:

>>> from skcomplex.systems import import_extra
>>> import_extra('math', 'pendulum', 'none').sqrt(4.0)
2.0
Source code in src/skcomplex/systems/_extras.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def import_extra(module: str, system: str, extra: str) -> ModuleType:
    """Return a module of an optional extra, or raise an error naming the system, the extra, and the install command.

    Args:
        module:
            The dotted name of the module.

        system:
            The name of the system that needs the module.

        extra:
            The name of the optional extra that holds the module.

    Returns:
        module:
            The imported module.

    Raises:
        MissingExtraError:
            The module is not installed.

    Examples:
        >>> from skcomplex.systems import import_extra
        >>> import_extra('math', 'pendulum', 'none').sqrt(4.0)
        2.0
    """
    try:
        return import_module(module)
    except ImportError as error:
        error_msg = (
            f"The system {system} needs the extra '{extra}'. Install it with "
            f"`pip install 'scikit-complexity[{extra}]'`."
        )
        raise MissingExtraError(error_msg) from error

register_system(name=None, *, field, description=None, tags=(), references=(), defaults=None, settings=None, observables=None, initial_state=None)

Return a decorator that registers a factory as a ready-made system.

Parameters:

Name Type Description Default
name str | None

The name of the system, or None for the factory's name.

None
field str

The field path of the system, its science and subfield, such as biology/ecology.

required
description str | None

One sentence on what the system shows, or None for the first line of the factory's docstring.

None
tags Sequence[str]

Keywords for filtering.

()
references Sequence[str]

The sources of the system.

()
defaults Mapping[str, object] | None

The default run settings: until and any Simulation argument such as dt or solver.

None
settings Callable[..., Mapping[str, object]] | None

A function of the factory's arguments that returns the run settings that replace defaults for these arguments, or None.

None
observables Callable[[System], Sequence[ObservableSpec]] | None

A function of the built system that returns its standard observables, which create_simulation records, or None.

None
initial_state Callable[..., Mapping[str, object] | None] | None

A function of the factory's arguments that returns the initial values run gives the simulation, by dotted state name, or None.

None

Returns:

Name Type Description
decorator Callable[[Factory], Factory]

The decorator, which returns the factory unchanged.

Raises:

Type Description
ValidationError

A default run setting is unknown, or a factory argument has no default.

Examples:

>>> from typing import Annotated
>>> from skcomplex.core.components.elements import Element
>>> from skcomplex.core.specs import StateSpec
>>> from skcomplex.core.components.system import System
>>> from skcomplex.systems import create_system, register_system
>>> class PopulationElement(Element):
...     def __init__(self, label, n: Annotated[float, StateSpec()] = 1.0):
...         super().__init__(label)
...         self.n = n
>>> @register_system(field='examples/demo', references=('This docstring',), defaults={'until': 5})
... def constant_population(n: float = 3.0) -> System:
...     '''Return one population that does not change.'''
...     return System(elements=[PopulationElement('a', n=n)])
>>> create_system('constant_population').elements[0].n
3.0
Source code in src/skcomplex/systems/_catalog.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def register_system(
    name: str | None = None,
    *,
    field: str,
    description: str | None = None,
    tags: Sequence[str] = (),
    references: Sequence[str] = (),
    defaults: Mapping[str, object] | None = None,
    settings: Callable[..., Mapping[str, object]] | None = None,
    observables: Callable[[System], Sequence[ObservableSpec]] | None = None,
    initial_state: Callable[..., Mapping[str, object] | None] | None = None,
) -> Callable[[Factory], Factory]:
    """Return a decorator that registers a factory as a ready-made system.

    Args:
        name:
            The name of the system, or `None` for the factory's name.

        field:
            The field path of the system, its science and subfield, such as `biology/ecology`.

        description:
            One sentence on what the system shows, or `None` for the first line of the factory's docstring.

        tags:
            Keywords for filtering.

        references:
            The sources of the system.

        defaults:
            The default run settings: `until` and any `Simulation` argument such as `dt` or `solver`.

        settings:
            A function of the factory's arguments that returns the run settings that replace `defaults` for these
            arguments, or `None`.

        observables:
            A function of the built system that returns its standard observables, which `create_simulation` records, or
            `None`.

        initial_state:
            A function of the factory's arguments that returns the initial values `run` gives the simulation, by dotted
            state name, or `None`.

    Returns:
        decorator:
            The decorator, which returns the factory unchanged.

    Raises:
        ValidationError:
            A default run setting is unknown, or a factory argument has no default.

    Examples:
        >>> from typing import Annotated
        >>> from skcomplex.core.components.elements import Element
        >>> from skcomplex.core.specs import StateSpec
        >>> from skcomplex.core.components.system import System
        >>> from skcomplex.systems import create_system, register_system
        >>> class PopulationElement(Element):
        ...     def __init__(self, label, n: Annotated[float, StateSpec()] = 1.0):
        ...         super().__init__(label)
        ...         self.n = n
        >>> @register_system(field='examples/demo', references=('This docstring',), defaults={'until': 5})
        ... def constant_population(n: float = 3.0) -> System:
        ...     '''Return one population that does not change.'''
        ...     return System(elements=[PopulationElement('a', n=n)])
        >>> create_system('constant_population').elements[0].n
        3.0
    """

    def decorator(factory: Factory) -> Factory:
        entry = SystemEntry(
            name or factory.__name__,
            factory,
            field,
            description or '',
            tuple(tags),
            tuple(references),
            dict(defaults or {}),
            observables,
            initial_state,
            settings,
        )
        SYSTEMS.register(entry._validated())
        return factory

    return decorator