Where Convergent Cross-Mapping Fails#
October 13, 2024
Convergent Cross-mapping (CCM) is a technique for causal inference in dynamical systems. CCM claims to infer cause and effect directionality given two time series \(X\) and \(Y\). The 2012 paper of Sugihara et al introduced CCM and they showed that for a deterministic βcoupledβ chaotic system where \(X\) influences \(Y\) and vice versa by some degree, CCM is able to infer the strength of effect. (For a more in depth background about CCM and how it works, see Chapter 6 of our online Time Series Handbook.)
Hundreds of papers have since used CCM to infer causal direction in ecosystems and other natural systems. In our 2022 paper, we used CCM to infer possible causal relations among factors in a water dam system.
However, because CCM is built with the assumption of a determinstic coupled chaotic dynamical system, itβs natural to ask, βhow applicable is CCM for time series data that come from systems that violate the assumption of CCM?β
In this article we investigate the ability of CCM to infer directionality between two observed time series variables on different classes of functions:
Deterministic Chaotic Systems - we investigate the effect of the presence of hidden confounding variables
Chaotic Systems with Random Noise - here, the system is no longer fully deterministic.
Additive Noise Models (ANMs) - standard model for Structural Causal Models of the form \(Y = f(X) + N_X\) where \(N_X\) is an independent random noise. We use non-linear Gaussian models. Linear Gaussian models have been shown to be unidentifiable (we cannot infer directionality of causation) unless the variances of the noise are all equal. (See the works of Scholkopf, Peters, Janzing, Pearl).
We summarize our findings below.
variable relations |
causal graph |
\(X \rightarrow Y\) |
\(Y \rightarrow X\) |
\(Z \rightarrow X\) |
\(Z \rightarrow Y\) |
\(X \rightarrow Z\) |
\(Y \rightarrow Z\) |
|
---|---|---|---|---|---|---|---|---|
1 |
chaotic (deterministic) |
\(X \perp Y, Z_t \rightarrow X_{t+1}, Z_t \rightarrow Y_{t+2}\) |
β |
β |
β |
β |
β |
β |
2 |
chaotic (deterministic) |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}, X_t \rightarrow X_{t+1}\), \(Y_t \rightarrow Y_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
3 |
chaotic (deterministic) |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
4 |
chaotic (w/ noise) |
\(X \perp Y, Z_t \rightarrow X_{t+1}, Z_t \rightarrow Y_{t+2}\) |
β |
β |
β |
β |
β |
β |
5 |
chaotic (w/ noise) |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}, X_t \rightarrow X_{t+1}\), \(Y_t \rightarrow Y_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
6 |
chaotic (w/ noise) |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
7 |
additive noise model |
\(X \perp Y, Z_t \rightarrow X_{t+1}, Z_t \rightarrow Y_{t+2}\) |
β |
β |
β |
β |
β |
β |
8 |
additive noise model |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}, X_t \rightarrow X_{t+1}\), \(Y_t \rightarrow Y_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
9 |
additive noise model |
\(X_t \rightarrow Y_{t+1}\), \(Y_t \rightarrow X_{t+1}\) |
β |
β |
NA |
NA |
NA |
NA |
We find that in the presence of a confounding variable in a chaotic system, CCM was able to infer the right causal directions.
However, once small Gaussian noise (standard deviation = 0.015) is added to the variables at each timestep, CCM fails to infer the right relationships between some variables (item 4 in the table)
Similarly, CCM fails to consistently infer the right causal relations if they are in the form of Additive Noise Models (ANMs).
These results tell us that we should be careful when using CCM especially when the relations among variables do not follow a fully deterministic chaotic system. (We have yet to check for more kinds of causal graphs to investigate if CCM fails for some causal graphs even if chaotic.)
Chaotic Models#
First we define our chaotic system builder. The chaotic function follows this form:
\(A = A (r - r A - \beta B)\)
Where \(\beta\) is the strength of effect of \(B\) on \(A\)
def chaotic_func(A, B, r, beta):
return A * (r - r * A - beta * B)
Example 1#
We define a coupled chaotic function among three time series \(X\), \(Y\), and \(Z\). But only \(X\) and \(Y\) are observed. We visualize below the relationships among the variables. In this case, \(Z_t \rightarrow X_{t+1}\) and \(Z_t \rightarrow Y_{t+2}\) but there is no causal relation between \(X\) and \(Y\).

We define a determinsitic coupled chaotic system as follows:
# Based on Supplementary Materials https://www.science.org/doi/10.1126/science.1227079
# params
r_x = 3.4
r_y = 3.6
r_z = 3.8
B_xy = 0.0 # effect on x given y (effect of y on x)
B_yx = 0.0 # effect on y given x (effect of x on y)
B_yz = 0.2 # effect of z on y
B_xz = 0.2 # effect of z on x
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
Z0 = 0.3
t = 2000 # time steps
X = [X0, X0]
Y = [Y0, Y0]
Z = [Z0, Z0]
for i in range(t):
Z_ = chaotic_func(Z[-1], 0, r_z, 0)
X_ = chaotic_func(X[-1], Z[-1], r_x, B_xz)
Y_ = chaotic_func(Y[-1], Z[-2], r_y, B_yz)
X.append(X_)
Y.append(Y_)
Z.append(Z_)

Here, we see clearly that there is no convergence of cross-mapping correlations. In this case, CCM correctly inferred that there is no causal link between \(X\) and \(Y\).

Can CCM infer that \(Z \rightarrow X\)? We can see below that yes, it can.

Similarly, CCM can infer that \(Z \rightarrow Y\)

Example 2#
In this case, \(X_t \rightarrow Y_{t+1}\) and \(Y_t \rightarrow X_{t+1}\) but also \(X_t \rightarrow X_{t+1}\) and \(Y_t \rightarrow Y_{t+1}\). We make the effect of \(X\) on \(Y\) stronger than \(Y\) on \(X\).

# params
r_x = 3.4
r_y = 3.6
B_xy = 0.1 # effect on x given y (effect of y on x)
B_yx = 0.4 # effect on y given x (effect of x on y)
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
t = 2000 # time steps
X = [X0]
Y = [Y0]
for i in range(t):
X_ = chaotic_func(X[-1], Y[-1], r_x, B_xy)
Y_ = chaotic_func(Y[-1], X[-1], r_y, B_yx)
X.append(X_)
Y.append(Y_)

Here we find that CCM was able to correctly infer the feedback loop and also the order of strength of effect, where \(X\) has stronger effect on \(Y\)

Example 3#
Here, we keep \(X_t \rightarrow Y_{t+1}\) and \(Y_t \rightarrow X_{t+1}\) but we remove \(X_t \rightarrow X_{t+1}\) and \(Y_t \rightarrow Y_{t+1}\). We make the effect of \(X\) on \(Y\) stronger than \(Y\) on \(X\).

# params
r_x = 3.4
r_y = 3.6
B_xy = 0.1 # effect on x given y (effect of y on x)
B_yx = 0.4 # effect on y given x (effect of x on y)
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
t = 2000 # time steps
X = [X0]
Y = [Y0]
for i in range(t):
X_ = chaotic_func(Y[-1], Y[-1], r_x, B_xy)
Y_ = chaotic_func(X[-1], X[-1], r_y, B_yx)
X.append(X_)
Y.append(Y_)

CCM was able to infer the feedback loop between the two, but was unable to correctly order the causal relation by strength.

Chaotic Models + Noise#
We conduct the same experiments as above but we add independent random Gaussian noise on each variable.
Example 1#
Again, we define a coupled chaotic function among three time series \(X\), \(Y\), and \(Z\). But only \(X\) and \(Y\) are observed. We visualize below the relationships among the variables. In this case, \(Z_t \rightarrow X_{t+1}\) and \(Z_t \rightarrow Y_{t+2}\) but there is no causal relation between \(X\) and \(Y\).
On top of this, we add random Gaussian noise per variable.

We use the same determinsitic coupled chaotic system as above, but we add a small Gaussian noise.
# params
r_x = 3.4
r_y = 3.6
r_z = 3.8
B_xy = 0.0 # effect on x given y (effect of y on x)
B_yx = 0.0 # effect on y given x (effect of x on y)
B_yz = 0.2 # effect of z on y
B_xz = 0.2 # effect of z on x
np.random.seed(42)
X_std = 0.015
Y_std = 0.015
Z_std = 0.015
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
Z0 = 0.3
t = 2000 # time steps
X = [X0, X0]
Y = [Y0, Y0]
Z = [Z0, Z0]
for i in range(t):
Z_ = chaotic_func(Z[-1], 0, r_z, 0) + np.random.normal(0, Z_std)
X_ = chaotic_func(X[-1], Z[-1], r_x, B_xz) + np.random.normal(0, X_std)
Y_ = chaotic_func(Y[-1], Z[-2], r_y, B_yz) + np.random.normal(0, Y_std)
X.append(X_)
Y.append(Y_)
Z.append(Z_)

Once we added noise, CCM incorrectly infers that \(X \rightarrow Y\). So now we see that when the dynamical system is not perfectly determinsitic, CCM fails. (Even when the Gaussian noise has a small standard deviation = 0.015). This may be a problem since we expect real world dynamical systems to have some randomness.

Can CCM infer that \(Z \rightarrow X\)? We can see below that yes, it still can.

Similarly, CCM can still infer that \(Z \rightarrow Y\). But it seems that CCM infers a small \(Y \rightarrow Z\) which is incorrect.

Example 2#

# params
r_x = 3.4
r_y = 3.6
B_xy = 0.1 # effect on x given y (effect of y on x)
B_yx = 0.4 # effect on y given x (effect of x on y)
np.random.seed(42)
X_std = 0.015
Y_std = 0.015
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
t = 2000 # time steps
X = [X0]
Y = [Y0]
for i in range(t):
X_ = chaotic_func(X[-1], Y[-1], r_x, B_xy) + np.random.normal(0, X_std)
Y_ = chaotic_func(Y[-1], X[-1], r_y, B_yx) + np.random.normal(0, Y_std)
X.append(X_)
Y.append(Y_)

Here we find that CCM was able to correctly infer \(X \rightarrow Y\) and some weaker \(Y \rightarrow X\)

Example 3#

# params
r_x = 3.4
r_y = 3.6
B_xy = 0.1 # effect on x given y (effect of y on x)
B_yx = 0.4 # effect on y given x (effect of x on y)
X_std = 0.015
Y_std = 0.015
X0 = 0.3 # initial val following Sugihara et al
Y0 = 0.3 # initial val following Sugihara et al
t = 2000 # time steps
X = [X0]
Y = [Y0]
for i in range(t):
X_ = chaotic_func(Y[-1], Y[-1], r_x, B_xy) + np.random.normal(0, X_std)
Y_ = chaotic_func(X[-1], X[-1], r_y, B_yx) + np.random.normal(0, Y_std)
X.append(X_)
Y.append(Y_)

CCM was able to infer the feedback loop between the two, but was unable to correctly order the causal relation by strength.

Additive Noise Models#
Additive Noise Models (ANMs) follow this general mathematical form: \(Y = f(X) + N_X\) where \(N_X\) is independent noise.
Example 1#
We still follow the same causal graph as Example 1 in the chaotic function class above. However, instead of a chaotic relationship between variables, we use ANM formulation. Based on the insights above on chaotic models with noise (essentially it no longer becomes chaotic technically since chaotic systems are deterministic), we may hypothesize that CCM will fail on ANMs as well.

# create a time series SCM
np.random.seed(42)
L = 2000
alpha = 0.5
beta = 0.2
X_std = 0.1
Y_std = 0.1
Z_std = 0.1
X = [np.random.normal(0, X_std), np.random.normal(0, X_std)]
Y = [np.random.normal(0, Y_std), np.random.normal(0, Y_std)]
Z = [np.random.normal(0, Z_std), np.random.normal(0, Z_std)]
for t in range(L):
X += [alpha * X[-1] + beta * Z[-1] + np.random.normal(0, X_std)]
Y += [alpha * Y[-1] + beta * Z[-2] + np.random.normal(0, Y_std)]
Z += [alpha * Z[-1] + np.random.normal(0, Z_std)]

Interestingly, CCM correctly infers that there is not causal link between \(X\) and \(Y\)

However, CCM incorrectly infers a weak causal link \(X \rightarrow Z\) which is the opposite of the actual relation for embedding dimension, E=4. For E=2 and E=3, CCM does not infer any causal effect.

Similarly, CCM incorrectly infers a weak causal link \(Y \rightarrow Z\) for E=4, which is the opposite of the actual relation.

Example 2#
Again this is the same causal graph as Example 2 above, but we use ANMs as the functional relations among variables instead of a chaotic function.

# create a time series SCM
np.random.seed(42)
L = 2000
alpha = 0.5
beta = 0.2
X_std = 0.1
Y_std = 0.1
X = [np.random.normal(0, X_std), np.random.normal(0, X_std)]
Y = [np.random.normal(0, Y_std), np.random.normal(0, Y_std)]
for t in range(L):
X += [alpha * X[-1] + beta * Y[-1] + np.random.normal(0, X_std)]
Y += [alpha * Y[-1] + beta * X[-1] + np.random.normal(0, Y_std)]

Unfortunately CCM does not infer any causal relation, which is incorrect.

Example 3#
Here, we make the effect of \(X\) on \(Y\) stronger than \(Y\) on \(X\).

# create a time series SCM
np.random.seed(42)
L = 2000
alpha = 0.5
beta = 0.1
X_std = 0.1
Y_std = 0.1
X = [np.random.normal(0, X_std), np.random.normal(0, X_std)]
Y = [np.random.normal(0, Y_std), np.random.normal(0, Y_std)]
for t in range(L):
X += [beta * Y[-1] + np.random.normal(0, X_std)]
Y += [alpha * X[-1] + np.random.normal(0, Y_std)]

CCM correctly infers the bidirectional causal relation with a stronger \(X \rightarrow Y\) than \(Y \rightarrow X\)

Chaotic System in the Original CCM Paper (Sugihara, et al, 2012)#
# FROM Supplementary Materials https://www.science.org/doi/10.1126/science.1227079
# params
r_x = 3.8
r_y = 3.5
B_xy = 0.02 # effect on x given y (effect of y on x)
B_yx = 0.1 # effect on y given x (effect of x on y)
X0 = 0.4 # initial val following Sugihara et al
Y0 = 0.2 # initial val following Sugihara et al
t = 3000 # time steps
X = [X0]
Y = [Y0]
for i in range(t):
X_ = chaotic_func(X[-1], Y[-1], r_x, B_xy)
Y_ = chaotic_func(Y[-1], X[-1], r_y, B_yx)
X.append(X_)
Y.append(Y_)
