Exercise 1 (5 points): Power, coherence and partial coherence based on multitaper spectra.

Consider the vector AR(3) process (“~ 20 Hz β-network”)

$$ \begin{align} X_{1,t} &= 0.95\sqrt(2)X_{1,t−1} − 0.9025X_{1,t−2} + ε_{1,t} \\ X_{2,t} &= 0.5X_{1,t−2} + ε_{2,t} \\ X_{3,t} &= −0.4X_{1,t−3} + ε_{3,t} , X_{4,t} = −0.5X_{1,t−2} + 0.25\sqrt(2)X_{4,t−1} + 0.25\sqrt(2)X_{5,t−1} + ε_{4,t} \\ X_{5,t} &=−0.25\sqrt(2)X_{4,t−1}+0.25\sqrt(2)X_{5,t−1}+ε_{5,t} \end{align} $$

where $X_{t}$ is a 5-dimensional column vector. The noise vector is Gaussian white with diagonal covariance matrix, where the diagonal entries correspond to [0.6, 0.5, 0.3, 0.3, 0.6]. (Note: a diagonal matrix is used here for simplicity. We could explore instantaneous noise correlations with a nondiagonal matrix.)

  1. (1 point) Is the process stationary? Check that the VAR(3) is stable.
  2. (1 point) Using the script provided in the warm-up code, sample 200 trials, each 3-second long with a sampling rate of 200 Hz. The initial condition can be zero mean unit variance Gaussian. Remember to throw way the first 200 samples or so to remove the transient. Plot a single trial showing all of the 5 channels. For sampling, we define the coefficient matrix in Matlab as (M = 5 is the dimension of the AR process of order = 3)

     p = 3 #order of AR(p)
     dim = 5 #number of random variables
     A = zeros(Float64, dim, dim, p)
     a = sqrt(2)
    
     A[1,1,1] =  0.95*a
     A[1,1,2] = -0.9025
     A[2,1,2] =  0.5
     A[3,1,3] = -0.4
     A[4,1,2] = -0.5
     A[4,4,1] =  0.25*a
     A[4,5,1] =  0.25*a
     A[5,4,1] = -0.25*a
     A[5,5,1] =  0.25*a

    Adapt the script provided in the warm-up code to estimate the spectral matrix based on the multitaper method. Use the provided function “multitaperSpectrum.m” and a 5 Hz bandwidth.

  3. (3 points) Compute and plot the power spectrum, spectral coherence and partial spectral coherence based on the estimated spectral matrix. (Your results should replicate the figure shown in the Lecture 5.)


In [1]:
using Distributions
using PlotlyJS


Plotly javascript loaded.

To load again call

init_notebook(true)

Question 1

  • Is the process stationary? Check that the VAR(3) is stable.

R: The process is stationary since the spectral radius of the companion matrix, ρ(Ac) = 0.95, is less than 1. See code below


In [2]:
# AR matrices
p = 3 #order of AR(p)
dim = 5 #number of random variables
T=3# Trial duration in seconds
ntrials=200
Fs=200# sampling rate in per second
dt=1/Fs
burnin=1000 # for transient removal
df = 1/T; #frequency resolution
N=T*Fs


Out[2]:
600

In [3]:
# Use companion matrix to check for stability
A = zeros(Float64, dim, dim, p)
a = sqrt(2)

A[1,1,1] =  0.95*a
A[1,1,2] = -0.9025
A[2,1,2] =  0.5
A[3,1,3] = -0.4
A[4,1,2] = -0.5
A[4,4,1] =  0.25*a
A[4,5,1] =  0.25*a
A[5,4,1] = -0.25*a
A[5,5,1] =  0.25*a

#Companion matrix
m,n,p = size(A)
pn = (p-1)*m
Ac = [reshape(A,m,p*n); eye(pn) zeros(pn,m)];

In [4]:
#stationary?
D, V = eig(Ac)

#spectracl radius
ρ = maximum(abs.(D))
if ρ < 1.0
    println("Process is Stationary")
else
    println("Process is NOT Stationary")
end
    
println("Spectral Radius ρ(Ac) = ", ρ)


