11
11
from __future__ import division , print_function
12
12
import numpy as np
13
13
from scipy .signal import fftconvolve
14
+ from scipy .signal import convolve2d
14
15
from scipy .ndimage .filters import gaussian_filter1d
15
16
from scipy .ndimage .interpolation import shift
17
+ from scipy .stats import multivariate_normal
16
18
from collections import Iterable
17
19
from inspect import getargspec
18
20
from copy import deepcopy
@@ -833,3 +835,74 @@ def __init__(self, name='tBreak', value=None, prior=None):
833
835
834
836
def __str__ (self ):
835
837
return 'Break-point'
838
+
839
+
840
+ class BivariateRandomWalk (TransitionModel ):
841
+ """
842
+ Correlated Gaussian parameter fluctuations. This model assumes that parameter changes follow a bivariate Gaussian
843
+ distribution.
844
+ """
845
+ def __init__ (self , name1 = 'sigma1' , value1 = None ,
846
+ name2 = 'sigma2' , value2 = None ,
847
+ name3 = 'rho' , value3 = None ,
848
+ prior = (None , None , None )):
849
+
850
+ if isinstance (value1 , (list , tuple )):
851
+ value1 = np .array (value1 )
852
+ if isinstance (value2 , (list , tuple )):
853
+ value2 = np .array (value2 )
854
+ if isinstance (value3 , (list , tuple )):
855
+ value2 = np .array (value3 )
856
+
857
+ self .study = None
858
+ self .latticeConstant = None
859
+ self .hyperParameterNames = [name1 , name2 , name3 ]
860
+ self .hyperParameterValues = [value1 , value2 , value3 ]
861
+ self .prior = prior
862
+ self .kernel = None
863
+ self .kernelParameters = None
864
+ self .tOffset = 0 # is set to the time of the last Breakpoint by SerialTransition model
865
+
866
+ def __str__ (self ):
867
+ return 'Bivariate random walk'
868
+
869
+ def computeForwardPrior (self , posterior , t ):
870
+ """
871
+ Compute new prior from old posterior (moving forwards in time).
872
+
873
+ Args:
874
+ posterior(ndarray): Parameter distribution from current time step
875
+ t(int): integer time step
876
+
877
+ Returns:
878
+ ndarray: Prior parameter distribution for subsequent time step
879
+ """
880
+
881
+ # if hyper-parameter values have changed, a new convolution kernel needs to be created
882
+ if not self .kernelParameters == self .hyperParameterValues :
883
+ normedSigma1 = self .hyperParameterValues [0 ] / self .latticeConstant [0 ]
884
+ normedSigma2 = self .hyperParameterValues [1 ] / self .latticeConstant [1 ]
885
+
886
+ self .kernel = self .createKernel (normedSigma1 , normedSigma2 , self .hyperParameterValues [2 ])
887
+ self .kernelParameters = deepcopy (self .hyperParameterValues )
888
+
889
+ newPrior = convolve2d (posterior , self .kernel , mode = 'same' )
890
+ newPrior /= np .sum (newPrior )
891
+ return newPrior
892
+
893
+ def computeBackwardPrior (self , posterior , t ):
894
+ return self .computeForwardPrior (posterior , t - 1 )
895
+
896
+ @staticmethod
897
+ def createKernel (sigma1 , sigma2 , rho ):
898
+ rv = multivariate_normal (cov = [[sigma1 ** 2. , rho * sigma1 * sigma2 ],
899
+ [rho * sigma1 * sigma2 , sigma2 ** 2. ]])
900
+
901
+ x = np .arange (- 3 * np .ceil (sigma1 ), 3 * np .ceil (sigma1 ) + 1 )
902
+ y = np .arange (- 3 * np .ceil (sigma2 ), 3 * np .ceil (sigma2 ) + 1 )
903
+
904
+ xv , yv = np .meshgrid (x , y , sparse = False , indexing = 'ij' )
905
+
906
+ kernel = rv .pdf (np .array ([xv , yv ]).T ).T
907
+ kernel /= np .sum (kernel )
908
+ return kernel
0 commit comments