Skip to content

CLI and SDK usage

For operator studies, the CLI and Python SDK share the same declared study runner. Use a Network for direct manipulation, or a StudySpec to retain a reusable preparation and operator word. The SDK also exposes the separate conditional relational model. Each route delegates to its engine execution and observation owners; the interface adds no physical law.

Install and discover

python -m pip install tnfr
tnfr --help
python -m tnfr --help

For the checked-out repository, install it with python -m pip install -e .. The commands below need the core package only. Plotting and optional numerical backends have separate extras listed in the root README. Use the installed command's help to check its available options.

Create, evolve and diagnose

from tnfr.sdk import TNFR, diagnose_network

network = TNFR.create(6, seed=42).ring()
network.evolve(steps=1, sequence="basic_activation")
diagnostics = diagnose_network(network)
print(diagnostics)

This supplies a six-node ring and one registered operator word. Initial EPI is zero, capacity is one, and phase is zero. A uniform initial state can retain uniform diagnostics; this preparation does not demonstrate spontaneous pattern formation. On this direct API, seed configures stochastic topology builders; runtime operators resolve the separate graph RANDOM_SEED setting. The run_study interface below supplies the same declared seed to both owners.

steps=1 executes one complete word, not one second of physical time. Word admission and live node/edge preconditions still apply. In particular, a word requiring resonance with a neighbor can fail on an isolated node in a random graph. A topology seed makes the random construction repeatable within its runtime; it does not make an inadmissible preparation valid.

evolve_grammar_aware is a separate sequential direct-glyph policy. It resolves the complete candidate list before selection, uses the supplied order to choose the first incrementally admitted glyph and abstains when none is admitted. Live operator failures propagate; filtering does not guarantee state admission, coherence growth or a complete valid word. Earlier successful operations remain applied if a later operation fails; this path has no whole-stage rollback. New support is visited on the next pass. Missing grammar support raises rather than substituting a different word.

diagnose_network observes a detached graph copy. It reads the stored pressure; it does not refresh pressure, evolve the network, infer a phase law, or supply missing temporal observations. A nodal product nu_f * DeltaNFR is a model-rate read-out, not a measured finite-time change.

Invalid or unavailable diagnostics retain explicit error/availability data instead of inventing a number. Circular curvature can be unavailable at individual nodes. Coherence-length provenance distinguishes the static product-fit length from the separate spectral fallback. See the tetrad owner for definitions and units. Diagnostic flags are observations or policies, not authorization to execute an operator or a guarantee of future stability.

Both SDK interfaces share the circular-mean availability policy: a vanishing finite resultant has no mean direction, while an invalid authoritative phase raises instead of falling through to another alias. The global mean's numerical tolerance is distinct from the tetrad curvature's exact represented-resultant criterion. Signed pressure means and population spreads reuse stable shared reductions, so an overflowing intermediate sum cannot turn a finite constant sample into infinite dispersion. Density follows the graph's directedness; loops and parallel edges can give density above one.

Fluent measure() computes its metrics on one detached graph snapshot. Core metric failures propagate; optional unified-field failures retain explicit unified_fields_available and unified_fields_error metadata. Exported scalar maps are detached from the result. Comparison tables display unavailable measurements as unavailable and include columns present in any supplied row.

Fluent save(path) delegates to export_to_json, refreshing the current measurements once and returning the network for chaining. It writes metadata and a measurement report, not a graph checkpoint. Validation and encoding precede atomic destination replacement.

StructuralObservation detaches payloads on construction and export, validates provenance and optional tolerance, and retains Python value types. The graph adapters preserve opaque node-label identity while copying field containers. Nested payloads are not recursively frozen and the envelope is not itself a JSON encoder. StudyResult instead enforces its string-key JSON schema and rejects invalid numerical values before constructing a retained report.

Regional and relational workflows

