Skip to main content

Modelling autonomy dynamics over the Autonometrics atlas.

Project description

Autodynamics

Layer 2 of 3 in the autonomy research trilogy: Autonometrics (measure) -> Autodynamics (explain) -> Ex-Machina (build / emulate)

Status: Pre-alpha. Recording substrate, algebra of trajectories, generic adapters, Granger-causal coupling primitives, and containment-envelope primitives. No theoretical model is claimed.

Vision

Autonometrics quantifies where a system sits on the five autonomy axes (closure, memory, constraint closure, persistence, coherence). This package gives mechanical support for working with how those measurements change over time: record ordered profiles, take differences, and summarise motion in the atlas with explicit primitives. It does not ship a dynamical theory (attractors, phase structure, or predictive claims about autonomy). Interpretation of trajectories as evidence for such a theory is out of scope of the public API as it stands; the code is a reproducible toolkit, not a proof.

What this package contains today

A recording substrate, a small algebra of trajectories, and two generic input adapters. Together they turn "a sequence of autonomy measurements" into "a trajectory in a metric space, queryable axis by axis".

  • ProfileTrajectory stores a sequence of AutonomyProfile values and exposes axis-wise time series, pairwise deltas, total path length, and the algebra primitives below.
  • Algebra primitives: velocities, accelerations, drift, volatility, path_length_per_axis, rolling_mean, rolling_std, and a one-shot per-axis summary.
  • Adapters: CSVTrajectoryAdapter (load from a CSV with canonical axis columns) and BatchTrajectoryAdapter (build several parallel trajectories from grouped profiles, with cross-group mean_summary).
  • Granger-causal coupling: granger_graph (directed pairwise Granger graph over admitted axes), CausalCouplingGraph, and four scalar diagnostics (symmetry_ratio, density, max_in_strength, max_out_strength).
  • Containment envelope: Envelope (per-axis admissible region), Envelope.from_trajectory (Shewhart 2-sigma calibration) and the trinary verdict ContainmentVerdict (INSIDE / OUTSIDE / UNDEFINED).
  • Pre-registered boundary regimes and a saturation theorem documented in docs/TRAJECTORY_DIAGNOSTICS.md; the coupling protocol is pre-registered in docs/COUPLING_DIAGNOSTICS.md; the envelope containment protocol in docs/ENVELOPE_DIAGNOSTICS.md.

Install with: pip install autodynamics Import as: import autodynamics

Quick run

pip install autodynamics
autodynamics-demo --n-states-list 3 4 5 6 8 --n-steps 600
autodynamics-demo --n-states-list 3 4 5 6 8 --n-steps 600 --report summary

The first command prints a small table of (closure, memory) profiles measured over a sweep of SimpleAutomaton configurations, the consecutive deltas between them, and the total path length. The second prints a per-axis summary instead of the deltas.

Toy demo: ProfileTrajectory

Disclaimer. This is the recording substrate of Autodynamics, not its theory. The trajectory class lets you collect, traverse, and compute simple geometric quantities over a sequence of AutonomyProfiles. It does not interpret what those movements mean — assigning meaning is outside the scope of the public API. Treat the code as a reproducible toolkit or template, not as evidence for a larger claim.

import autonometrics as anm
from autodynamics import ProfileTrajectory

trajectory = ProfileTrajectory(axes=("closure", "memory"))

for n_states in [3, 4, 5, 6, 8]:
    sys = anm.SimpleAutomaton.demo(n_states=n_states, n_steps=600)
    sys.run()
    profile = anm.measure(sys, axes=["closure", "memory"])
    trajectory.append(profile)

print(trajectory.axis_series("closure"))    # time series of one axis
print(trajectory.deltas())                  # pairwise consecutive movements
print(trajectory.total_path_length())       # sum of delta magnitudes

Trajectory algebra

Beyond the substrate, the package exposes an algebra of trajectories (velocity-style differences, rolling statistics, per-axis summary). Every primitive is mosaic-dropout fielty: None propagates through differences, but never aborts aggregations.

from autodynamics import ProfileTrajectory

trajectory = ProfileTrajectory(axes=("closure", "memory"))
# ... append profiles as above ...

