88import torch .nn .functional as F
99
1010from .cov_util import (calc_cross_cov_mats_from_data , calc_pi_from_cross_cov_mats ,
11- form_lag_matrix , calc_pi_from_cross_cov_mats_block_toeplitz )
11+ calc_pi_from_cross_cov_mats_block_toeplitz )
1212
1313__all__ = ['DynamicalComponentsAnalysis' ,
1414 'DynamicalComponentsAnalysisFFT' ,
15- 'DynamicalComponentsAnalysisKNN' ,
1615 'ortho_reg_fn' ,
1716 'build_loss' ,
1817 'init_coef' ]
@@ -181,8 +180,9 @@ class DynamicalComponentsAnalysis(object):
181180 """Dynamical Components Analysis.
182181
183182 Runs DCA on multidimensional timeseries data X to discover a projection
184- onto a d-dimensional subspace which maximizes the complexity, as defined by the Gaussian
185- Predictive Information (PI) of the d-dimensional dynamics over windows of length T.
183+ onto a d-dimensional subspace of an N-dimensional space which maximizes the complexity, as
184+ defined by the Gaussian Predictive Information (PI) of the d-dimensional dynamics over windows
185+ of length T.
186186
187187 Parameters
188188 ----------
@@ -226,6 +226,18 @@ class DynamicalComponentsAnalysis(object):
226226
227227 Attributes
228228 ----------
229+ T : int
230+ Default T used for PI.
231+ T_fit : int
232+ T used for last cross covariance estimation.
233+ d : int
234+ Default d used for fitting the projection.
235+ d_fit : int
236+ d used for last projection fit.
237+ cross covs : torch tensor
238+ Cross covariance matrices from the last covariance estimation.
239+ coef_ : ndarray (N, d)
240+ Projection matrix from fit.
229241
230242 """
231243 def __init__ (self , d = None , T = None , init = "random_ortho" , n_init = 1 , stride = 1 , tol = 1e-6 ,
@@ -278,7 +290,7 @@ def estimate_cross_covariance(self, X, T=None, regularization=None, reg_ops=None
278290 X : ndarray or list of ndarrays
279291 Data to estimate the cross covariance matrix.
280292 T : int
281- T for PI calculation (optional.)
293+ T for PI calculation (optional).
282294 regularization : str
283295 Whether to regularize cross covariance estimation.
284296 reg_ops : dict
@@ -313,6 +325,10 @@ def fit_projection(self, d=None, T=None, n_init=None):
313325 ----------
314326 d : int
315327 Dimensionality of the projection (optional.)
328+ T : int
329+ T for PI calculation (optional). Default is `self.T`. If `T` is set here
330+ it must be less than or equal to `self.T` or self.estimate_cross_covariance() must
331+ be called with a larger `T`.
316332 n_init : int
317333 Number of random restarts (optional.)
318334 """
@@ -340,6 +356,10 @@ def _fit_projection(self, d=None, T=None, record_V=False):
340356 ----------
341357 d : int
342358 Dimensionality of the projection (optional.)
359+ T : int
360+ T for PI calculation (optional). Default is `self.T`. If `T` is set here
361+ it must be less than or equal to `self.T` or self.estimate_cross_covariance() must
362+ be called with a larger `T`.
343363 record_V : bool
344364 If True, saves a copy of V at each optimization step. Default is False.
345365 """
@@ -350,13 +370,15 @@ def _fit_projection(self, d=None, T=None, record_V=False):
350370 self .d_fit = d
351371 if T is None :
352372 T = self .T
373+ if T < 1 :
374+ raise ValueError
353375 if (2 * T ) > self .cross_covs .shape [0 ]:
354376 raise ValueError ('T must less than or equal to the value when ' +
355- '`estimate_cross_covariance` was called.' )
377+ '`estimate_cross_covariance() ` was called.' )
356378 self .T_fit = T
357379
358380 if self .cross_covs is None :
359- raise ValueError ('Call estimate_cross_covariance() first.' )
381+ raise ValueError ('Call ` estimate_cross_covariance()` first.' )
360382
361383 c = self .cross_covs [:2 * T ]
362384 N = c .shape [1 ]
@@ -706,117 +728,3 @@ def score(self, X):
706728 X : ndarray or list
707729 """
708730 return pi_fft (X , self .coef_ , self .T )
709-
710-
711- class DynamicalComponentsAnalysisKNN (object ):
712- """Dynamical Components Analysis using MI estimation using the
713- k-nearest neighbors methos of Kraskov, et al. This estimator is not
714- differentiable and so numerical gradients are taken (very slow!).
715-
716- WARNING: This code has not been used or tested and is still being developed.
717-
718- Runs DCA on multidimensional timeseries data X to discover a projection
719- onto a d-dimensional subspace which maximizes the complexity of the d-dimensional
720- dynamics.
721-
722- Parameters
723- ----------
724- d : int
725- Number of basis vectors onto which the data X are projected.
726- T : int
727- Size of time windows across which to compute mutual information.
728- init : string
729- Options: "random", "PCA"
730- Method for initializing the projection matrix.
731- """
732- def __init__ (self , d = None , T = None , init = "random" , n_init = 1 , tol = 1e-6 ,
733- ortho_lambda = 10. , verbose = False , use_scipy = True ):
734- self .d = d
735- self .T = T
736- self .init = init
737- self .n_init = n_init
738- self .tol = tol
739- self .ortho_lambda = ortho_lambda
740- self .verbose = verbose
741- self .coef_ = None
742-
743- def fit (self , X , d = None , T = None , n_init = None ):
744- self .mean_ = X .mean (axis = 0 , keepdims = True )
745- X -= self .mean_
746- if n_init is None :
747- n_init = self .n_init
748- pis = []
749- coefs = []
750- for ii in range (n_init ):
751- coef , pi = self ._fit_projection (X , d = d )
752- pis .append (pi )
753- coefs .append (coef )
754- idx = np .argmax (pis )
755- self .coef_ = coefs [idx ]
756- return self
757-
758- def _fit_projection (self , X , d = None ):
759- from info_measures .continuous import kraskov_stoegbauer_grassberger as ksg
760- if d is None :
761- d = self .d
762-
763- N = X .shape [1 ]
764- if type (self .init ) == str :
765- if self .init == "random" :
766- V_init = np .random .normal (0 , 1 , (N , d ))
767- elif self .init == "random_ortho" :
768- V_init = scipy .stats .ortho_group .rvs (N )[:, :d ]
769- elif self .init == "uniform" :
770- V_init = np .ones ((N , d )) / np .sqrt (N )
771- V_init = V_init + np .random .normal (0 , 1e-3 , V_init .shape )
772- else :
773- raise ValueError
774- else :
775- raise ValueError
776- V_init /= np .linalg .norm (V_init , axis = 0 , keepdims = True )
777-
778- callback = None
779- if self .verbose :
780-
781- def callback (v_flat ):
782- v = v_flat .reshape (N , d )
783- X_lag = form_lag_matrix (X .dot (v ), 2 * self .T )
784- mi = ksg .MutualInformation (X_lag [:, :self .T * d ],
785- X_lag [:, self .T * d :])
786- pi = mi .mutual_information ()
787- reg_val = ortho_reg_fn (v , self .ortho_lambda )
788- print ("PI: {} bits, reg: {}" .format (str (np .round (pi , 4 )),
789- str (np .round (reg_val , 4 ))))
790- callback (V_init )
791-
792- def f (v_flat ):
793- v = v_flat .reshape (N , d )
794- X_lag = form_lag_matrix (X .dot (v ), 2 * self .T )
795- mi = ksg .MutualInformation (X_lag [:, :self .T ], X_lag [:, self .T :])
796- pi = mi .mutual_information ()
797- reg_val = ortho_reg_fn (v , self .ortho_lambda )
798- loss = - pi + reg_val
799- return loss
800- opt = minimize (f , V_init .ravel (), method = 'L-BFGS-B' , callback = callback )
801- v = opt .x .reshape (N , d )
802-
803- # Orthonormalize the basis prior to returning it
804- V_opt = scipy .linalg .orth (v )
805- final_pi = self .score (X , V_opt )
806- return V_opt , final_pi
807-
808- def transform (self , X ):
809- return (X - self .mean_ ).dot (self .coef_ )
810-
811- def fit_transform (self , X , d = None , T = None ):
812- self .fit (X , d = d , T = T )
813- return self .transform (X )
814-
815- def score (self , X , coef = None ):
816- if coef is None :
817- coef = self .coef_
818- from info_measures .continuous import kraskov_stoegbauer_grassberger as ksg
819- X_lag = form_lag_matrix (X .dot (coef ), 2 * self .T )
820- mi = ksg .MutualInformation (X_lag [:, :self .T ], X_lag [:, self .T :])
821- pi = mi .mutual_information ()
822- return pi
0 commit comments