The regional and relational SDK guide contains the Python preparations for form observations, supplied sources, conditional joint evolution, pattern reports, bridge/joint-reset comparisons and capture certificates. Its coefficient-response workflow also covers module-level jet/sample uncertainty, exact export and read-only acquisition audits. These are not Network methods or extra CLI study modes. Detailed admission and report fields belong to the relational contracts; their theorems remain in the theory catalog.

The same guide covers hidden bridge memory, exact collective families, finite maintenance with uncertainty and actual collective-pulse feedback. These are detached module-level readers with different support and evidence requirements. relational_report_to_dict exports their scopes, exact fractions, intervals and unavailable fields through the shared SDK envelope; exporting a report neither advances a network nor admits it as input to another theorem.

For acquired winding classes, recovery, joined interfaces and complete-state readouts, follow the formed-object workflow. It uses the existing mathematical owners and the same exact report exporter, with a zero-horizon storage example that performs no evolution. These APIs retain their supplied source and complete law; they do not implicitly read Network.G or change the native relational execution model.

Run and export the same study from either interface

The Python declaration is explicit and serializable:

from tnfr.sdk import StudySpec, export_to_json, run_study

spec = StudySpec(
    nodes=6,
    topology="ring",
    seed=42,
    sequence="basic_activation",
    cycles=1,
    name="ring-study",
)
result = run_study(spec)
export_to_json(spec.to_dict(), "study.json")
export_to_json(result, "report.json")

The equivalent CLI preparation is:

tnfr network --nodes 6 --topology ring --seed 42 --sequence basic_activation --steps 1 --name ring-study --export-spec study.json --output report.json

The CLI maps --steps to the declaration's cycles. To run an already exported declaration:

python -m tnfr network --spec study.json --output replay-report.json

--spec is exclusive with preparation options such as --nodes, --seed and --steps. Edit or construct the declaration before running it; an additional command-line value does not silently override a recorded input.

Python reads the same file through the strict declaration constructor:

from tnfr.sdk import StudySpec, import_from_json, run_study

spec = StudySpec.from_dict(import_from_json("study.json"))
replayed = run_study(spec)
report = replayed.to_dict()

StudySpec is immutable and validates its declared inputs. Its fields are nodes, topology, seed, sequence, cycles, probability and name. Supported topologies are ring, path, star, complete and random; probability configures random edges. sequence names a registered word. Each call creates its own prepared network rather than continuing another run.

The result records the declaration, runtime provenance, realized initial and final triad/support, and final diagnostics. to_dict() returns detached data; export_to_json uses the shared JSON writer. Without --output, the CLI writes JSON to standard output and sends messages to standard error. An output path receives the report instead. Retain the declaration together with the report when comparing studies.

JSON export rejects nonfinite numerical payloads before replacing the destination. Represent unavailable observations with their availability record and null, not a nonstandard NaN or Infinity token. The generic exporter also rejects recursively colliding encoded object keys (for example integer 1 and string "1") before replacing the destination. Non-colliding key conversions retain the existing JSON encoder's behavior. import_from_json, also used for CLI recipe input, rejects duplicate decoded keys at every depth, nonfinite numbers and nonzero fractional literals that underflow to binary64 zero. Integer literals remain Python integers; ordinary fractional literals retain binary64 rounding. Decoding neither authenticates report provenance nor replaces StudySpec field validation. The shared decoder is tnfr.utils.io.json_loads; JSON configuration files use the same admission policy before injection. YAML/TOML keep their parser semantics and still require validation by the consuming model.

Report key Content
spec Validated declaration, including the seed and requested cycles
execution Package/runtime versions, precision mode, registered word and completed cycles
initial_state Indexed scalar triad/pressure observations and support before execution
final Final state projection, metrics, nodal observations and tetrad with availability/provenance

Other engine configuration is inherited from the running process. The report does not capture every effective setting; retain relevant external configuration with the declaration. Scalar state rows use indices in graph iteration order; display labels are not serialized node identities, and edge attributes are not included in this projection.

The executable reproducible study example writes a declaration, reads it back, runs it and exports the result:

python examples/01_foundations/reproducible_study.py --output-dir output/study

Discover registered words and operators

tnfr sequences
tnfr sequences basic_activation
tnfr operators
tnfr operators emission

These commands expose existing catalogs rather than a second grammar. They accept --output for JSON export. Python accesses the same named words with list_sequences() or list_sequences("basic_activation") from tnfr.sdk. The operator contracts own operator metadata, while grammar scope distinguishes admission policies from mathematical stability results.

Reproducibility and scope

Network.nodal_scan() returns supplied prediction/readout records. Their mean_local_coherence averages admitted local values in [0,1]; it does not replace invalid negative values by magnitudes. Total coherence still aggregates the supplied pressure/rate channels independently. Logical verdicts accept booleans or unavailable None, with separate active_unavailable_count, equilibrium_unavailable_count and bifurcation_unavailable_count totals. Truthy strings/numbers and contradictory prediction aliases reject at report consumption. Export keeps the existing string-keyed node mapping, but raises when distinct labels such as 1 and "1" collide instead of dropping a node. These checks do not authenticate a manually constructed report as a live state.

Network.conservation() uses strictly increasing retained observation times. Its balance and candidate-energy secant use the same represented interval; missing intervals remain unavailable. candidate_energy_nonincreasing reads the admitted secant's sign, which agrees with the captured endpoint ordering; candidate_energy_within_numerical_tolerance retains the separate numerical alert. A small positive energy change is still an increase even when it passes that alert. Nonzero unrepresentable temporal rates reject instead of reporting perfect balance. Failed combined observations do not append partial evidence; returned tracker snapshots and reports are detached from retained data. Neither observation establishes general dynamical stability.

Network.nfr() is a stored-state observation, not a certificate that an NFR has formed. Its radial/annular/multinodal labels classify the unit-source potential centrality profile under a configured policy; a uniform profile does not establish a literal ring or rotational symmetry. Empty or unsupported geometry returns topology="unavailable", topology_available=False and an explicit topology_status. Consumers must handle that availability state. An all-zero centrality profile caused by numeric underflow reports centrality_below_represented_range; it cannot establish annular geometry.

Its coherence_length now uses the shared tetrad estimator, with coherence_length_available and coherence_length_provenance, instead of the old untagged topology-only spectral proxy. A fitted length has structural distance units; the spectral fallback is a separate dimensionless scale. Neither is a fractal dimension. Pressure observations remain available even when rate/capacity information is missing. Partial or invalid stored rates do not become equilibrium evidence; only absent rate telemetry permits the explicitly labelled unforced nodal-product prediction. It does not infer Gamma, refresh pressure, establish full-state equilibrium or measure persistence.

depi_dt_status records rate availability. The nodal-product fallback reuses the canonical derivative; if two nonzero factors produce a rounded zero, it reports nodal_product_underflow rather than certifying observed stationarity. This observation rule does not alter runtime product rounding. Tetrad summaries retain unavailable fields, and their overall safety advisory requires matching nonempty local field support; an empty snapshot cannot pass it.

Network.phase() exposes the classifier's actual imbalance ratios, node count, and coherence-length availability/provenance. Its historical phase labels are configured, size-sensitive diagnostics, not autonomous events or a biological claim. See the phase classification scope.

Retained item What it establishes What it does not establish
Declaration and seed Supplied preparation, word and requested cycle count Autonomous selection of those inputs
Initial/final triad and support Observed endpoints of that finite invocation Every intermediate state or future behavior
Runtime provenance Context for comparing executions Bit-identical results across arbitrary versions, platforms or numerical backends
Diagnostic values and availability Read-outs of stored state with estimator scope Complete reconstruction or a new constitutive law

A study report is not a complete resumable checkpoint. The declaration can be executed again from its supplied initial state; the report does not restore all callbacks, caches, operator history or external state. This distinction also applies to older export_to_json payloads and import_from_json, which reads JSON data without reconstructing a live execution.

For experimental interpretation, the research plan and measurement protocol remain the owners of hypothesis selection, calibration and reserved evaluation. Exporting a study does not by itself satisfy those scientific admission gates.

Existing advanced execution routes

tnfr run, sequence, math.run, epi.validate and metrics retain their specialized execution/configuration paths. Their --help output owns the available flags. They are not aliases for network, and their history formats are not StudySpec declarations. Prefer network for the shared SDK/CLI study workflow and use a specialized route when its actual configuration is needed.

epi.validate reports stored affinity, capacity and all-edge phase diagnostics. Its compatibility flag --check-coherence checks the sign of stored W_mean affinity with --tolerance; it does not measure canonical C preservation. Capacity must be finite and nonnegative, and U3 uses its configured hard gate; the affinity tolerance relaxes neither requirement. --no-check-coherence, --no-check-frequency and --no-check-phase disable individual checks. Malformed inputs fail, missing observations remain unavailable, and selecting no checks or obtaining no observed checks returns a nonzero exit status.

On run, explicit --stop-early-window or --stop-early-fraction options enable the stopping policy. The runtime requires a Boolean enabled, a positive integer window and a finite real fraction in [0, 1]; inactive window/fraction fields are not consumed. The policy is fixed for the invocation. Stopping requires new observations and a complete consecutive window of valid recorded stability fractions. Invalid/missing samples break that window; older retained telemetry alone cannot stop a new invocation. The built-in metric producer tracks a sample revision, including with bounded histories. Custom growing series can signal new samples by increasing their length. An uninstrumented fixed-size custom buffer supplies no freshness evidence. Only the required tail is inspected, rather than rescanning the full history after each step. This remains a configured finite-observation stopping rule, not a proof of convergence or full-state equilibrium.

HISTORY_MAXLEN bounds each retained metric series, not the number of metric names. Resizing a bound preserves the newest samples; disabling it restores growing lists. Explicit least-used-key removal remains a separate operation. Runtime callbacks and candidate sampling share a zero-based execution ordinal, independent of retained metrics and physical time. Both callback boundaries see the same index. current_step_idx reads that active index during execution and the next index between calls. The first tracked invocation starts at zero; old metrics do not reconstruct unobserved runtime history. Admission failure reserves no index, while an admitted call that later fails consumes its index because partial state changes may remain. Same-graph recursive calls reject. Standalone graphs without runtime markers retain their documented history-index fallbacks; those are not lifetime execution counts. Physical-time observations continue to use the separately declared clock.

REMESH cooldown uses the runtime ordinal after an epoch has been established; standalone calls keep their stable-sample-count basis. The stored basis prevents subtracting those different quantities during migration. The first eligible successful operation establishes the new basis, while the physical-time cooldown remains independent. Limiting history does not remove metric names or prevent bounded REMESH transactions, and detached stage validation cannot append advisory events into the live history.

The example index classifies the other demonstrations. The SDK's fluent builders, auxiliary physics adapters and optimizer policies have their own scopes; this guide does not promote them into autonomous nodal laws.

Auxiliary arithmetic command

tnfr-is-prime uses the declared integer arithmetic-pressure model, separately from network evolution. Basic execution reuses the shared divisor and factor functions. --optimized reuses one sieve/result-cache owner; --batch evaluates sorted distinct inputs. --cached and --no-optimize select the basic route. --benchmark N requires N >= 100 and prints the mathematics benchmark report; its timings are machine/input dependent. Automatic arithmetic execution uses NumPy and requires no GPU runtime.

The historical tnfr.tools.tnfr_is_prime_cli_optimized module delegates to this same command. Its benchmark_basic compatibility call now returns the shared benchmark schema (total_numbers_tested, total_time_ms, cache_statistics, and related fields), replacing its former basic-versus-cached timing schema.