trajectory.velocities("closure")        # first differences, axis by axis
trajectory.accelerations("closure")     # second differences
trajectory.drift("closure")             # net change between first and last defined values
trajectory.volatility("closure")        # sample std of the velocities
trajectory.path_length_per_axis()       # sum of |velocity| per axis
trajectory.rolling_mean("closure", window=10)  # right-aligned rolling mean
trajectory.rolling_std("closure", window=10)   # right-aligned rolling sample std
trajectory.summary()                    # per-axis report: n_total, n_defined, mean, std, drift, volatility, path_length

Calling a primitive without an axis argument returns a dict over every axis configured on the trajectory; calling it with a canonical axis name returns the value for that axis directly. The full list of boundary regimes and the saturation theorem are pre-registered in docs/TRAJECTORY_DIAGNOSTICS.md.

Adapters

Two generic adapters open input paths into ProfileTrajectory without introducing calibration or threshold tuning of their own.

CSV adapter

from autodynamics import CSVTrajectoryAdapter

adapter = CSVTrajectoryAdapter()
trajectory = adapter.load_path("path/to/profiles.csv")
print(trajectory.summary())

The adapter reads columns closure, memory, constraint, persistence, coherence (any subset is fine; missing columns yield fully-undefined axes). Empty or whitespace-only cells become None. Extra columns (class, params, seed, notes, ...) are ignored. Pass order_column="step" (or any integer-valued column name) to sort rows numerically before constructing snapshots.

Batch adapter

from autodynamics import BatchTrajectoryAdapter
import autonometrics as anm

batch = BatchTrajectoryAdapter()
for params in benchmark_configurations:
    for seed in range(K):
        system = build_system(params, seed)
        system.run()
        profile = anm.measure(system)
        batch.add(params, profile)

trajectories = batch.trajectories()        # one ProfileTrajectory per group key
mean_summary = batch.mean_summary()        # per-axis cross-group means of summary metrics

A reproducible end-to-end example using both adapters over a public fixture is shipped at examples/trajectory_demo.py.

Coupling analysis

Beyond intra-axis algebra, the package exposes a cross-axis primitive: a directed Granger-causal coupling graph between every pair of admitted axes of a ProfileTrajectory, with a stationarity gate (Augmented Dickey-Fuller), mosaic-dropout-fielty axis admission, and four scalar diagnostics. The implementation is the standard pairwise Granger test (Granger 1969; Sims 1980; Lütkepohl 2005) applied uniformly to the axes of the autonomy atlas, packaged so it composes with the rest of the algebra without bespoke calibration.

from autodynamics import (
    granger_graph, density, symmetry_ratio,
    max_in_strength, max_out_strength,
)

graph = granger_graph(trajectory)
# Also accepts a Mapping[str, Sequence[float | None]]:
# graph = granger_graph({"closure": [...], "memory": [...]})

graph.axes_used                                # admitted axes
graph.excluded_axes                            # axis -> rejection reason
graph.edge("closure", "memory").f_stat         # F-stat, lag, p-value, status

symmetry_ratio(graph)                          # average min/max F over pairs
density(graph)                                 # fraction of edges above F critical
max_in_strength(graph, "memory")               # max incoming F-stat
max_out_strength(graph, "closure")             # max outgoing F-stat

The protocol (length gate, ADF + up to two differences, AIC-selected VAR up to max_lag, F-test, mosaic-dropout-fielty axis admission) is pre-registered in docs/COUPLING_DIAGNOSTICS.md. It is not a claim that any axis Granger-causes any other axis on any specific Autonometrics zoo; it is a composable primitive that can be fed real or synthetic trajectories without bespoke threshold tuning.

Envelope checks

A per-axis admissible region in profile space and a trinary containment verdict (INSIDE / OUTSIDE / UNDEFINED). The verdict is intentionally distinct from boolean: False would silently equate "outside the region" with "the profile did not report on this axis", which collapses the mosaic-dropout fielty preserved everywhere else in the package. Envelope.from_trajectory learns per-axis bounds from a reference trajectory using the Shewhart (1931) control-limit recipe applied independently per axis.

from autodynamics import (
    Envelope, ContainmentVerdict, ContainmentResult,
)

# Direct construction with explicit bounds
envelope = Envelope(bounds={
    "closure":    (0.4, 0.9),
    "memory":     (0.0, 0.6),
    "constraint": (0.0, 1.0),
})

# Or learn bounds from a reference trajectory
envelope = Envelope.from_trajectory(
    reference_trajectory,
    width_multiplier=2.0,            # Shewhart 1931
    axes=("closure", "memory"),       # optional; default = all
)

