Migrating from 0.6 to 0.7
Move code written for 0.6 to 0.7, name by name. Version 0.7 aligns the library with scikit-learn: what you define is an estimator, and what you do to it is a function. It is one breaking release, and the removed names have no aliases.
In short:
- every ready-made system is a class imported from its subfield, such as
SIRSystemfromskcomplex.systems.biology.epidemiology, in place of a factory function and a catalog name, all_systemsfinds the classes, in place ofcatalog,describe, and the registry,simulate(system, ...)runs a system and returns itsTrajectory, in place ofSimulationandcreate_simulation,- every analysis is a function that returns a frozen result, in place of an analysis class,
- every experiment is a function of a system, calibration among them,
- the compilers and most problem classes are private,
- every part has capability tags, from
get_tags(), - specs are frozen values without
prepareandrun, and estimators check their settings at the start of_prepare.
Ready-made systems
A ready-made system was a factory function registered under a catalog name. It is now a class named after the system
with the suffix System, whose constructor takes the arguments of the factory with the same defaults, then label as
the last keyword-only argument.
Before:
from skcomplex.systems import create_system, describe
system = create_system('sir', beta=0.0004)
entry = describe('sir')
entry.description, entry.tags, entry.references, entry.defaults
After:
from skcomplex.systems.biology.epidemiology import SIRSystem
system = SIRSystem(beta=0.0004)
SIRSystem.__doc__.splitlines()[0], SIRSystem.topics, SIRSystem.references, system.get_run_settings()
A system is imported from its subfield package, where the factory was, such as SIRSystem from
skcomplex.systems.biology.epidemiology in place of create_system('sir').
The catalog name of a system is the snake case of its class without System, as the gallery and the inventory use it.
For most systems the name and the factory stay as they were, lotka_volterra and LotkaVolterraSystem. These names
changed, because their class names do not round-trip:
| Name in 0.6 | Name in 0.7 | Class |
|---|---|---|
boids_3d |
boids3d |
Boids3dSystem |
carbon_13_mfa |
carbon13_mfa |
Carbon13MFASystem |
intelligent_driver_system |
intelligent_driver |
IntelligentDriverSystem |
lorenz_84 |
lorenz84 |
Lorenz84System |
lorenz_96 |
lorenz96 |
Lorenz96System |
random_energy_system |
random_energy |
RandomEnergySystem |
trap_system |
trap |
TrapSystem |
voter_system |
voter |
VoterSystem |
The catalog
| 0.6 | 0.7 |
|---|---|
catalog() |
all_systems(), which returns (name, class) pairs |
catalog(field='biology/ecology') |
all_systems(field='biology/ecology') |
catalog(tag='oscillation') |
all_systems(topics='oscillation') |
all_systems(tags={'randomness': 'stochastic'}), by capability tags |
|
describe(name) |
the class, its attributes, and its methods |
create_system(name, **parameters) |
SystemClass(**parameters) |
create_simulation(name, **parameters) |
simulate(SystemClass(**parameters)) |
register_system(...) |
a class attribute field, topics, and references, read from the source by all_systems |
SYSTEMS, SystemCatalog |
all_systems() |
SystemEntry, SystemListing |
the class itself |
skcomplex.systems.types |
removed |
SystemListing.tags, SystemEntry.tags |
the class attribute topics |
SystemEntry.description |
the first line of the docstring of the class |
SystemEntry.factory |
the class |
SystemEntry.parameters |
get_params() |
SystemEntry.defaults, settings, initial_state, observables |
get_run_settings(), from class attributes such as until and dt |
SystemEntry.formalism |
get_tags() |
create_system(name).set_params(label=...) |
SystemClass(label=...) |
A system class builds its parts in prepare, and keeps them as system_, the System that the factory returned:
SIRSystem().prepare().system_.elements
Simulations
Simulation is private. simulate takes the system and the run settings as keyword arguments, runs from time 0 to
until, and returns the trajectory. An argument left out comes from the tuned run settings of the system class.
Before:
from skcomplex.core.engine.runtime import Simulation
from skcomplex.systems import create_simulation, create_system
trajectory = create_simulation('sir').prepare().run().trajectory_
simulation = Simulation(create_system('lotka_volterra'), dt=0.1).prepare()
trajectory = simulation.run(until=50).trajectory_
After:
from skcomplex.core.engine.runtime import simulate
from skcomplex.systems.biology.ecology import LotkaVolterraSystem
from skcomplex.systems.biology.epidemiology import SIRSystem
trajectory = simulate(SIRSystem())
trajectory = simulate(LotkaVolterraSystem(), until=50, dt=0.1)
| 0.6 | 0.7 |
|---|---|
Simulation(system, **settings).prepare().run(until).trajectory_ |
simulate(system, until=until, **settings) |
Simulation(..., solver=..., backend=..., dt=..., clock=..., observers=..., schedule=..., observables=..., invariants=..., initial_state=..., random_state=..., checked=...) |
the keyword arguments of simulate with the same names |
Simulation(..., compiler=...) |
simulate(..., compiler=...), for a compiler on BaseCompiler that a system package defines |
simulation.set_params(...) before a run |
the arguments of the next simulate call |
run(until=...) |
simulate(..., until=...) |
update_parameters({path: value}) |
simulate(..., schedule={path: function_of_time}), whose values are checked against the domains of their parameters at every frame |
extend(until) |
simulate(..., initial_state=...) from the last frame of the previous trajectory |
Simulation(..., clock=RealTimeClock()) |
simulate(..., clock=RealTimeClock()), which paces the run in real time to watch it in Rerun |
start, pause, resume, stop, wait, and parameter changes while running |
removed, a run goes in the calling thread and its parameters change only by its schedule |
problem_, stepper_, time_ |
no public replacement, the trajectory holds times and the values |
clone(simulation) |
a new simulate call, since simulate never changes the system |
Validation errors that the compiler reported through PassCompiler().prepare(system).report_ are raised by simulate,
such as a RoleElementError that names the role, the state, and the fix. ParameterError now derives from
ValidationError, so except ValidationError catches every validation failure of simulate, an unknown or invalid
scheduled parameter included. A solver or a backend that does not support
the tags of a system raises a TagError before anything compiles.
Compilers and problems
simulate chooses the compiler of the form of a system, the way the tags choose the default solver, so the compilers
of the library are private.
| 0.6 | 0.7 |
|---|---|
PassCompiler, OperatorCompiler, RewritingCompiler |
private, chosen by simulate |
Compilation, FlattenPass, LayoutPass, ProblemPass, ObservableEvaluator |
private |
BaseCompiler, BasePass, BaseLowering, HilbertSpaceError |
public, for a system package that brings a form of its own through the compiler run setting |
BaseProblemSpec, ProblemSpec, FrameSpec, BaseKernel, BaseSearch, BasePrinter |
public, unchanged |
every other name of skcomplex.core.engine.problems, such as OperatorProblemSpec, RewritingProblemSpec, KineticProblemSpec, LearningProblemSpec, LinearProgramSpec, PartitionSpec, and their kernels |
private |
Analyses
Every analysis class is a function named after it in snake case without Analysis, with a clearer name where snake
case reads badly. The source is the first argument, the former constructor settings are keyword-only with the same
defaults, and the function returns a frozen result whose fields are the former fitted attributes without the trailing
underscore. A first positional setting, such as the kind, is now the keyword argument kind, law for fit_law, variables
for mode_decomposition, sparse_identification, and reservoir, shape for coarse_grain, and analysis and
attribute for surrogate_test.
Before:
from skcomplex.core.analysis import EntropyAnalysis, SymbolicAnalysis
SymbolicAnalysis().prepare(system).run().equations_
EntropyAnalysis('permutation', dimension=4).prepare(values).run().entropy_
After:
import numpy as np
from skcomplex.core.analysis import entropy, symbolic
from skcomplex.systems.biology.epidemiology import SIRSystem
values = np.sin(np.arange(500) * 0.1)
symbolic(SIRSystem()).equations
entropy(values, kind='permutation', dimension=4).entropy
BaseAnalysis is removed. An analysis given to an experiment was an unprepared instance, and is now a function or the
result of bind_analysis(function, **settings), such as bind_analysis(entropy, kind='permutation'). An experiment
names its columns <function name>.<field>, such as entropy.entropy.
| Old class | Function | Result |
|---|---|---|
AlignmentAnalysis |
alignment |
AlignmentResult |
AutocatalyticSetAnalysis |
autocatalytic_sets |
AutocatalyticSetsResult |
AutomatonAnalysis |
automaton |
AutomatonResult |
CellComplexAnalysis |
cell_complex |
CellComplexResult |
ClusterAnalysis |
clusters |
ClustersResult |
CoarseGrainAnalysis |
coarse_grain |
CoarseGrainResult |
CollapseAnalysis |
data_collapse |
DataCollapseResult |
ComplexityAnalysis |
complexity |
ComplexityResult |
CorrelationAnalysis |
correlation |
CorrelationResult |
DefectAnalysis |
defects |
DefectsResult |
DiscoveryAnalysis |
applicable_analyses |
ApplicableAnalysesResult |
DisplacementAnalysis |
displacement |
DisplacementResult |
DistributionAnalysis |
distribution |
DistributionResult |
DomainAnalysis |
domains |
DomainsResult |
ElementaryModeAnalysis |
elementary_modes |
ElementaryModesResult |
EmbeddingAnalysis |
embedding |
EmbeddingResult |
EmergenceAnalysis |
emergence |
EmergenceResult |
EntropyAnalysis |
entropy |
EntropyResult |
EpsilonMachineAnalysis |
epsilon_machine |
EpsilonMachineResult |
ErgodicityAnalysis |
ergodicity |
ErgodicityResult |
EventAnalysis |
events |
EventsResult |
FitAnalysis |
fit_law |
FitLawResult |
InformationAnalysis |
information |
InformationResult |
InvariantAnalysis |
invariants |
InvariantsResult |
IrreversibilityAnalysis |
irreversibility |
IrreversibilityResult |
KramersMoyalAnalysis |
kramers_moyal |
KramersMoyalResult |
LargeDeviationAnalysis |
large_deviations |
LargeDeviationsResult |
LinearizationAnalysis |
linearization |
LinearizationResult |
LocalInformationAnalysis |
local_information |
LocalInformationResult |
LoopAnalysis |
loops |
LoopsResult |
LyapunovAnalysis |
lyapunov |
LyapunovResult |
MarkovAnalysis |
markov |
MarkovResult |
ModeDecompositionAnalysis |
mode_decomposition |
ModeDecompositionResult |
MotifAnalysis |
motifs |
MotifsResult |
NeighborAnalysis |
neighbors |
NeighborsResult |
NetworkAnalysis |
network_measures |
NetworkMeasuresResult |
OperatorAnalysis |
operator_measures |
OperatorMeasuresResult |
PartialInformationAnalysis |
partial_information |
PartialInformationResult |
PassageAnalysis |
passage |
PassageResult |
RecurrenceAnalysis |
recurrence |
RecurrenceResult |
RenormalizationAnalysis |
renormalization |
RenormalizationResult |
ReservoirAnalysis |
reservoir |
ReservoirResult |
RollingAnalysis |
rolling |
RollingResult |
ScalingAnalysis |
scaling |
ScalingResult |
SectionAnalysis |
section |
SectionResult |
ShapeAnalysis |
chain_shape |
ChainShapeResult |
SignatureAnalysis |
signatures |
SignaturesResult |
SparseIdentificationAnalysis |
sparse_identification |
SparseIdentificationResult |
SpectrumAnalysis |
spectrum |
SpectrumResult |
StabilityAnalysis |
stability |
StabilityResult |
SurrogateTestAnalysis |
surrogate_test |
SurrogateTestResult |
SymbolicAnalysis |
symbolic |
SymbolicResult |
SensitivityAnalysis (experiments) |
sensitivity |
SensitivityResult |
Field realizations moved from analysis classes of the system packages to functions of the same subfields:
| Old class | Subfield of skcomplex.systems |
Function | Result |
|---|---|---|---|
EarlyWarningAnalysis |
physics.climate |
early_warning |
EarlyWarningResult |
CracklingNoiseAnalysis |
physics.criticality |
crackling_noise |
CracklingNoiseResult |
KineticTemperatureAnalysis |
physics.statistical_mechanics |
kinetic_temperature |
KineticTemperatureResult |
MaxwellBoltzmannAnalysis |
physics.statistical_mechanics |
maxwell_boltzmann |
MaxwellBoltzmannResult |
SusceptibilityAnalysis |
physics.statistical_mechanics |
susceptibility |
SusceptibilityResult |
GaugeAnalysis |
physics.waves |
gauge_observables |
GaugeObservablesResult |
FuzzyOilDropAnalysis |
biology.systems_biology |
fuzzy_oil_drop_measures |
FuzzyOilDropMeasuresResult |
LabellingAnalysis |
biology.systems_biology |
labelling |
LabellingResult |
FluxVariabilityAnalysis |
biology.common.metabolism |
flux_variability |
FluxVariabilityResult |
ProductionScanAnalysis |
biology.common.metabolism |
production_scan |
ProductionScanResult |
CobraAnalysis |
biology.common.metabolism |
cobra_measures |
CobraMeasuresResult |
StrainDesignAnalysis |
biology.common.metabolism |
strain_design |
StrainDesignResult |
CommunityAnalysis |
biology.common.metabolism |
community_growth |
CommunityGrowthResult |
NeutralNetworkAnalysis |
biology.evolution |
neutral_network |
NeutralNetworkResult |
Experiments
Every experiment class is a function of a system, in the role of the model selection functions of scikit-learn. The system is the first argument, a system instance or a factory of systems such as a system class, and the results are frozen values.
Before:
from skcomplex.core.experiments import CalibrationExperiment, Experiment, GridDesign
from skcomplex.systems import describe
sir = describe('sir').factory
result = Experiment(sir, GridDesign(), {'gamma': [0.1, 0.2]}, until=60).prepare().run().result_
fit = CalibrationExperiment(sir, data, bounds).prepare().run()
fit.estimates_
After:
import numpy as np
from skcomplex.core.engine.runtime import simulate
from skcomplex.core.experiments import calibrate, sweep
from skcomplex.systems.biology.epidemiology import SIRSystem
result = sweep(SIRSystem(), {'gamma': [0.1, 0.2]}, until=60, outputs=['I.n'])
times = np.arange(0.0, 60.0, 5.0)
data = {'I.n': (times, simulate(SIRSystem(), until=60).values('I.n')[::5][: len(times)])}
fit = calibrate(SIRSystem(), data, {'gamma': (0.05, 0.2)})
fit.estimates, fit.system.gamma
| Old name | New name | Result |
|---|---|---|
Experiment(source, design, bounds) |
sweep(system, parameters, design=...) |
ExperimentResult |
Experiment(source, replicates=n) |
ensemble(system, replicates=n) |
ExperimentResult |
Experiment(source, scenarios=...) |
compare_scenarios(system, scenarios) |
ExperimentResult |
GridDesign() |
sweep(..., design='grid'), the default |
|
RowsDesign(rows) |
sweep(system, rows) |
|
RandomDesign(n) |
sweep(..., design='random', n=n) |
|
LatinHypercubeDesign(n) |
sweep(..., design='latin_hypercube', n=n) |
|
SobolDesign(n) |
sweep(..., design='sobol', n=n) |
|
OneAtATimeDesign() |
sensitivity(..., method='oat') |
SensitivityResult |
MorrisDesign(n) |
sensitivity(..., method='morris', n=n) |
SensitivityResult |
SaltelliDesign(n) |
sensitivity(..., method='sobol', n=n) |
SensitivityResult |
sensitivity(result, ...) |
sensitivity(system, bounds, output=...) |
SensitivityResult |
BasinStabilityExperiment |
basin_stability |
BasinStabilityResult |
OrbitDiagramExperiment |
orbit_diagram |
OrbitDiagramResult |
CalibrationExperiment |
calibrate, fitting model parameters |
CalibrationResult, with the fitted system |
ExperimentResult |
ExperimentResult, now a frozen value |
|
BaseExperiment, BaseDesign |
removed, the shared runs are private |
Tags
Every part has capability tags, a frozen Tags of skcomplex.core.specs from get_tags(): its time, its domains,
its space, the kinds of its laws, its randomness, and its structure. The tags of a system are derived from its parts.
They replace the hand-written vocabulary of time, space, laws, and domains in the inventory, and the compatibility
page, which the generated pages under Parts replace. simulate, the analyses, and the experiments read them and raise
a TagError that names a tag they cannot use.
from skcomplex.systems.biology.epidemiology import SIRSystem
SIRSystem().get_tags()
Specs and estimators
A spec, such as StateSpec, RoleSpec, or Tags, is a frozen value. It is no longer a configurable object, so it has
neither prepare nor run, and get_params of the part that holds it no longer lists its fields. The class that owns
a spec checks it through the private hook _check when it reads it, and a spec is changed with dataclasses.replace.
An estimator checks its settings at the start of its _prepare, before it sets any attribute that ends in _. The
hooks _check and _validated of BaseConfig are removed. A custom part starts its _prepare with
super()._prepare(context), so the base of the parts checks its label and declared specs first. A solver prepared
without its compiled problem raises ContextError, a ValidationError, after it checks its own settings. Since every
part checks its arguments when it is prepared, a system whose third part is invalid may have its first two parts
prepared when the error is raised, and preparing it again replaces their attributes.
Before:
from skcomplex.core.components.elements import Element
class StockElement(Element):
def __init__(self, label, n=1.0):
super().__init__(label)
self.n = n
def _check(self):
if self.n < 0:
raise ValueError('n must not be negative.')
StateSpec(order=2).prepare()
After:
from skcomplex.core.components.elements import Element
from skcomplex.core.specs import StateSpec
class StockElement(Element):
def __init__(self, label, n=1.0):
super().__init__(label)
self.n = n
def _prepare(self, context=None):
super()._prepare(context)
if self.n < 0:
error_msg = 'n must not be negative.'
raise ValueError(error_msg)
StockElement('stock').prepare()
StateSpec(order=2)._check()
| Old name | New name |
|---|---|
spec.prepare(), spec.run() |
removed, the owner of a spec calls spec._check() |
_check of an estimator |
the start of its _prepare |
_validated() |
prepare(...) for an estimator, _check() for a spec |
Calibration(system, data, bounds).prepare().run().system_ |
calibrate(system, data, bounds).system |
ValidationError of a solver without a problem |
ContextError, a ValidationError |