Process is Stationary
Spectral Radius ρ(Ac) = 0.9500000000000005

Question 2 (b)(1 point)

  • Using the script provided in the warm-up code, sample 200 trials, each 3-second long with a sampling rate of 200 Hz. The initial condition can be zero mean unit variance Gaussian. Remember to throw way the first 200 samples or so to remove the transient.
  • Plot a single trial showing all of the 5 channels.
  • Adapt the script provided in the warm-up code to estimate the spectral matrix based on the multitaper method. Use the provided function “multitaperSpectrum.m” and a 5 Hz bandwidth.

In [5]:
# Sampling from the VAR(3) model
# The noise covariance (Using DIAGONAL COVARIANCE MATRIX FOR SIMPLICITY ...)
ntrials = 200
Σ_diag = [0.6, 0.5, 0.3, 0.3, 0.6]
μ = zeros(Float64, dim)
d = MvNormal(μ, Σ_diag)


Out[5]:
DiagNormal(
dim: 5
μ: [0.0, 0.0, 0.0, 0.0, 0.0]
Σ: [0.36 0.0 … 0.0 0.0; 0.0 0.25 … 0.0 0.0; … ; 0.0 0.0 … 0.09 0.0; 0.0 0.0 … 0.0 0.36]
)

In [6]:
X = zeros(Float64, dim, N+burnin, ntrials)

for r=1:ntrials
    #Multivariate GWN sequence of dimension (dim x cases)
    E=rand(d, N+burnin) 
    
    #Intial state  - first "p" times
    X[:,1:p,r]=E[:,1:p]   
    
    #Iterate through time 
    for k=p+1:N+burnin 
        x_k=zeros(Float64, dim)
        
        #autoregression
        for j=1:p 
            x_k=x_k+A[:,:,j]*X[:,k-j,r] #5-dim
        end
        
        X[:,k,r]=x_k + E[:,k]
    end
end

#remove burn-in
X=X[:,burnin+1:end,:];

Single trial showing all of the 5 channels.


In [7]:
#Plot the path
plots = Vector{typeof(plot())}(dim)
for d=1:dim
    plots[d] = plot(0:dt:dt*N, X[d,:,1],  line_width=0.8, name="X$d");
end


all_plots = [plots...]
all_plots.plot.layout["title"] = "VAR(3)"
all_plots.plot.layout["xaxis5_title"] = "time"
all_plots


Out[7]:

Spectral matrix based on the multitaper method - bandwidth = 5Hz

  • Note: Partial coherence seems different to that in lectures? Should the max be 1?

In [8]:
using DSP
using FFTW
include("transform_stats.jl")
include("multitaper.jl")