# Evaluate a profile (or any Mapping[axis, value])
result = envelope.evaluate(profile)
result.verdict           # ContainmentVerdict.INSIDE / OUTSIDE / UNDEFINED
result.per_axis          # dict[axis, ContainmentVerdict]
result.violated_axes     # tuple of axes outside the region
result.undefined_axes    # tuple of axes whose value is None / NaN
result.reasons           # human-readable reasons, one per non-INSIDE axis

# Boolean shortcut: True iff verdict == INSIDE
envelope.contains(profile)

The protocol (closed-interval bounds, trinary verdict, OUTSIDE strictly dominating UNDEFINED in the aggregation, non-finite values treated as UNDEFINED not OUTSIDE) is pre-registered in docs/ENVELOPE_DIAGNOSTICS.md. It is not a claim about the "correct" admissible region for any specific system; the envelope is a composable primitive whose threshold tuning is the caller's responsibility.

Public validation track

Pre-registered, falsifiable experiments that stress the algebra primitives against the public Autonometrics benchmarks. Each experiment locks its hypothesis in docs/ before any output is generated, and records both confirmations and rejections verbatim. Recent public runs include negative results: they narrow what the current primitives can support and are not written as motivation for a new public release cycle.

Roadmap

  • v0.1.0a0 / v0.1.0a1: Toy trajectory recorder. Reserves name, declares vision, ships demo.
  • v0.2.0a0: Trajectory algebra (velocities, accelerations, drift, volatility, rolling statistics, summary), generic CSV / batch adapters, pre-registered diagnostics, public validation track.
  • v0.2.1a0: Documentation cleanup; PyPI summary description shortened.
  • v0.3.0a0: Granger-causal coupling primitives (granger_graph, CausalCouplingGraph, four scalar diagnostics), with pre-registered diagnostics (docs/COUPLING_DIAGNOSTICS.md) and public validation track (docs/COUPLING_VALIDATION.md, verdict: REJECTED).
  • v0.4.0a0 (current): Containment envelope primitives (Envelope, Envelope.from_trajectory, trinary ContainmentVerdict), with pre-registered diagnostics (docs/ENVELOPE_DIAGNOSTICS.md) and public validation track (docs/ENVELOPE_VALIDATION.md, verdict: CONFIRMED).

Beyond v0.4.0a0, the package returns to maintenance mode. No further cycle is pre-declared. Future work will be opened only when motivated by a concrete pre-registered design document or external collaboration.

Position in the trilogy

Layer Project Question it answers
1 Autonometrics Where does a system sit on the autonomy atlas?
2 Autodynamics How do successive profiles differ, how do their axes couple, and is a profile inside an admissible region (recorded motion + pairwise Granger coupling + per-axis containment envelope, not a dynamical model)?
3 Ex-Machina Can we build a system that occupies a chosen region?

License

Apache License 2.0 — see LICENSE.

Citation

If you reference this work, please cite Autodynamics directly. A formal citation block will be added when the API stabilises.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

autodynamics-0.4.0a0.tar.gz (101.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

autodynamics-0.4.0a0-py3-none-any.whl (40.3 kB view details)

Uploaded Python 3

File details

Details for the file autodynamics-0.4.0a0.tar.gz.

File metadata

  • Download URL: autodynamics-0.4.0a0.tar.gz
  • Upload date:
  • Size: 101.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for autodynamics-0.4.0a0.tar.gz
Algorithm Hash digest
SHA256 b1d889a53c8e395b9fceff8f808de46e348546d8c225c8e44b5d8af3141872b1
MD5 592c752a6f5a23ded0d5b2db33f0a6fe
BLAKE2b-256 1a585ae8171ec3163e18a0b33df783dd4b936922d47707404bc16cad610450ce

See more details on using hashes here.

File details

Details for the file autodynamics-0.4.0a0-py3-none-any.whl.

File metadata

  • Download URL: autodynamics-0.4.0a0-py3-none-any.whl
  • Upload date:
  • Size: 40.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for autodynamics-0.4.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 772d4dc7063657be5de30905ee93ada62607e17a0aa574c660395c5c935c984a
MD5 a91f851ee58ea9988b6945f5c9920e5c
BLAKE2b-256 95d929db4a1f8ab264f35530eb7e7467c76f758ea846a5b8a25cdb9ed66d17e0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page