| """Online-ish Pearson correlation of all n x n variable pairs simultaneously.""" |
| from typing import NamedTuple |
|
|
| import jax.numpy as jnp |
|
|
|
|
| class OnlineMean(NamedTuple): |
| sum: jnp.ndarray |
| count: int |
|
|
| @staticmethod |
| def init(num_features: int): |
| return OnlineMean(sum=jnp.zeros(num_features), count=0) |
|
|
| def update(self, batch: jnp.ndarray): |
| return OnlineMean(self.sum + jnp.sum(batch, axis=0), self.count + batch.shape[0]) |
|
|
| def mean(self): |
| return self.sum / self.count |
|
|
| class OnlineCovariance(NamedTuple): |
| a_mean: jnp.ndarray |
| b_mean: jnp.ndarray |
| cov: jnp.ndarray |
| var_a: jnp.ndarray |
| var_b: jnp.ndarray |
| count: int |
|
|
| @staticmethod |
| def init(a_mean: jnp.ndarray, b_mean: jnp.ndarray): |
| assert a_mean.shape == b_mean.shape |
| assert len(a_mean.shape) == 1 |
| d = a_mean.shape[0] |
| return OnlineCovariance(a_mean, |
| b_mean, |
| cov=jnp.zeros((d, d)), |
| var_a=jnp.zeros((d, )), |
| var_b=jnp.zeros((d, )), |
| count=0) |
|
|
| def update(self, a_batch, b_batch): |
| assert a_batch.shape == b_batch.shape |
| batch_size, _ = a_batch.shape |
| a_res = a_batch - self.a_mean |
| b_res = b_batch - self.b_mean |
| return OnlineCovariance(a_mean=self.a_mean, |
| b_mean=self.b_mean, |
| cov=self.cov + a_res.T @ b_res, |
| var_a=self.var_a + jnp.sum(a_res**2, axis=0), |
| var_b=self.var_b + jnp.sum(b_res**2, axis=0), |
| count=self.count + batch_size) |
|
|
| def covariance(self): |
| return self.cov / (self.count - 1) |
|
|
| def a_variance(self): |
| return self.var_a / (self.count - 1) |
|
|
| def b_variance(self): |
| return self.var_b / (self.count - 1) |
|
|
| def a_stddev(self): |
| return jnp.sqrt(self.a_variance()) |
|
|
| def b_stddev(self): |
| return jnp.sqrt(self.b_variance()) |
|
|
| def E_ab(self): |
| return self.covariance() + jnp.outer(self.a_mean, self.b_mean) |
|
|
| def pearson_correlation(self): |
| |
| |
| eps = 0 |
| |
| |
| return jnp.nan_to_num(self.cov / (jnp.sqrt(self.var_a[:, jnp.newaxis]) + eps) / |
| (jnp.sqrt(self.var_b) + eps)) |
|
|
| class OnlineInnerProduct(NamedTuple): |
| val: jnp.ndarray |
|
|
| @staticmethod |
| def init(d: int): |
| return OnlineInnerProduct(val=jnp.zeros((d, d))) |
|
|
| def update(self, a_batch, b_batch): |
| assert a_batch.shape == b_batch.shape |
| return OnlineInnerProduct(val=self.val + a_batch.T @ b_batch) |
|
|