WARNING: deprecated syntax "inner constructor PowerSpectrum(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:114".
Use "PowerSpectrum{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PowerSpectrumVariance(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:146".
Use "PowerSpectrumVariance{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor CrossSpectrum(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "CrossSpectrum{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor CrossSpectrum(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "CrossSpectrum{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor Coherency(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:203".
Use "Coherency{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor Coherency(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:204".
Use "Coherency{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor Coherence(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:203".
Use "Coherence{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor Coherence(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:204".
Use "Coherence{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLV(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "PLV{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLV(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "PLV{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PPC(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "PPC{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PPC(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "PPC{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLI(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "PLI{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLI(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "PLI{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLI2Unbiased(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "PLI2Unbiased{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor PLI2Unbiased(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "PLI2Unbiased{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor WPLI(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "WPLI{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor WPLI(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "WPLI{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor WPLI2Debiased(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:50".
Use "WPLI2Debiased{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor WPLI2Debiased(...) around /Users/mrestrep/dropbox_personal/BrownAgain/Projects/NEUR2110/neur2110_code.jl/homework3/transform_stats.jl:51".
Use "WPLI2Debiased{T}(...) where T" instead.
Out[8]:
coherence (generic function with 1 method)

In [9]:
#need to permute dims because multitaper expects matrix of dims =  samples x channels x trials
A = permutedims(X, [2,1,3])
fNyquist=Fs/2;
freqrange = 0:df:fNyquist#frequency grid
bandWidth = 5
NFFT=N

(xs, s) = multitaper(A, [CrossSpectrum(), PowerSpectrum()], Fs, tapers = dpss(size(A, 1), bandWidth), nfft = N);

In [10]:
#  Coompute the coherence and partial coherence 
pairs = allpairs(dim)
cop = zeros(Float64, length(freqrange), size(pairs, 2))
co = zeros(Float64, length(freqrange), size(pairs, 2))


for i = 1:size(pairs, 2)
    ch1 = pairs[1, i]
    ch2 = pairs[2, i]
    i1=setdiff(1:dim,ch1)
    i2=setdiff(1:dim,ch2)
    for f=1:length(freqrange)
        S = eye(Complex{Float64}, dim).*s[f,:]
        for j = 1:size(pairs, 2)
            S[pairs[1, j], pairs[2, j]] = xs[f, j]
            S[pairs[2, j], pairs[1, j]] = conj(xs[f, j])
        end
        M12 = det(S[i1,i2])
        M11 = det(S[i1,i1])
        M22 = det(S[i2,i2])          
        co[f, i] = abs(xs[f, i])^2/(s[f, ch1]*s[f, ch2])
        cop[f, i] = abs(M12)/sqrt(abs(M11)* abs(M22))
    end                
end

In [11]:
#---------------- Plot Coherence and Partial Coherence---------------------
nplots_c = size(pairs, 2)
plots_c = Vector{typeof(plot())}(nplots_c)
for i = 1:nplots_c
    ch1 = pairs[1, i]
    ch2 = pairs[2, i]
    c_trace = scatter(x = collect(freqrange), y=co[:, i], line_width=0.8, name="X$i", marker_color="red")
    cp_trace = scatter(x=collect(freqrange), y=cop[:, i], line_width=0.8, name="X$i", marker_color="green");

    plots_c[i] = plot([c_trace, cp_trace]);
end

#---------------- Plot Power Spectrum---------------------
plots_s = Vector{typeof(plot())}(dim)
for i=1:dim
    plots_s[i] = plot(collect(freqrange), s[:, i], line_width=0.8, name="X$i", marker_color="blue");
end


all_plots = [plots_s[1] plots_c[1] plots_c[2] plots_c[3] plots_c[4]; 
             plots_c[1] plots_s[2] plots_c[5] plots_c[6] plots_c[7];
             plots_c[2] plots_c[5] plots_s[3] plots_c[8] plots_c[9];
             plots_c[3] plots_c[6] plots_c[8] plots_s[4] plots_c[10];
             plots_c[4] plots_c[7] plots_c[9] plots_c[10] plots_s[5]]
all_plots.plot.layout["title"] = "Power(blue) , Square-Coherence(red), Partial-Coherence (green)"
all_plots.plot.layout["showlegend"] = false
all_plots


Out[11]:

Exercise 2 (5 points): Power, coherence and partial coherence computed from estimated VAR(p) models.

Based on the sample from the model generated in exercise (1),

  1. Adapt the script provided in the warm-up code to estimate the VAR(p) model using the maximum entropy method for the multivariate case. Use the provided function “var_maxent.m”. This function expects the data X to be a 3D array in the format of X(M,N,R), where M = 5 is the dimension of the model, N is number of samples per trial, and R is the number of trials. Based on the estimated coefficient matrices and noise covariance:
    1. (2 points) Compute the transfer function and from it the spectral matrix (see script in warm-up code). Note: because of numerical issues, you will need to enforce the diagonal of the spectral matrix to be real. The matlab function “real.m” can be used for that purpose. Compute the corresponding (auto) power spectrum for each of the 5 components.
    2. (3 points) As in exercise 1(d), compute and plot the power spectrum, spectral coherence and partial spectral coherence, but now based on the AR(p) estimated spectral matrix. Your results should replicate the figure shown in Lectures 5-6.

In [12]:
include("var_maxent.jl");

In [13]:
# Estimating VAR(p) from data ===================
df_est=0.001#frequency resolution to be used in the parametric AR estimation
nF=Int(round(1/df_est+1))#number of frequencies evaluated in the parametric AR estimation

Ah,SIGMAh,Eh = var_maxent(X, p)
DSIG = det(SIGMAh)# residuals covariance matrix determinant
if DSIG <= 0
    warn("Residuals covariance not positive definite")
end
M=N*ntrials
L= -(M/2)*log(DSIG) #max loglikelihood
aic = -2*L + 2*p*dim^2*(M/(M-p-1)) # Note AIC without correction = -2*L + 2*order*xDim^2
bic = -2*L + p*log(M)
# Transfer function H(f) and spectral matrix S(f) from fitted model
# F=collect(0:nF-1)*df*Fs/2
delta=1
F=linspace(0,0.5,nF);
j=0
Sh = zeros(Complex{Float64}, dim, dim, nF)
for f in F
    j=j+1
    H = eye(dim) # identity matrix
    for m=1:p
        H=H-Ah[:,:,m]*exp(-1im*m*2*pi*f)        
    end
    H = inv(H)
    #S(:,:,j) = H*SIGMAh*ctranspose(H) 
    Sh[:,:,j] = H*SIGMAh*H'
    #Note: MATLAB (julia - should also do the same through multiple dispatch)
    #1. the transpose in  will also recognize the complex data and perform the conjugate transpose
    #2. however, that .' implements only the transpose, not the conjugate transpose
end
Sh = 2 * delta^2 * 1/(N*delta) * Sh #Normalize the spectral matrix
for k = 1:dim
    Sh[k,k,:] = real(Sh[k,k,:])
end #make sure the diagonal has real numbers (power spectrum)
# F=F*Fs# Change frequency to Hz;

In [14]:
# To compute the partial coherence =======================
# S is the spectral matrix S(channel j, channel k, frequency), j,k = 1, 2, ... xDim

Ch = zeros(Float64, nF, size(pairs, 2))
CPh = zeros(Float64, nF, size(pairs, 2))
Ph = zeros(Float64, nF, dim)


for i = 1:size(pairs, 2)
    j = pairs[1, i]
    k = pairs[2, i]
    i1=setdiff(1:dim,j)
    i2=setdiff(1:dim,k)
    for f=1:nF
        Mjk = det(Sh[i1,i2,f])
        Mjj = det(Sh[i1,i1,f])
        Mkk = det(Sh[i2,i2,f]) 
        Ch[f, i] = abs(Sh[j,k,f])^2/real(Sh[j,j,f]* Sh[k,k,f])
        CPh[f, i] = abs(Mjk)/real(sqrt(Mjj*Mkk))#to avoid numerical issues, force it to be real 
    end                
end

for j=1:dim
    for f=1:nF
        Ph[f,j] = abs(Sh[j,j,f])
    end                
end

In [15]:
#---------------- Plot Coherence and Partial Coherence---------------------
nplots_c = size(pairs, 2)
plots_c = Vector{typeof(plot())}(nplots_c)
for i = 1:nplots_c
    ch1 = pairs[1, i]
    ch2 = pairs[2, i]
    c_trace = scatter(y=Ch[:, i], line_width=0.8, name="X$i", marker_color="red")
    cp_trace = scatter(y=CPh[:, i], line_width=0.8, name="X$i", marker_color="green");

    plots_c[i] = plot([c_trace, cp_trace]);
end

#---------------- Plot Power Spectrum---------------------
plots_s = Vector{typeof(plot())}(dim)
for i=1:dim
    plots_s[i] = plot(Ph[:, i], line_width=0.8, name="X$i", marker_color="blue");
end


all_plots = [plots_s[1] plots_c[1] plots_c[2] plots_c[3] plots_c[4]; 
             plots_c[1] plots_s[2] plots_c[5] plots_c[6] plots_c[7];
             plots_c[2] plots_c[5] plots_s[3] plots_c[8] plots_c[9];
             plots_c[3] plots_c[6] plots_c[8] plots_s[4] plots_c[10];
             plots_c[4] plots_c[7] plots_c[9] plots_c[10] plots_s[5]]
all_plots.plot.layout["title"] = "Power(blue) , Square-Coherence(red), Partial-Coherence (green)"
all_plots.plot.layout["showlegend"] = false
all_plots


Out[15]:

In [ ]: