CCM (CCM v1.0.0)
View SourceConvergent Cross Mapping (CCM) implementation for detecting causality in coupled nonlinear dynamical systems.
CCM is a nonlinear time-series method introduced by Sugihara et al. (2012) that detects causal relationships between variables without requiring a parametric model. The key insight is that if variable X causally influences variable Y, then the historical values of Y contain enough information to recover the state of X — and this recovery improves as more data are added (i.e. the cross-map skill converges with library size).
Typical workflow
# 1. Build a CCM struct for the two time series
ccm = CCM.new(x_series, y_series, embedding_dim: 3, tau: 1)
# 2. (Optional) select the best embedding dimension automatically
%{optimal_dim: e} = CCM.optimal_embedding_dim(x_series)
ccm = CCM.new(x_series, y_series, embedding_dim: e)
# 3. Test causality in both directions simultaneously
%{x_causes_y: fwd, y_causes_x: rev} = CCM.bidirectional_ccm(ccm)
fwd.convergent #=> true / false
rev.convergent #=> true / false
Summary
Functions
Performs CCM analysis in both causal directions concurrently.
Performs CCM analysis for a single causal direction.
Creates a new CCM analysis structure.
Determines the optimal embedding dimension for a time series using simplex projection with leave-one-out (LOO) cross-validation (Sugihara & May, 1990).
Functions
Performs CCM analysis in both causal directions concurrently.
Runs :x_causes_y and :y_causes_x as parallel Tasks and collects the results.
This is the recommended entry point for a full causality analysis because genuine
unidirectional causality produces convergence in only one direction, while bidirectional
coupling produces convergence in both.
Parameters
ccm– a%CCM{}struct produced bynew/3
Returns
A map with two keys, each containing the result map from cross_map/2:
:x_causes_y– result for the X → Y direction:y_causes_x– result for the Y → X direction
Examples
iex> {x, y} = CoupledLogisticMapsGenerator.run(300, 0.15)
iex> ccm = CCM.new(x, y, embedding_dim: 3, tau: 1, num_samples: 50)
iex> %{x_causes_y: fwd, y_causes_x: rev} = CCM.bidirectional_ccm(ccm)
iex> is_boolean(fwd.convergent) and is_boolean(rev.convergent)
true
Performs CCM analysis for a single causal direction.
For each library size in ccm.lib_sizes, num_samples random sub-libraries are drawn.
Each sub-library is used to reconstruct the source attractor and predict the target
variable via simplex projection. The mean Pearson correlation across samples is
recorded as the cross-map skill for that library size. A positive trend in skill
with increasing library size is the hallmark of genuine CCM causality (convergence).
Parameters
ccm– a%CCM{}struct produced bynew/3direction– atom indicating the causal direction to test::x_causes_y(default) – tests whether X drives Y by cross-mapping Y's manifold to recover X:y_causes_x– tests whether Y drives X by cross-mapping X's manifold to recover Y
Returns
A map with:
:direction– the direction atom passed in:results– list of{library_size, mean_correlation}tuples, one per lib size:convergent–trueif the skill shows a significant positive trend and the peak skill exceeds 0.3 (Sugihara et al. 2012 criterion),falseotherwise
Creates a new CCM analysis structure.
Parameters
x_series– list of numeric values for variable Xy_series– list of numeric values for variable Y (must be the same length asx_series)opts– keyword list of options::embedding_dim– embedding dimension E used to reconstruct the attractor (default:3). Useoptimal_embedding_dim/2to choose this automatically.:tau– time delay between successive embedding coordinates (default:1). Values > 1 are useful when the time series is oversampled.:lib_sizes– explicit list of library sizes to evaluate. When omitted, sizes are generated automatically from a tenth of the usable length up to the maximum valid size.:num_samples– number of random bootstrap sub-library samples averaged at each library size (default:100). Higher values reduce variance at the cost of runtime.
Returns
A %CCM{} struct ready to be passed to cross_map/2 or bidirectional_ccm/1.
Raises
ArgumentErrorifx_seriesandy_serieshave different lengths.ArgumentErrorif:embedding_dimor:tauis less than 1.ArgumentErrorif:num_samplesis less than 1.
Examples
iex> CCM.new([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]) |> Map.get(:embedding_dim)
3
iex> CCM.new([1, 2, 3], [1, 2], []) |> Map.get(:embedding_dim)
** (ArgumentError) x_series and y_series must have the same length
Determines the optimal embedding dimension for a time series using simplex projection with leave-one-out (LOO) cross-validation (Sugihara & May, 1990).
For each candidate embedding dimension from 1 to max_dim, the series is embedded
with the given time delay tau, and each point is predicted from E+1 nearest neighbours
drawn from the rest of the library (LOO). The Pearson correlation between predictions
and actuals is used as the skill score. The embedding dimension that maximises this
skill is returned as the optimal choice.
Parameters
series– list of numeric valuesopts– keyword list of options::tau– time delay (default: 1):max_dim– largest embedding dimension to test (default: 10)
Returns
A map with:
:optimal_dim– the embedding dimension with the highest LOO skill:skills– list of{embedding_dim, skill}tuples for every dimension tested
Examples
iex> {x, _y} = CoupledLogisticMapsGenerator.run(200, 0.3)
iex> %{optimal_dim: e} = CCM.optimal_embedding_dim(x)
iex> e in 1..10
true