Fast pairwise Convergent Cross Mapping in PyTorch.
FastCCM computes exact CCM scores equivalent to pyEDM>=2.3.2.
- Pairwise CCM and pairwise S-Map.
- Separate source and target sets.
- Vectorized
E,tau,tpsearch and convergence testing. - Blocked execution, auto batching, and memmap output for large matrices.
Average runtime over three runs on an Apple M4 Pro 64GB CPU. All inputs
use float32.
Simplex and S-Map timings with E=5, exclusion_window=5, and a 10GB
memory budget.
| Condition | Simplex (s) | S-Map (s) |
|---|---|---|
| 100x100, T=1000 | 0.041558 | 0.467506 |
| 200x200, T=1000 | 0.124226 | 1.309896 |
| 800x800, T=500 | 0.376993 | 5.922905 |
| 100x100, T=8000 | 1.063445 | 11.787477 |
Simplex timings with E=5, exclusion_window=5, and a 16GB memory budget.
| Sources | Targets | T | Time (s) |
|---|---|---|---|
| 1000 | 1 | 1000 | 0.244822 |
| 5000 | 1 | 1000 | 1.290271 |
| 20000 | 1 | 1000 | 5.385606 |
| 20000 | 1 | 2000 | 17.392626 |
| 1 | 1000 | 1000 | 0.016070 |
| 1 | 5000 | 1000 | 0.069529 |
| 1 | 20000 | 1000 | 0.281602 |
| 1 | 20000 | 2000 | 0.384756 |
Simplex and S-Map self-prediction timings with E=20,
exclusion_window=10, and a 10GB memory budget.
| Condition | Simplex (s) | S-Map (s) |
|---|---|---|
| T=2000 | 0.004840 | 0.008117 |
| T=8000 | 0.016766 | 0.078616 |
| T=32000 | 0.194169 | 1.255505 |
| T=128000 | 2.788039 | 20.402604 |
See the full CPU and GPU benchmark reports for settings, ranges, and CPU-utilization results.
Requirements: Python >= 3.9, pip.
CPU-only
pip install torch==2.10.0 --index-url https://download.pytorch.org/whl/cpu
pip install fastccmCUDA 12.6
pip install torch==2.10.0 --index-url https://download.pytorch.org/whl/cu126
pip install fastccmmacOS (CPU / MPS)
pip install torch==2.10.0
pip install fastccmFastCCM expects lists of 2D arrays.
X_emb: source embeddings with shape(T, E).Y_emb: target embeddings with shape(T, E_y), or(T, 1)for scalar targets.- Different lengths are end-aligned automatically.
import numpy as np
from fastccm import PairwiseCCM, Functions, Visualizer, utils
from fastccm.data import get_truncated_rossler_lorenz_rand
system = get_truncated_rossler_lorenz_rand(
tmax=200,
n_steps=4000,
C=2,
seed=0,
)
funcs = Functions(device="cpu", memory_budget_gb=2.0, verbose=0)
viz = Visualizer()
searches = [
funcs.find_optimal_embedding_params(
system[:, i],
sample_size=400,
exclusion_window=500,
E_range=np.arange(1, 20),
tau_range=np.arange(1, 20),
tp_range=np.arange(1, 100, 10),
seed=i,
subtract_global=False, # Subtract global linear model fit. Set to True to select E and tau
# based on the Simplex Projection without autoregression
)
for i in range(system.shape[1])
]
opt_E = [res["optimal_E"] for res in searches]
opt_tau = [res["optimal_tau"] for res in searches]
print(opt_E)
print(opt_tau)
viz.visualize_optimal_e_tau(searches[3])X_emb = utils.embed(system, E=opt_E, tau=opt_tau)
Y_emb = system.T[:, :, None]
ccm = PairwiseCCM(device="cpu", memory_budget_gb=2.0, verbose=0)
scores = ccm.score_matrix(
X_emb=X_emb,
Y_emb=Y_emb,
library_size="auto",
sample_size="auto",
exclusion_window=20,
method="simplex",
seed=0,
)
ccm_matrix = scores[0] # Y is scalar, so output shape is (1, n_Y, n_X)
print(ccm_matrix.shape) # (6, 6)
print(ccm_matrix)x_idx = 1
y_idx = 3
X_pair = [utils.embed(system[:, x_idx], E=opt_E[x_idx], tau=opt_tau[x_idx])[0]]
Y_pair = [utils.embed(system[:, y_idx], E=opt_E[y_idx], tau=opt_tau[y_idx])[0]]
conv = funcs.convergence_test(
X_emb=X_pair,
Y_emb=Y_pair,
library_sizes=[100, 200, 400, 800, 1600],
sample_size="auto",
exclusion_window=20,
method="simplex",
trials=10,
seed=0,
)
print(conv["library_sizes"])
print(conv["X_to_Y"].shape) # (n_sizes, trials, E_y, n_Y, n_X)
viz.plot_convergence_test(conv)import numpy as np
rng = np.random.default_rng(0)
X_emb = rng.uniform(0.0, 1.0, size=(50_000, 1000, 5)).astype(np.float32)
Y_emb = rng.uniform(0.0, 1.0, size=(50_000, 1000, 1)).astype(np.float32)
funcs = Functions(device="cpu", memory_budget_gb=2.0, verbose=1)
scores_mm = funcs.score_matrix_blocked(
X_emb=X_emb,
Y_emb=Y_emb,
x_block=100,
y_block=50_000,
library_size="auto",
sample_size="auto",
exclusion_window=20,
method="simplex",
seed=0,
out_path="ccm_scores.dat",
)
print(type(scores_mm))
print(scores_mm.shape)notebooks/CCM results comparison.ipynbscripts/README_CPU.mdscripts/README_GPU.mdscripts/benchmark_performance.pyscripts/benchmark_flat_arrays.pyscripts/benchmark_single_series_self_prediction.py