FAQ and troubleshooting¶
Common install and runtime friction for KoopmanGraph. For a full install walkthrough see Installation. For the public vs power-user API contract see Architecture and API layers.
Installation order (PyTorch / PyG / wheels)¶
Install PyTorch, then PyTorch Geometric (PyG), then KoopmanGraph. KoopmanGraph depends on both; installing the package first often pulls an incompatible or source-built stack.
Pick a PyTorch build (CPU or CUDA) from the PyTorch Get Started selector.
Install matching PyG wheels from the PyG installation guide.
Install KoopmanGraph (
pip install koopman-graph,uv pip install koopman-graph, or an editable clone /uv sync).
If the installer tries to compile extensions or cannot find wheels, re-check
that the installed torch version and CUDA tag match the PyG wheel index you
used. With uv, uv pip install torch --torch-backend=auto (or a specific
backend) helps pick a matching PyTorch index; see Installation.
Which platforms does CI cover?¶
CI runs the full Ubuntu test matrix on Python 3.10–3.12 and a macOS core smoke job on Python 3.12. Windows is best-effort community support (not in CI). See Installation (Supported platforms / CI).
CUDA vs CPU mismatches¶
Symptoms include CUDA-related import errors, RuntimeError about devices, or
kernels failing only on GPU.
Confirm
torch.cuda.is_available()matches the build you intended.Reinstall PyTorch and PyG for the same CUDA (or CPU) choice; mixing a CPU
torchwheel with CUDA PyG extensions (or the reverse) is a common failure mode.When reporting install failures, include
python --version,torch.__version__, and whether CUDA is expected.
Editable installs and extras¶
From a clone of the repository:
pip install -e . # runtime package only
pip install -e ".[dev]" # tests, Ruff, pre-commit
pip install -e ".[docs]" # Sphinx documentation build
# uv equivalents:
uv sync # runtime package only (CPU torch by default)
uv sync --extra dev
uv sync --extra docs
Use .[dev] for local testing and .[docs] before cd docs && make html.
The [dev] and [docs] extras do not replace the PyTorch / PyG prerequisite
order above when you need a non-default (non-CPU) accelerator.
Optional feature extras¶
pip install "koopman-graph[mpc]" # OSQP for KoopmanMPC
pip install "koopman-graph[symmetry]" # networkx for auto node orbits
pip install "koopman-graph[rl]" # Gymnasium / Stable-Baselines3
pip install "koopman-graph[lightning]" # Fabric + KoopmanLightningModule
pip install "koopman-graph[ray]" # Ray Train + ensemble Ray
pip install "koopman-graph[dask]" # offline dask_prep helpers
pip install "koopman-graph[msm]" # deeptime / GraphVAMP interop
pip install "koopman-graph[md]" # mdtraj molecular I/O stubs
pip install "koopman-graph[equivariance]" # e3nn Tier-B encoder
pip install "koopman-graph[baselines-ode]" # torchdiffeq for STGODE
pip install "koopman-graph[baselines-graphcast]" # reserved; teaching GraphCast is pure PyTorch
pip install "koopman-graph[distributed]" # meta: lightning + ray + dask
MPC:
from koopman_graph.mpc import KoopmanMPC, TubeKoopmanMPC. Construction works without OSQP;solve/rollout/evaluateraise with install guidance if OSQP is missing.Symmetry:
koopman_auto_orbits=Trueusesnetworkx(method="auto"). Without[symmetry],node_orbit_partitionwarns and returns the identity partition (no tying). Exact orbits /koopman_symmetry="isotypic"need optionalpynautyseparately.Distributed trainers: native DDP /
torchrunneed only core PyTorch. Fabric and optionalKoopmanLightningModuleTrainer sugar need[lightning].[ray]covers bothrun_ray_train_fit_loop()(model DDP) andfit_ensemble_with_ray()(ensemble members). Prefer Fabric / DDP for multi-GPU model training unless you already standardize on Ray Train (see below).[dask]activateskoopman_graph.distributed.dask_prepmaterialize helpers (not a training loop; see “Can I use Dask?” below). See Installation and Capabilities and datasets (Distributed training).MD / MSM:
[msm]pins deeptime for GraphVAMP /koopman_graph.interop.[md]pins mdtraj for optional trajectory I/O underkoopman_graph.datasets.molecular; the synthetic contact-graph oracle needs no extra.See Installation for the full extras table.
Import paths after 0.6¶
Version 0.6 keeps a thin root façade. Core workflow symbols remain
from koopman_graph import … (model; encoders/decoders including delay and
hypergraph; operators including graph / hypergraph / global-local /
continuous-graph; snapshot containers; primary spectrum helpers;
__version__).
Specialized symbols are capability-module imports only (hard cut; no root aliases), for example:
from koopman_graph.baselines import DMDBaseline, EDMDBaseline
from koopman_graph.losses import ForwardConsistencyLoss
from koopman_graph.training import FitHistory, LossWeights
from koopman_graph.adaptation import RecursiveKoopmanAdapter
from koopman_graph.env import GraphKoopmanEnv
from koopman_graph.data import temporal_split, WindowSampler
from koopman_graph.metrics import evaluate_forecast, EvaluationResult
from koopman_graph.uq import (
BayesianKoopmanUQ,
ConformalKoopmanUQ,
EnsembleGraphKoopmanModel,
)
from koopman_graph.mpc import KoopmanMPC, TubeKoopmanMPC, TubeMPCReport
from koopman_graph.nn import AdaptiveAdjacency
from koopman_graph.analysis import (
identify_sparse_dynamics,
koopman_spectral_clustering,
resdmd,
spectral_residuals,
)
from koopman_graph.baselines import vamp2_score
from koopman_graph.statistics import spectral_distance
# Root façade also exports SimplicialEncoder / InvariantGeometryEncoder
ImportError: cannot import name '…' from 'koopman_graph' for one of these
names usually means the import should use the capability module. See the Keep-in
/ Demote inventories in Architecture and API layers.
Choosing an adjacency mode (graph operators)¶
Pairwise networked operators (koopman="graph" and
koopman="continuous_graph") take adjacency (factory:
koopman_adjacency):
"symmetric"(default) — undirected \(D^{-1/2} A D^{-1/2}\). Use when the graph is undirected or when you intentionally symmetrize directed edges."random_walk"— row-normalized \(D_{\mathrm{out}}^{-1} A\). Use for one-way / directed coupling that the symmetric mode cannot represent."dual_random_walk"— forward walk plus \(D_{\mathrm{in}}^{-1} A^{\top}\) (extraK_bwd/L_bwdfactors). Use when both directions matter but should not be forced into a single symmetric matrix (DCRNN-style bidirectional diffusion).
Hypergraph operators do not expose adjacency (Zhou symmetric
incidence). Self-adaptive topology and orbit-tied K_{\mathrm{self}} are
separate options and are not substitutes for directed normalization. See
Architecture and API layers (adjacency contract) and Scope and limitations.
Can I train on several graphs or topologies?¶
Yes. Pass a MultiTrajectory of homogeneous
(or all-hetero) sequences to fit().
That path already averages per-trajectory losses; it is not a
single-topology restriction.
fit(..., batch_graphs=True) is an opt-in vectorization of that
loop: independent graphs are collated into one PyG Batch so shared
\(K\) applies to the disconnected union with per-graph shifts.
Reconstruction (and forward consistency when weighted) match the mean of
the per-sequence losses on modest batches. The default Python loop is
unchanged. Hetero, hypergraph, windowed, and DDP graph-batching are out
of scope. This flag does not enable multi-topology training for the
first time, and this page does not quote a throughput number.
See Architecture and API layers (multi-graph Batch).
Why is training slow on large \(N\)?¶
Several training paths still assemble or multiply dense structures whose size grows with \(N\) (or \(N\cdot d\)), even when latents and supports are cached within an evaluation:
Exact inverse and continuous dense \(\Phi=\exp(\Delta t\, L_{\mathrm{eff}})\) on \((N\cdot d)\times(N\cdot d)\) matrices
Exact spectrum when the Kronecker path does not apply (dense \((N\cdot d)\) eigendecomposition, or discrete distributed Arnoldi surrogate); eligible graph / continuous-graph spectrum uses Kronecker-sum reduction instead — see below and Scope and limitations (Scale)
DiffConv diffusion supports and hypergraph Zhou \(\hat{H}\) as dense \(N\times N\) tensors
Eigenvalue hinge on dense / ODO networked operators (\(O((N\cdot d)^3)\) eigendecomposition of the assembled map)
Self-adaptive topology materializing full \(N^2\) COO
Shared pair latents, inverse / support / \(\hat{H}\) / \(\Phi\) reuse, and PDE/worst-case prediction sharing reduce repeated work; they do not change those representation sizes. See Scope and limitations (Scale), Matrix-free linear operators, and Capabilities and datasets (Training performance).
Does spectrum still assemble \(N\cdot d\)?¶
Not always for discrete GraphKoopmanOperator /
ContinuousGraphKoopmanOperator. .spectrum auto-routes:
sparsity="distributed"— Arnoldi leading-modulus surrogate on discrete graph / multiplex hetero; continuous graph has no Arnoldi spectrum path and uses dense \(L_{\mathrm{eff}}\)Else if eligible (shared self;
adjacencyin{"symmetric", "random_walk"};sparsityin{"dense", "block_diagonal"}) — Kronecker-sum exact spectrum (order \(O(N^3 + N d^3)\) via dense \(N\times N\) \(\widehat{A}\) plus \(N\) blocks of size \(d\times d\)). Discrete hop degree \(P\ge 0\) is eligible; the pencil is \(B(\lambda)=\sum_k\lambda^k K_k\), not a sum of independent factor eigenvalues. Continuous graph stays the one-tap generator.Else — dense \((N\cdot d)\) eigendecomposition (
dual_random_walk, discrete orbit / isotypic self banks, hetero / hypergraph, helper fall-back)
Exact inverse and eigenvalue-regularization hinges are unchanged (still dense assembled ceilings where documented). Details: Scope and limitations (Scale) and Architecture and API layers (spectrum routing).
Does a deeper GNN encoder make up for one-hop \(K\)?¶
No. Encoder neighborhood mixing and the Koopman factor hop degree are
different maps. fit()
warns when encoder hops exceed discrete graph filter_degree
(GCN/GAT depth, or DiffConv num_layers * diffusion_steps). Raise
koopman_filter_degree or reduce encoder depth if you want them
matched. The check does not fail training and skips operators without
a hop radius. See Scope and limitations,
examples/38_operator_factorization_ablation.ipynb (hop-matched
\(P\) arm; not a rewrite of the historical joint-LS gap), and
examples/49_multi_hop_factorization.ipynb (Kronecker versus dense
\(P=2\) spectrum).
Does identification replace Adam fit?¶
No. fit() still defaults to
Adam (identification=None). Pass
IdentificationConfig to alternate
frozen-encoder closed-form \(K\) updates (ridge, TLS, or
constrained least squares) with encoder/decoder Adam steps. That path
currently supports discrete dense per-node
KoopmanOperator only. After fit, read
model.identification_report for latent one-step / short-rollout
mean squared error (MSE) and \(\rho(K)\) — not a Haseli–Cortés,
ResDMD, or stability certificate. Types and solvers stay off the root
façade (from koopman_graph.identification import ...). Tutorials
continue to use Adam unless they opt in. See Operator identification
and examples/48_identification_invariance.ipynb.
Is evaluate(..., include_invariance=True) a Haseli–Cortés certificate?¶
No. Opt-in include_invariance (and
subspace_invariance_report())
reports a dimensionless finite-sample projection leakage
\(\eta\) on a truncated-SVD basis of encoded snapshots. Default
evaluate MAE / RMSE / MAPE are unchanged. The helper currently
supports discrete dense per-node
KoopmanOperator only. It is not
the Haseli–Cortés invariance-proximity certificate (principal angles /
worst-case bound; HaseliCortes2023), not
ForwardConsistencyLoss, and not
spectral_residuals(). Closed-form
fit still does not fill IdentificationReport.invariance.
Does residual-aware selection certify a ResDMD spectral measure?¶
No. select_resdmd_gated() compares
already-scored dictionaries: lowest train one-step mean squared error
(MSE) wins, unless gate_resdmd=True first drops candidates whose
max finite-dictionary ResDMD residual exceeds the default cutoff
\(10^{-2}\) (same as
resdmd()).
IdentificationConfig.gate_resdmd=True fills
IdentificationReport.spectral on the final identification fit;
it does not abort training.
ResDMDFitCallback defaults to
mode="observe". mode="gate" raises at on_fit_end when the
observed max residual exceeds that cutoff, without mutating parameters.
None of these is an infinite-dimensional residual certificate.
Does identify_sparse_graph_factors replace SINDy or L1 training?¶
No. identify_sparse_graph_factors()
fits shared \(K_{\mathrm{self}}\) / \(K_{\mathrm{nbr}}\) on
frozen encodings (STLSQ or a teaching proximal group-lasso, then an
unpenalized refit). It is not
identify_sparse_dynamics() (polynomial /
graph library on learned latents) and not
KoopmanSparsityLoss (soft training
penalty on operator entries). Those tools still ship. Dual
random-walk and polynomial \(P>1\) hops are out of scope. Related
sparse-Koopman literature: Pan, Arnold-Medabalimi, and Duraisamy
(J. Fluid Mech., 2021; Pan2021SparseSubspace). The identifier is
not that paper’s multi-task EDMD dictionary pruning.
Does select_latent_rank replace Ray Tune for latent_dim?¶
No. select_latent_rank() scores a
truncated-SVD grid of frozen encodings (in-tree VAMP-2, a
finite-dictionary ResDMD residual elbow, or stability-penalized
held-out one-step mean squared error). It does not train an encoder
per candidate and does not choose
GraphKoopmanModel latent_dim.
koopman_graph.tuning Ray Tune helpers remain caller-owned
example scaffolds; KoopmanGraph is not an AutoML product. deeptime
([msm]; deeptime2021) is an optional VAMP-2 cross-check, not a
runtime requirement. See Operator identification and
examples/53_latent_rank_selection.ipynb.
Does koopman-graph benchmark run train a model?¶
No. koopman-graph benchmark run --manifest … --data … --out …
verifies the dataset SHA-256 against a frozen
ExperimentManifest and writes
identity-bound summary.json (canonical digest;
executed=False). verify recomputes that digest and fails on a
tampered hash. Neither command fits
GraphKoopmanModel, downloads METR-LA, or
hosts a LibCity / BasicTS leaderboard. Default CI verifies hashed
stand-ins under benchmarks/v0.15/ (see Command-line interface and
Identity-bound benchmarks); it does not download full telemetry.
When should I use sparsity="block_diagonal"?¶
Use sparsity="block_diagonal" on graph / hypergraph / continuous-graph
operators when the dense \(N\cdot d\) path dominates wall time and a
self-dominated (Jacobi-style) approximation is acceptable for your
advance / inverse use case. Prefer sparsity="dense" when you need the
full coupled effective map for advance / inverse. Eligible graph /
continuous-graph .spectrum may still Kronecker-route the full coupled
factors under block_diagonal. Tutorial:
examples/29_large_graph_block_diagonal.ipynb.
How do I enable automatic mixed precision (AMP)?¶
Pass use_amp=True to GraphKoopmanModel.fit (or run_fit_loop).
Optional amp_dtype selects the autocast dtype (default
torch.float16). AMP is supported on CUDA only; on CPU or MPS the
fit loop warns once and continues in FP32. AMP does not change loss
definitions — only numeric precision during the forward / backward pass.
With Lightning Fabric, prefer Fabric precision or use_amp —
not both stacked (fit_with_fabric raises if both own autocast).
How do I use multiple GPUs or processes?¶
Use the power-user koopman_graph.distributed helpers (not root
__all__):
Native DDP / ``torchrun`` (core install):
torchrun --standalone --nproc_per_node=2 \\ examples/scripts/ddp_fit_torchrun.py
Or call
model.fit(..., strategy="ddp")/run_ddp_fit_loop()under a process group. PreferDistributedWindowSampler(orwindow_length=...) so a single trajectory can shard across ranks; full-sequence mode requires at least as many trajectories as ranks.Lightning Fabric —
fit_with_fabric()afterpip install "koopman-graph[lightning]".Lightning Trainer (optional sugar) —
KoopmanLightningModulewraps a composedGraphKoopmanModel. CollateDataLoaderbatches as aGraphSnapshotSequenceor a list of sequences; export withexport_format1_checkpoint. Prefer Fabric / DDP when you need full loss schedules orDistributedWindowSampler.Ray Train model DDP (optional) —
run_ray_train_fit_loop()afterpip install "koopman-graph[ray]". Same scientific epoch driver under Ray TrainTorchTrainer. Prefer DDP / Fabric unless you already standardize on Ray (see “Which trainer should I choose?” below). Multi-node Ray Train is outside the CI contract.Ray ensemble members (optional) —
fit_ensemble_with_ray()afterpip install "koopman-graph[ray]", orEnsembleGraphKoopmanModel.fit(..., parallel_backend="ray", member_factory=...). Sequential ensemble fit remains the default. Prefer a picklable (ideally module-level) factory. This does not change UQ coverage guarantees and does not shard one model across GPUs — that is Ray Train / DDP / Fabric.Ray Tune HPO — power-user helpers in
koopman_graph.tuning(fit_history_metrics,run_ray_tune, optionalexample_*smoke scaffolds) plusexamples/scripts/ray_tune_koopman_example.py. The search configuration stays script-/caller-owned; KoopmanGraph is not an AutoML product. Optuna is examples-only (no library Optuna API).
Default fit / run_fit_loop remain single-process when
strategy is unset. Distributed training does not reduce dense
\(N\cdot d\) ceilings (see Scope and limitations). Multi-node behavior
is not covered by default CI.
Which trainer should I choose — DDP, Fabric, Ray Train, or Ray ensemble?¶
They answer different questions:
Native DDP / Fabric — recommended default for multi-GPU model training and full loss schedules / window sharding.
Ray Train (
run_ray_train_fit_loop) — optional model-DDP backend when your cluster already uses Ray Train. Same scientific fit loop; not a multi-node production path in CI.Ray ensemble (
fit_ensemble_with_ray) — parallel independent member fits forEnsembleGraphKoopmanModel. Not model DDP.
Do not stack Ray Train and Fabric autocast ownership, and do not confuse
any of these with operator sparsity="distributed".
Does heterogeneous / multiplex koopman="hetero_graph" work with DDP /
Fabric / Lightning / Ray?
————————————————————————–
Yes. RelGraph / HeteroGraphKoopmanOperator models compose with the
same trainer adapters:
model.fit(..., strategy="ddp")/run_ddp_fit_loop()KoopmanLightningModule(hetero batches coerce via sequence helpers)run_ray_train_fit_loop()Ray ensemble member fits when members accept hetero inputs
find_unused_parameters defaults to True for hetero RelGraph stacks
(override on the DDP fit path if you know every parameter is used).
Single-process windowed run_fit_loop accepts windowed hetero sequences
(parity with world-size-1 DDP window sampling). Dense \(N\cdot d\) /
stacked width ceilings are unchanged by multi-GPU training.
Is trainer “distributed” the same as sparsity="distributed"?¶
No. They are unrelated:
Trainer orchestration — optional DDP / Fabric /
KoopmanLightningModulepaths underkoopman_graph.distributed(data-parallel gradients / devices).``sparsity=”distributed”`` — an operator sparsity mode for matrix-free inverse and Arnoldi spectrum on discrete graph and multiplex hetero constructors (hypergraph / continuous peers may still assemble). It does not enable multi-GPU training.
LinearOperatorProtocolis the same operator-math surface (polynomial graph + one-tapmatrix_free). Trainer DDP does not shrinkMAX_DENSE_LINEAR_OPERATOR_SIZE.
Can I use Dask with KoopmanGraph?¶
Yes, for offline data prep — not as a second training runtime.
pip install "koopman-graph[dask]" activates
koopman_graph.distributed.dask_prep helpers
(materialize_sequences, materialize_window_index_list). The library
does not import Dask from training; trainers remain native DDP /
Fabric / Ray Train / Ray ensemble.
Typical pattern:
import dask
from koopman_graph.distributed import materialize_sequences
delayed_seqs = [dask.delayed(load_sequence)(path) for path in paths]
sequences = materialize_sequences(delayed_seqs)
model.fit(list(sequences), epochs=10) # or windowed / DDP fit
Do not replace run_fit_loop() /
DDP / Fabric with a Dask-worker training loop.
How do I log fits to CSV, TensorBoard, W&B, or MLflow?¶
Use observe-only FitCallback hooks on
single-process fit / run_fit_loop():
CSV / TensorBoard (in-tree) —
CsvFitLoggerandTensorBoardFitLogger:from koopman_graph.tracking import CsvFitLogger, TensorBoardFitLogger model.fit( sequence, epochs=20, callbacks=[ CsvFitLogger("runs/fit.csv"), TensorBoardFitLogger("runs/tb"), # needs: pip install tensorboard ], )
W&B / MLflow (DIY, no library pin) — implement
FitCallbackand callwandb.log/mlflow.log_metricsfromon_epoch_endusingtrain_breakdown.to_floats(). Sketch classes live inexamples/tracking/wandb_mlflow_callback.py. Install those SDKs yourself; KoopmanGraph does not declare them as dependencies.Lightning Trainer — attach Lightning loggers to
KoopmanLightningModule/Trainer; do not expectfit(..., callbacks=)on the Lightning path. Nativestrategy="ddp"rejects non-Nonecallbacksuntil that path is wired.
What is the difference between spectral_residuals and ResDMD?¶
They answer different questions:
``spectral_residuals`` — held-out data-driven check that claimed eigenpairs propagate as \(a(t+1)\approx\lambda\, a(t)\) in the learned latent / observable space. Diagnostic filter (
trustworthy_mask), not a residual-DMD certificate.``resdmd`` / resolvent-norm grid — finite-dictionary ResDMD MVP and finite-matrix resolvent helpers in
koopman_graph.analysis. Useful for residual-aware spectral diagnostics on a fixed dictionary; not infinite-dimensional certified pseudospectra / spectral measures. Seeexamples/40_resdmd_pseudospectra.ipynband Scope and limitations.
Does MpEDMDBaseline replace Euclidean EDMD or spectral conditioning?¶
No. MpEDMDBaseline is measure-preserving
EDMD (Colbrook, SIAM J. Numer. Anal., 2023; Colbrook2023mpEDMD):
a Gram-weighted orthogonal Procrustes polar factor of the dictionary
map. Unitarity is in that Gram inner product. On a regular polygonal
planar rotation (identity dictionary; empirical Gram a multiple of the
identity), mpEDMD matches EDMDBaseline;
on a contraction it does not recover the dissipative map
(eigenvalues stay on the unit circle). It does not obsolete Euclidean
conditioning diagnostics on a general directed
\(K_{\mathrm{eff}}\). Use
evaluate_forecast() with the same Data-only
predict call site as EDMD. Euclidean conditioning on a general
directed \(K\) is Spectral diagnostics.
Does GEDMDBaseline infer a generator from irregular timestamps?¶
No. GEDMDBaseline is generator EDMD
(Klus et al., Physica D, 2020; Klus2020gEDMD): least squares of
\(\dot\psi \approx \psi L^{\top}\) on a polynomial dictionary.
Callers must supply \(dx/dt\) as Data.dx_dt
or fit(..., derivatives=). Discrete neural fit still rejects
non-uniform \(\Delta t\). Irregular timestamps on the gEDMD
sequence are unused and do not create \(L\). This is not
identify_sparse_dynamics() (SINDy / STLSQ
on learned latents, including mode="derivative"). predict
advances by \(\exp(L\,\Delta t)\) with fitted time_step;
evaluate_forecast() uses the same Data-only
call site as EDMD.
Does SpectralDiagnostics certify a finite-horizon bound?¶
No. SpectralDiagnostics reports
\(\kappa(V)\), Wilkinson \(\kappa_i\), departure from
normality, discrete Nyquist \(1/(2\Delta t)\) in cycles per unit
time, and per-mode aliasing flags. Discrete
compute_spectrum() warns when a
mode is Nyquist-adjacent.
mode_amplitudes()
warns when \(\kappa(V)\) exceeds
CONDITION_WARN (\(10^{6}\))
and still solves \(Va=z^{\top}\). None of these is a bound on
\(\|K^{k}\|\). See Spectral diagnostics and
examples/51_spectral_diagnostics.ipynb.
Does monitor_critical_transition certify a critical transition?¶
No. monitor_critical_transition() is a
sliding-window spectral-gap heuristic. A positive rate means the
closest-eigenvalue gap shrank. It is not a Ghosh-grade
topology-criticality certificate (Ghosh2025) and not
stability_certificate().
See Spectral-gap criticality monitor and examples/54_criticality_monitor.ipynb.
Does LinearOperatorProtocol replace Kronecker spectrum or DDP?¶
No. LinearOperatorProtocol is
matvec / solve / Arnoldi algebra without assembling
\(K_{\mathrm{eff}}\). Leading eigpairs are Ritz values, not
\(\operatorname{eig}(B(\lambda))\). Trainer DDP does not shrink
the representation. Dense assembly is refused above
MAX_DENSE_LINEAR_OPERATOR_SIZE.
See Matrix-free linear operators.
Does GraphDynamicsConfig close the topology loop by default?¶
No. Default graph_dynamics=None keeps the 0.14 hold-last path.
Pass GraphDynamicsConfig to attach a
topology head (default sparse_candidate). Recursive prediction
is opt-in and mutually exclusive with
learn_topology="self_adaptive" when the head is not none.
See Predicted graph dynamics and
examples/50_graph_state_closure.ipynb (wiring check, not a
learned-forecast claim).
Can discrete neural fit use irregular \(\\Delta t\)?¶
No. Discrete fit and predict_at still require a uniform
increment equal to time_step. Gaps raise
(validate_uniform_discrete_increments). Set
dynamics_mode="continuous" so predict_at integrates a
generator over the supplied intervals. Generator EDMD is a different
escape hatch and requires supplied derivatives; irregular
timestamps on that sequence do not create \(L\)
(Klus2020gEDMD). See Time and parameter conditioning and
examples/12_irregular_sampling_continuous_time.ipynb.
How do I encode time of day?¶
diurnal_control_features() returns Fourier
sine/cosine columns for existing additive / bilinear
control_inputs. diurnal_phase_index()
bins timestamps for a per-step phase_index on
koopman="switched". These are recipes, not a native calendar
field or checkpoint key. Discrete uniform-\(\\Delta t\)
validation is unchanged. Heterogeneous sequences have no calendar
helper. See Time and parameter conditioning.
What does example 22 report for GraphKoopman versus the GNN ports?¶
Saved METR-LA weekday-cache output ranks GraphKoopman first on
aggregate RMSE (z-scored speed): \(0.6551\) versus STGCN
\(0.7076\), DCRNN \(1.0754\), and Graph WaveNet
\(0.9036\). GraphKoopman uses a longer ODO / rollout /
early-stopping budget than the GNN teaching refs (unequal budgets).
These are in-repo teaching baselines, not dedicated-library SOTA.
See examples/22_gnn_forecaster_comparison.ipynb.
Does CochainKoopmanOperator replace koopman="hodge" or TopologicX?¶
No. CochainKoopmanOperator advances
node and edge latents on a static signed \(B_1\). It is not a
factory kind; koopman=None stays "pernode".
koopman="hodge" is a node Laplacian neighbor term
(HodgeKoopmanOperator). Face latents
may be stored; \(k=2\) is not evolved.
boundary_nilpotency() flags
\(B_1 B_2\\approx 0\). This is not sheaf theory and not
TopologicX parity (Lim2020Hodge, TopoX2024).
Is the order-2 teaching path TopologicX or TDA parity?¶
No. order2_cochain_teaching() binds
CochainKoopmanOperator to a filled
triangle and scores \(B_1 B_2\\approx 0\). Face latents may be
stored; \(k=2\) is not evolved. Optional tetrahedra reach
MAX_CELL_COMPLEX_DEGREE (3). Sheaf
restriction maps stay learned-optional (default diagonal). This is
not TopologicX or TDA ecosystem parity (TopoX2024).
Are Hodge mode components physical circulation?¶
No. hodge_decompose_modes() projects
stored eigenvector columns onto the combinatorial gradient / curl /
harmonic subspaces of a static signed \(B_1\)
(Lim2020Hodge). On a consistently oriented cycle the constant
1-cochain is harmonic; that algebraic kernel is not a validated
fluid or electrical current. The helper is analysis-only, not a
factory kind, not koopman="hodge", and not TopologicX / sheaf
parity (TopoX2024).
Is dynamics_mode="stochastic" a continuous-time SDE?¶
No. That factory string adds learned diagonal process noise after a
discrete linear map. DriftDiffusionKoopman
is a separate Euler–Maruyama / Yosida stepper: forward is the
conditional-expectation semigroup and advance samples a path. It
is not certified Itô theory and not SDMD
(Xu2025StochasticSemigroup, Zhou2025Yosida).
What coverage does conformal UQ claim?¶
coverage names
JointCoverageSpec
target="per_node_marginal". That is frequentist marginal coverage
under exchangeability, approximate on graph time series. Simultaneous
node–feature–horizon boxes and event coverage are named but not
implemented (Schlembach2025Conformal). Proper scores
(gaussian_crps(),
gaussian_nll(),
energy_score()) evaluate forecasts; they do
not certify coverage.
Does Hankel-DMD or HAVOK replace DelayEmbeddingEncoder?¶
No. DelayEmbeddingEncoder stacks
Takens-style channels around a sized GNN encoder.
HankelDMDBaseline (Arbabi and Mezić,
SIAM J. Appl. Dyn. Syst., 2017; Arbabi2017HankelDMD) and
HAVOKBaseline (Brunton et al.,
Nature Communications, 2017; Brunton2017HAVOK) fit operators on
delay-embedded flattened snapshots. HAVOK predict is autonomous
(\(u=0\)). Optional history supplies older delay slots
(oldest → newest); without it, those slots are zeros, so
predict(data, steps) is not a faithful delay initial condition
when n_delays > 1.
Is delay embedding or HAVOK a Mori–Zwanzig memory model?¶
No. DelayEmbeddingEncoder stacks
Takens-style channels. HAVOKBaseline
is delay-plus-forcing (Brunton2017HAVOK), not a projection-operator
memory kernel. markov_closure_report()
flags residual-energy autocorrelation (Ljung–Box-style;
Ljung1978Box). FiniteMemoryKoopman
is a convolution MVP at the same latent width as a delay encoder, not
a factory kind and not Mori–Zwanzig identification
(Lin2021MoriZwanzig). Recovered memory length is an oracle test,
not a general theorem.
Does InvariantGeometryEncoder make the Koopman operator equivariant?¶
No. Tier A
InvariantGeometryEncoder builds
rotation-/translation-invariant features from Data.pos and lifts them
with a standard GCN. Optional Tier B
E3EquivariantEncoder (e3nn,
[equivariance]) uses steerable message passing but still defaults
to invariant scalar latents. A separate
EquivariantKoopmanOperator is a
block MVP: scalars, scale * I_3 vectors, and optional
\(l=2\) scale * I_5 tensors. It is not a factory kind
and not a molecular MD production stack (see Scope and limitations).
Does symplectic \(K\) conserve decoded mass?¶
No. parameterization="symplectic" constrains the latent
operator matrix. A nonlinear decoder can still break mass in
feature space (Greydanus2019HNN). Use
MassConservingDecoder or
LinearConservingDecoder when a named
decoded channel must satisfy a linear conservation law. Those
heads do not turn IEEE-118 Laplacian diffusion into AC power
flow.
Does TubeKoopmanMPC prove recursive feasibility?¶
No. TubeKoopmanMPC erodes nominal
output boxes by conformal quantiles or ensemble residual radii
and reports constraint-violation rate, feasibility rate, and
quadratic stage cost on a toy closed loop. Local decoder
linearization is unchanged. The helper is not a chance-constraint
solver and not a Lyapunov closed-loop certificate. Zhang et al.,
Automatica 137:110114 (2022), prove robustness for an r-KMPC
scheme with an offline nonlinear ancillary law; this MVP does not
implement that controller or inherit those proofs
(Zhang2022TubeMPC).
Does granger_latent_influence recover interventional edges?¶
No. granger_latent_influence()
is non-interventional: it reports linear residual-MSE
reduction on observed latents. The labeled synthetic helper
recover_synthetic_interventional_edges()
recovers a known do-edge on
teaching_three_node_scm() only. That
protocol is not observational discovery on field data.
Does evaluate_topology_transfer mean factorization transfers well?¶
No. evaluate_topology_transfer()
measures zero-shot or fine-tune transfer across a node-count change.
It always reports a mandatory pernode control and may return
negative transfer advantage (as on the seeded path-diffusion fixture
in examples/37_cross_topology_transfer.ipynb). Self-adaptive, orbit,
and isotypic configurations bind \(N\) and are excluded. Naming is
deliberately evaluate_* / measure-style — not a success path. See
Scope and limitations.
Are AGCRN / MTGNN / STGODE / GraphCast leaderboard reproductions?¶
No. The in-repo ports under koopman_graph.baselines.gnn are
teaching baselines with documented ForecasterProtocol deviation
tables for side-by-side comparisons on the same PEMS / METR slices. They
are not protocol-matched LibCity / BasicTS leaderboard entries. GraphCast
is a small-mesh weather teaching adapter, not a PEMS sensor-graph
forecaster and not ERA5-scale production training. Prefer dedicated
traffic libraries when you need competition numbers (see
Scope and limitations).
Hierarchical pooling: per_snapshot vs hold_perm¶
HierarchicalGraphKoopmanModel defaults to
pool_schedule="per_snapshot": TopK / SAG scores are recomputed each
timestep from that snapshot’s features (0.7.0-compatible). Set
pool_schedule="hold_perm" to pool from the first snapshot and reuse
that permutation for the rest of the sequence — fewer pool passes, but
pool assignments no longer track per-timestep feature changes.
Hypergraph dense \(\hat{H}\) and clear_hyperedge_cache¶
koopman="hypergraph" advances through a dense Zhou \(\hat{H}\)
(\(N\times N\)). Static incidence reuses a pointer-keyed cache shared
by advance, eigen, and dense inverse assembly. Call
clear_hyperedge_cache() (or
HypergraphKoopmanOperator.clear_hyperedge_cache()) after in-place
edits to hyperedge_index / hyperedge_weight that keep the same
storage pointers. New incidence tensors invalidate automatically.
Caching does not remove the dense \(O(N^2)\) ceiling — see
Scope and limitations.
Training cost: eigenvalue loss and continuous dense \(\Phi\)¶
Two training terms can dominate on large graphs:
Eigenvalue hinge (
LossWeights.eigenvalue > 0) with dense or ODOkoopman="graph"/"hypergraph"/ continuous-graph peers builds the effective \(N\cdot d\) operator and callstorch.linalg.eigvals. Cost is cubic in \(N\cdot d\). Prefer structural parameterizations (schur,lyapunov,dissipative) or keep \(N\) modest when this weight is non-zero.Continuous dense advance (
dynamics_mode="continuous"withkoopman="graph"/"continuous_graph"andsparsity="dense") forms \(\Phi=\exp(\Delta t\, L_{\mathrm{eff}})\). For large \(N\), prefersparsity="block_diagonal"(self-only shortcut). The dense path caches \(\Phi\) for repeated topology / \(\Delta t\) within a singlecompute_training_lossevaluation and clears that cache at the next evaluation so optimizer steps never see a stale transition.
See Scope and limitations (Scale) and Capabilities and datasets.
Checkpoint format and load failures¶
Checkpoints use FORMAT_VERSION. Current saves write format_version: 1.
Loaders accept only supported versions (currently {1}).
Previously published format-2 checkpoints and sparse historical format-1 payloads are rejected (no silent migration).
Typical failure:
ValueError/ load error naming an unsupportedformat_version. Retrain or re-save under the current schema, or use the package version that produced the checkpoint.Serialization details and Built-in operator kinds are documented in Architecture and API layers (checkpoint / serialization sections).
Where to ask for help¶
Reuse the project support routing (also in repository CONTRIBUTING.md):
Usage / how-to — GitHub Discussions
Bugs (crash, wrong results, install failure with a repro) — bug report
Features / API changes — feature request
Responses are best-effort; there is no SLA. Security vulnerabilities should be
reported privately — see the repository SECURITY.md.