├── .gitignore ├── LICENSE.md ├── README.md ├── example └── demo.m ├── filtering_maximization ├── Decoder.m ├── FIS_modified.m ├── KalmanAndPointProcessMultiscaleFilter.m ├── KalmanPrediction.m ├── Maximization_diag_bias.m ├── Maximization_nondiag_bias.m ├── Obj_Func_Fast_Matlab.m └── PointProcessFilter.m ├── functions_main └── EM_multiscale_unsupervised_function.m └── generate_state_space ├── Acont_fromEIG.m ├── Generate_Poles_discreteplane.m ├── NeuronSigSim_Multi_Open.m ├── NeuronSigSim_Open.m ├── build_statespace_realiz.m ├── generate_spike_field_from_statespace.m ├── linear_obs_generator.m ├── spike_generator.m ├── state_generator.m └── theta_generator_onlymodes.m /.gitignore: -------------------------------------------------------------------------------- 1 | # External dependencies 2 | external/ 3 | 4 | *.fig 5 | *.gif 6 | *.mat 7 | ##--------------------------------------------------- 8 | ## Remove autosaves generated by the Matlab editor 9 | ## We have git for backups! 10 | ##--------------------------------------------------- 11 | 12 | # psb log files 13 | *.o* 14 | *.o* 15 | 16 | # Atom editor files 17 | .idea* 18 | 19 | 20 | # Zip files 21 | *.zip 22 | 23 | 24 | # Windows default autosave extension 25 | *.asv 26 | 27 | # OSX / *nix default autosave extension 28 | *.m~ 29 | 30 | # Compiled MEX binaries (all platforms) 31 | *.mex* 32 | 33 | # Simulink Code Generation 34 | slprj/ 35 | 36 | # Session info 37 | octave-workspace 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This software is Copyright © 2020 The University of Southern California. All Rights Reserved. 2 | 3 | Permission to use, copy, modify, and distribute this software and its documentation for educational, research 4 | and non-profit purposes, without fee, and without a written agreement is hereby granted, provided that the 5 | above copyright notice, this paragraph and the following three paragraphs appear in all copies. 6 | 7 | Permission to make commercial use of this software may be obtained by contacting: 8 | USC Stevens Center for Innovation 9 | University of Southern California 10 | 1150 S. Olive Street, Suite 2300 11 | Los Angeles, CA 90115, USA 12 | 13 | This software program and documentation are copyrighted by The University of Southern California. The software 14 | program and documentation are supplied "as is", without any accompanying services from USC. USC does not warrant 15 | that the operation of the program will be uninterrupted or error-free. The end-user understands that the program 16 | was developed for research purposes and is advised not to rely exclusively on the program for any reason. 17 | 18 | IN NO EVENT SHALL THE UNIVERSITY OF SOUTHERN CALIFORNIA BE LIABLE TO ANY PARTY FOR 19 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 20 | PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE 21 | UNIVERSITY OF SOUTHERN CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 22 | DAMAGE. THE UNIVERSITY OF SOUTHERN CALIFORNIA SPECIFICALLY DISCLAIMS ANY 23 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED 25 | HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF SOUTHERN CALIFORNIA HAS NO 26 | OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 27 | MODIFICATIONS. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Multiscale EM algorithm 3 | 4 | This repository contains the code for the multiscale expectation-maximization (EM) algorithm to extract latent states (and learn the multiscale state-space model) from the combined spiking activity (in form of binary point process Poisson observations) and field activity (in form of continuous linear Gaussian observations, e.g., local field potentials or LFP). 5 | 6 | This algorithm is used for the paper "[Multiscale low dimensional motor cortical state dynamics predict naturalistic reach-and-grasp behavior](https://www.nature.com/articles/s41467-020-20197-x)". The mathematical derivation of the algorithm could be found [here](https://ieeexplore.ieee.org/abstract/document/8698887). 7 | 8 | ## Installation guide 9 | 10 | To use the software, you just need to download this repository or clone the repository using git. Make sure to include all the repository folders to the path. For a normal desktop computer that already has MATLAB installed on it, this process takes less than 5 mins. Installing MATLAB usually takes about a couple hours. 11 | 12 | ## Dependencies 13 | 14 | This repository does not need any extra dependencies apart from the built-in MATLAB functions. 15 | 16 | ## User guide 17 | 18 | The main function is located at [EM_multiscale_unsupervised_function.m](https://github.com/SalarAbb/emrelease/blob/main/functions_main/EM_multiscale_unsupervised_function.m). To get familiar with how the algorithm works, there exists a demo script [demo.m](https://github.com/SalarAbb/emrelease/blob/main/example/demo.m) which generates a simulated time-series of multiscale spike-LFP activity from a state-space model, learns it with the multiscale EM algorithm and visualizes the learned eigenvalues compared to the true systems eigenvalues. To run on your data, you just need to prepare the lfp features time-series (Y) and spike time-series (N), and set some settings parameters of your interest. The detailed instructions could be found in the demo script and also inside functions. 19 | 20 | ## Version compatibility 21 | 22 | The code has been tested on the following MATLAB and operating system versions. 23 | - MATLAB 2017a, Windows 10 Pro (version 1909) 24 | - MATLAB 2019b, Windows 10 Pro (version 1909) 25 | - MATLAB 2018b, CentOS Linux (version 7) 26 | 27 | ## License 28 | Copyright (c) 2020 University of Southern California 29 | See full notice in [LICENSE.md](LICENSE.md) 30 | Hamidreza Abbaspourazad and Maryam M. Shanechi 31 | Shanechi Lab, University of Southern California 32 | -------------------------------------------------------------------------------- /example/demo.m: -------------------------------------------------------------------------------- 1 | % a light demonstration of how to use multiscale EM algorithm 2 | % this script first generates an spontaneopus state-space model 3 | % it then learns the parameters with multiscale EM algorithm 4 | % and finally visualize the learned eigenvalues 5 | %% OPTIONS for generating a spontaneous state-space model 6 | OPTIONS = struct; 7 | OPTIONS.dim_hid = 8;% dim of hidden state 8 | OPTIONS.dim_hid_fit = 8;% dim of hidden state for fitting (this can be different from what we simulate data with) 9 | OPTIONS.dim_inp = 2;% dim of input (arbitrary) 10 | OPTIONS.dim_Y = 150;% number of LFP features 11 | OPTIONS.dim_N = 30;% nummber of neurons 12 | OPTIONS.scale_dif = 5;% scale difference between spike and lfp 13 | OPTIONS.iteration_stop = 100 ;% number of iterations to run EM for 14 | OPTIONS.delta = 0.002;% time step 15 | OPTIONS.scale = 'MS';% generate multiscale observations 16 | OPTIONS.Ttrain = 50000; % number of samples in training set 17 | OPTIONS.spike_modulation = 'modesonly'; % do not change this 18 | OPTIONS.normalization_c = 'peaktopeak'; %'std' or 'peaktopeak' or 'version1' 'predefined' %this is to determine how i normalize matrix C 19 | OPTIONS.switch_nondiag = 0; % some settings for EM (explained in EM_multiscale_unsupervised_function.m) 20 | OPTIONS.switch_nondiagQ = 0;% some settings for EM (explained in EM_multiscale_unsupervised_function.m) 21 | OPTIONS.switch_biasobs = 0;% some settings for EM (explained in EM_multiscale_unsupervised_function.m) 22 | OPTIONS.init_type = 'random'; %'subspace' or 'random', % some settings for EM (explained in EM_multiscale_unsupervised_function.m) 23 | OPTIONS.save_iter = 'startandend';% some settings for EM (explained in EM_multiscale_unsupervised_function.m) 24 | OPTIONS.spike_bs_init = 'random';% 'random' or 'meanFR', % some settings for EM (explained in EM_multiscale_unsupervised_function.m) 25 | OPTIONS.number_systems = 1; % always set to 1 26 | %% generate (or load) a sample state space model 27 | [ REALIZ, TRUE ] = generate_spike_field_from_statespace( OPTIONS ); 28 | Y_train = REALIZ.Y_Obs; 29 | N_train = REALIZ.N_Obs; 30 | %% use EM algorithm which saves all parameters at iterations and also saves last step iteration parameters 31 | handles = struct; 32 | handles.dim_hid = OPTIONS.dim_hid_fit; 33 | handles.scale_dif_inp = OPTIONS.scale_dif; 34 | handles.delta_inp = OPTIONS.delta; 35 | handles.num_iter = OPTIONS.iteration_stop; 36 | handles.switch_nondiagQ = OPTIONS.switch_nondiagQ; 37 | handles.switch_nondiag = OPTIONS.switch_nondiag; 38 | handles.init_type = OPTIONS.init_type; 39 | handles.switch_biasobs = OPTIONS.switch_biasobs; 40 | handles.save_iter = OPTIONS.save_iter; 41 | handles.spike_bs_init = OPTIONS.spike_bs_init; 42 | %% fits the model with EM 43 | [ resultsEM ,~,ITER ] = EM_multiscale_unsupervised_function( Y_train,N_train,handles); 44 | %% plot learned eigs 45 | fig = figure('Position',[50,248,783,752]); 46 | S1 = scatter( real(eig( resultsEM.A )) , imag( eig(resultsEM.A) ),100);hold on; 47 | S1.MarkerEdgeColor = [76,0,153]/255; 48 | S1.MarkerFaceColor = [76,0,153]/255; 49 | S2 = scatter( real(eig( TRUE.A )) , imag(eig( TRUE.A )),100); 50 | S2.MarkerEdgeColor = [0,255,255]/255; 51 | S2.MarkerFaceColor = [0,255,255]/255; 52 | legend([S1,S2],{'Learned modes','True modes'}) 53 | xlim([0.90,1]); 54 | xticks((0.90:0.02:1)); 55 | ylim([0,0.08]); 56 | yticks((0:0.02:0.08)); 57 | xlabel('Real') 58 | ylabel('Imaginary') 59 | title('learned modes vs. true modes') 60 | set(gca,'view',[-55.9,90]);grid on 61 | -------------------------------------------------------------------------------- /filtering_maximization/Decoder.m: -------------------------------------------------------------------------------- 1 | function [ Xupd_t,Xpred_t,Covupd_t,Covpred_t ] = Decoder(A,B,Q,Init_X,Init_Cov,C,D,R,Theta,Y_Obs,N_Obs,settings ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % Multiscale decoder for multiscale state space models as explained in this paper: 9 | % 10.1088/1741-2552/aaeb1a 10 | % 11 | % In general, the states are infered with Bayesian inference and Laplace approximation 12 | % the state space model is as follows: 13 | % 14 | % x_{t + 1} = A * x_{t} + q_t; COV(q_t) = Q 15 | % y_{t} = C * x_{t} + r_t; COV(r_t) = R 16 | % p(N_{t}|x_{t}) = (\lambda(x_{t})) ^ (N_{t}) * exp( -\lambda(x_{t}) * \Delta ); \lambda(x_{t}) = exp(-\alpha*x_{t} + \beta), \Delta: time-step 17 | % 18 | % INPUTS : 19 | % - A: state transition matrix (dim * dim) 20 | % - B: state-input matrix (default: zeros/not learned) (dim * dim_inp) 21 | % - Q: state noise covariance matrix (dim * dim) 22 | % - Init_X: initial estimation of the latent state at t = 1 (dim * 1) 23 | % - Init_Cov: initial estimation of the latent state estimation error at t = 1 (dim * dim) 24 | % - C: observation emission matrix (dim_Y * dim) 25 | % - D: observation-input matrix (default: zeros/not learned) (dim_Y * dim_inp) 26 | % - R: observation noise covariance matrix (dim_Y * dim_Y) 27 | % - Theta: parameters of spike modulation -> [\beta_c;alpha_c] in each column for every neuron ( (dim + 1) * N) 28 | % - Y_Obs: the zero-meaned lfp observations (dim_Y * T) 29 | % - N_Obs: binary spiking observation (dim_Y * N) 30 | % - settings: struct with following fields: 31 | % - Scale_dif: difference in time-scale of spike and lfp: 32 | % scale difference in spikes and LFPs, k, i.e., spikes are available at every time-step and lfp are available 33 | % only at k, 2k, 3k, ... 34 | % - delta: timescale of dynamics, or sampling in seconds 35 | % - Input: input time-series 36 | % OUTPUTS: 37 | % - X_upd_t: filtered values of states (causal inference) (dim * T) 38 | % - X_pred_t: filtered values of states (causal inference, prediction before update with observation at t) (dim * T) 39 | % - Covupd_t: covariance of error of filtered values of states (causal inference) (dim * dim * T) 40 | % - X_upd_t: covariance of error of filtered values of states (causal inference, prediction before update with observation at t) (dim *dim * T) 41 | 42 | %% get some values 43 | T = size(N_Obs,2); 44 | [dim,~] = size(A); 45 | Scale_dif = settings.Scale_dif; 46 | delta = settings.delta; 47 | %d_init=settings.d_init; 48 | Input = settings.Input; 49 | 50 | %% whitening lfp observations -> solve many numerical errors and is much faster 51 | %this part is an optional for future i need to put a settings parameter 52 | [eigvec_R,eigval_R,~] = svd(R); 53 | eigval_R_vectorized = diag(eigval_R); 54 | 55 | index_ill = find(eigval_R_vectorized<10^(-12),1); 56 | 57 | eigval_R_new_vectorized = eigval_R_vectorized; 58 | eigval_R_new_vectorized(index_ill:end) = []; 59 | R_new = diag(eigval_R_new_vectorized); 60 | 61 | transform_matrix = eigvec_R; 62 | transform_matrix(:,index_ill:end) = []; 63 | 64 | % whiten 65 | R = R_new; 66 | C = transform_matrix' * C; 67 | Y_Obs = transform_matrix' * Y_Obs; 68 | D = transform_matrix' * D; 69 | %% start filtering 70 | % set the place holders 71 | Xupd_t = zeros(dim,T); 72 | Xpred_t = zeros(dim,T); 73 | Covupd_t = zeros(dim,dim,T); 74 | Covpred_t = zeros(dim,dim,T); 75 | % set the initial values 76 | Xupd_t(:,1) = Init_X; 77 | Covupd_t(:,:,1) = Init_Cov; 78 | 79 | % run it iteratively 80 | for i=2:T 81 | 82 | if (floor(i/Scale_dif) - i/Scale_dif) == 0 83 | % in this case multiscale filter will run because we have spike and lfp 84 | % predict 85 | [Xpred_t(:,i),Covpred_t(:,:,i)] = KalmanPrediction(A,B,Q,Xupd_t(:,i-1),squeeze(Covupd_t(:,:,i-1)),Input(:,i)); 86 | % correct based on input 87 | Y_Obs_sub = Y_Obs(:,i) - D * Input(:,i); 88 | % update 89 | [Xupd_t(:,i),Covupd_t(:,:,i),ill_level] = KalmanAndPointProcessMultiscaleFilter(Xpred_t(:,i),squeeze(Covpred_t(:,:,i)),Y_Obs_sub,C,R,transpose(N_Obs(:,i)),Theta,delta,0); 90 | else 91 | % in this case only the point process filter will run 92 | % predict 93 | [Xpred_t(:,i),Covpred_t(:,:,i)] = KalmanPrediction(A,B,Q,Xupd_t(:,i-1),squeeze(Covupd_t(:,:,i-1)),Input(:,i)); 94 | % update 95 | [Xupd_t(:,i),Covupd_t(:,:,i),ill_level] = PointProcessFilter(Xpred_t(:,i),squeeze(Covpred_t(:,:,i)),transpose(N_Obs(:,i)),Theta,delta); 96 | end 97 | 98 | end 99 | 100 | end 101 | 102 | -------------------------------------------------------------------------------- /filtering_maximization/FIS_modified.m: -------------------------------------------------------------------------------- 1 | function [ Xsmth_t,Covsmth_t,VarW_t,CorW_t ] = FIS_modified( Xupd_t,Xpred_t,Covupd_t,Covpred_t,A,settings ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % this function is the fast implementation of fixed interval smoother: 9 | % for details read 10.1109/TNSRE.2019.2913218, equations (17)-(20) 10 | % INPUTS : 11 | % inputs are outputs of Decoder.m, you can read there fore more info 12 | % - X_upd_t: filtered values of states (causal inference) (dim * T) 13 | % - X_pred_t: filtered values of states (causal inference, prediction before update with observation at t) (dim * T) 14 | % - Covupd_t: covariance of error of filtered values of states (causal inference) (dim * dim * T) 15 | % - X_upd_t: covariance of error of filtered values of states (causal inference, prediction before update with observation at t) (dim *dim * T) 16 | % - A: state transition matrix (dim * dim) 17 | % - settings: unused but for future use! 18 | % OUTPUTS: 19 | % these 20 | % - Xsmth_t: smoothed values of states (non-causal inference) (dim * T) 21 | % - Covsmth_t: covariance of error of smoothed values of states (non-causal inference) (dim * dim * T) 22 | % - VarW_t: variance of latent states (dim * dim * T) 23 | % - CorW_t: cross-covariance of latent states from adjacant steps (dim * dim * T-1) 24 | %% get some values 25 | [dim, T] = size(Xupd_t); 26 | %% create place holders 27 | Xsmth_t = zeros(dim, T); 28 | Covsmth_t = zeros(dim, dim, T); 29 | Varw_t = zeros(dim, dim, T); 30 | CorW_t = zeros(dim, dim, T-1); 31 | 32 | Xsmth_t(:, T) = Xupd_t(:, T); 33 | Covsmth_t(:, :, T) = Covupd_t(:, :, T); 34 | A_aux = zeros(dim,dim); 35 | 36 | % start from t = T 37 | VarW_t(:,:,T)=Covsmth_t(:,:,T)+Xsmth_t(:,T)*transpose(Xsmth_t(:,T)); 38 | 39 | for i=T-1:-1:1 40 | if rcond(Covpred_t(:,:,i+1))<10^(-12) 41 | A_aux=0; 42 | else 43 | % equation (12) 44 | A_aux=Covupd_t(:,:,i)*transpose(A)*inv((Covpred_t(:,:,i+1))); 45 | end 46 | % equation (13) 47 | Xsmth_t(:,i)=Xupd_t(:,i)+A_aux*(Xsmth_t(:,i+1)-Xpred_t(:,i+1)); 48 | % equation (14) 49 | Covsmth_t(:,:,i)=Covupd_t(:,:,i)+A_aux*(Covsmth_t(:,:,i+1)-Covpred_t(:,:,i+1))*transpose(A_aux); 50 | 51 | auxCov=A_aux*Covsmth_t(:,:,i+1); 52 | % equation (19) 53 | VarW_t(:,:,i)=Covsmth_t(:,:,i) + Xsmth_t(:,i)*transpose(Xsmth_t(:,i)); 54 | % equation (20) 55 | CorW_t(:,:,i)=auxCov' + Xsmth_t(:,i+1) * transpose(Xsmth_t(:,i)); 56 | end 57 | 58 | 59 | end 60 | 61 | -------------------------------------------------------------------------------- /filtering_maximization/KalmanAndPointProcessMultiscaleFilter.m: -------------------------------------------------------------------------------- 1 | function [x_esti,W_esti,ill_level] = KalmanAndPointProcessMultiscaleFilter(x_onestep,W_onestep,KF_sig,KF_mat,KF_noise,PPF_sig,PPF_para,PPF_delta,PPF_rate_fac) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This is a multiscale filter including continuous (linear model) and discrete (point process with log-linear firing rate) type of signals. The approximation 9 | % is based on the Lagrange approximation. 10 | % 11 | % 12 | % Inputs: 13 | % x_onestep: The state vector (a column). Its formation is [num_state*1]. 14 | % W_onestep: The corariance matrix of the state vector. Its form is [num_state*num_state]. 15 | % KF_sig: The continuous signal from the linear model. It's a column vector [num_channel]. 16 | % KF_mat: The observation matrix in the linear model the continuous signal. Its form is [num_channel*num_state]. 17 | % KF_noise: The noise covariance at each channel. It can be a 2-dim matrix or a row vector. When it's a row vector, it's been considered as the diagonal of 18 | % the noise covariance matrix. 19 | % PPF_sig: A row vector [1*num_spike]. It records all spike values. 20 | % PPF_para: A 2-dim matrix [num_state+1*num_spike]. It's composed by all parameters in the point process with log-linear firing rate. 21 | % PPF_delta: A scalar. It indicates the time step (real time) in the point process. 22 | % PPF_rate_fac: A row vector [1*num_spike]. It contains the effect of the non-decoding values in the firing rate. 23 | % 24 | % 25 | % Outputs: 26 | % x_esti: The updating state vector (a column). Its formation is [num_state*1]. 27 | % W_esti: The updating corariance matrix of the state vector. Its form is [num_state*num_state]. 28 | % ill_level: A scalar. The illness level of the W_esti. 29 | 30 | % the illness level tolerance. "0" means no constraint. 31 | ill_tole = 10^-12; 32 | 33 | if(iscolumn(PPF_sig)) 34 | PPF_sig = PPF_sig'; 35 | end 36 | 37 | % the covariance matrix of KF 38 | if(isrow(KF_noise)) 39 | inv_KF_noise = diag(1./KF_noise); 40 | else 41 | inv_KF_noise = inv(KF_noise); 42 | end 43 | KF_cov = KF_mat' * inv_KF_noise * KF_mat; 44 | 45 | % the covariance matrix of PPF 46 | % discard the constant part (first row) in the parameter matrix. 47 | PPF_submatrix = PPF_para(2:end,:); 48 | num_state = size(PPF_submatrix,1); 49 | weighted = exp( x_onestep' * PPF_submatrix + PPF_para(1,:) + PPF_rate_fac ); 50 | PPF_cov = ((ones(num_state,1) * weighted).*PPF_submatrix) * PPF_submatrix.'*PPF_delta; % This is a summation process. Matrix can speed up the program. 51 | 52 | % the mean vector of KF 53 | KF_mean = KF_mat' * inv_KF_noise * (KF_sig - KF_mat * x_onestep); 54 | 55 | % the mean vector of PPF 56 | PPF_mean = PPF_submatrix * (PPF_sig - weighted * PPF_delta).'; 57 | 58 | % develop the x_esti and W_esti under the illness level criterion 59 | W_esti = inv(W_onestep) + KF_cov + PPF_cov; 60 | ill_level = rcond(W_esti); 61 | if(ill_level>ill_tole) 62 | W_esti = inv(W_esti); 63 | x_esti = x_onestep + W_esti * (KF_mean + PPF_mean); 64 | else 65 | W_esti = W_onestep; 66 | x_esti = x_onestep; 67 | end 68 | 69 | end -------------------------------------------------------------------------------- /filtering_maximization/KalmanPrediction.m: -------------------------------------------------------------------------------- 1 | function [x_onestep, W_onestep] = KalmanPrediction(A,B,W,x_pre,W_pre,Input) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % About output: 9 | % x_onestep: the one step prediction of the goal at the next step 10 | % W_onestep: the one step prediction about the covariance matrix at the next step 11 | % 12 | % About inputs : 13 | % A,B,W: they are parameters of the linear dynamical system ( x.t+1 = A*x.t+B*u.t+w.t ) 14 | % Q,R: they are symetric matrix coefficients of the cost function ( J = Sigma_all_t_from_1_to_infinum( (|Q)+(|R) ) 15 | % L: the optimal feedback control multiplier. 16 | % x_pre: the last time dynamic state 17 | % W_pre: the last time covariance matrix 18 | % x_goal: the final target of the whole process. Its dimension is the same as the x_pre. 19 | % 20 | % NOTE: In this program, we don't set the stopping time but just only give a one step ahead prediction. The whole process is finite or infinite horizon is depending 21 | % on the calling function, which inputs the corresponding L each time. The reason is that in finite horizon testing, the recursive formula is calculated 22 | % backward, which means that it must be calculated ahead, not on the real time. 23 | % 24 | % NOTE: Split the target terms from the state space model based on two reasons. First, this is a more natural way do dispaly the model. Everything has its own 25 | % state space, but not everyone know where to go; second, split target terms can make the whole simulation more flexible. It can change goal at any time 26 | % in the simulation process after start. 27 | 28 | if(any(any(B)) == 0) % which means that there is no control term in this model and it's a random walk. 29 | 30 | x_onestep = A * x_pre; 31 | W_onestep = A * W_pre * A' + W; 32 | 33 | else 34 | 35 | % Otherwise, calculate the one step prediction of the next time with the gain matrix. 36 | x_onestep = (A) * x_pre + B * Input; 37 | W_onestep = (A) * W_pre *(A)' + W; 38 | 39 | end 40 | 41 | end 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /filtering_maximization/Maximization_diag_bias.m: -------------------------------------------------------------------------------- 1 | function [ A,B,Q,Init_X,Init_Cov,C,bias,D,R,Theta ] = Maximization_diag_bias( Xsmth_t,Covsmth_t,VarW_t,CorW_t,Y_Obs,N_Obs,Previous_Theta,settings ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %This code plays the role of maximization step in the multiscale EM 9 | %algorithm, for the details read: 10.1109/TNSRE.2019.2913218 10 | % INPUTS : 11 | % - Xsmth_t: smoothed values of states (non-causal inference) (dim * T) 12 | % - Covsmth_t: covariance of error of smoothed values of states (non-causal inference) (dim * dim * T) 13 | % - VarW_t: variance of latent states (dim * dim * T) 14 | % - CorW_t: cross-covariance of latent states from adjacant steps (dim * dim * T-1) 15 | % - Y_Obs: time series of the LFPs, its dimension is (dim_Y*T), where dim_Y is the number of channles and T is 16 | % the total number of samples! if time-scale of LFP is k (available 17 | % at k,2k,3k,...), it is recommended to put the samples in between as NaN, however the model will never use them 18 | % - N_Obs: time series of spikes with size N*T, N is number of neurons 19 | % - Previous_Theta: spike modulation matrix from previous iteration, used as initial value of this iteration 20 | % - settings: consists of some settings, with following fields 21 | % - dim_input: arbitrary dimension of input 22 | % - scale_dif : scale difference in spikes and LFPs, k, i.e., spikes are available at every time-step and lfp are available 23 | % only at k, 2k, 3k, ... 24 | % - delta :time step of the spike bins (or real time-scale of sampling/dynamics) (in seconds) 25 | % - Input: arbitrary input (usually zeros(dim_input, T)) 26 | % -switch_nondiagQ: (Always put this as 1, for fast learning!) if it is 1 it means the state noise is non-diagonal, if it is 0 the state noise is diagonal 27 | % -switch_biasobs: whether to fit bias for linear observations, if 1, it learns a linear bias for Y_Obs 28 | % OUTPUTS: 29 | % - A: state transition matrix 30 | % - B: state-input matrix (default: zeros/not learned) 31 | % - Q: state noise covariance matrix 32 | % - Init_X: initial estimation of the latent state at t = 1 33 | % - Init_Cov: initial estimation of the latent state estimation error at t = 1 34 | % - C: observation emission matrix 35 | % - bias: bias of lfp observation 36 | % - D: observation-input matrix (default: zeros/not learned) 37 | % - R: observation noise covariance matrix 38 | % - Theta: parameters of spike modulation -> [\beta_c;alpha_c] in each column for every neuron 39 | %% get some values 40 | [dim,T] = size(Xsmth_t); 41 | dim_Y = size(Y_Obs, 1); 42 | % optimization of B and D are not implemented 43 | B = zeros(dim, settings.dim_input); 44 | D = zeros(dim_Y, settings.dim_input); 45 | % add some place holder 46 | Xsmth_t_bias = [ones(1,settings.T);Xsmth_t]; 47 | % get dim_input 48 | dim_input = settings.dim_input; 49 | Scale_dif = settings.Scale_dif; 50 | N = size(N_Obs, 1); 51 | deltan = settings.delta; 52 | Input = settings.Input; 53 | Theta = zeros(1 + dim,N); 54 | %% create some place holders (the main role of this place holders is to keep track of the sum of all inferrred values from expectation step in time) 55 | Sum_VarW = zeros(dim,dim); 56 | Sum_VarW_bias = zeros(dim + 1,dim + 1); 57 | Sum_CorW = zeros(dim,dim); 58 | Sum_CorYX = zeros(dim_Y,dim); 59 | Sum_CorYX_bias = zeros(dim_Y,dim + 1); 60 | Sum_VarY = zeros(dim_Y,dim_Y); 61 | Sum_VarW_Scale = zeros(dim,dim); 62 | Sum_VarW_Scale_bias = zeros(dim + 1,dim + 1); 63 | Sum_Cor_inp2_Scale = zeros(dim_input,dim); 64 | Sum_Cor_inpout = zeros(dim_Y,dim_input); 65 | Sum_Cor_inp3_Scale = zeros(dim_input,dim_input); 66 | Sum_Cor_inp=zeros(dim_input,dim); 67 | Sum_Cor_inp2=zeros(dim_input,dim); 68 | Sum_Cor_inp3=zeros(dim_input,dim_input); 69 | VarW_t_bias=zeros(dim + 1,dim + 1,T); 70 | %% start the sumations 71 | for i=1:T 72 | 73 | VarW_t_bias(:,:,i)=[1,Xsmth_t(:,i)';Xsmth_t(:,i),VarW_t(:,:,i)]; 74 | 75 | if floor(i/Scale_dif) - i/Scale_dif == 0 76 | Y_Obs_sub = Y_Obs(:,i) - D * Input(:,i); 77 | Sum_CorYX = Sum_CorYX + Y_Obs_sub * transpose(Xsmth_t(:,i)); 78 | Sum_CorYX_bias = Sum_CorYX_bias + Y_Obs_sub * transpose(Xsmth_t_bias(:,i)); 79 | Sum_VarW_Scale = Sum_VarW_Scale + VarW_t(:,:,i); 80 | Sum_VarW_Scale_bias = Sum_VarW_Scale_bias + VarW_t_bias(:,:,i); 81 | Sum_VarY = Sum_VarY + Y_Obs_sub * transpose(Y_Obs_sub); 82 | Sum_Cor_inp2_Scale = Sum_Cor_inp2_Scale + Input(:,i) * Xsmth_t(:,i)'; 83 | Sum_Cor_inpout = Sum_Cor_inpout + Y_Obs(:,i) * Input(:,i)'; 84 | Sum_Cor_inp3_Scale = Sum_Cor_inp3_Scale + Input(:,i) * (Input(:,i))'; 85 | end 86 | if i==T 87 | Sum_VarW = Sum_VarW + VarW_t(:,:,i); 88 | Sum_VarW_bias = Sum_VarW_bias + VarW_t_bias(:,:,i); 89 | else 90 | Sum_VarW = Sum_VarW + VarW_t(:,:,i); 91 | Sum_VarW_bias = Sum_VarW_bias + VarW_t_bias(:,:,i); 92 | Sum_CorW = Sum_CorW + CorW_t(:,:,i); 93 | end 94 | if i~=1 95 | Sum_Cor_inp = Sum_Cor_inp + Input(:,i) * Xsmth_t(:,i - 1)'; 96 | Sum_Cor_inp2 = Sum_Cor_inp2 + Input(:,i) * Xsmth_t(:,i)'; 97 | Sum_Cor_inp3 = Sum_Cor_inp3 + Input(:,i) * (Input(:,i))'; 98 | end 99 | end 100 | %% optimize A 101 | A = (Sum_CorW - B * Sum_Cor_inp) * inv(Sum_VarW - VarW_t(:,:,T)); 102 | %% optimize Q 103 | Qaux = (Sum_VarW - VarW_t(:,:,1)) - A * transpose(Sum_CorW - B * Sum_Cor_inp) - (Sum_CorW - B * Sum_Cor_inp) * transpose(A) +... 104 | A * (Sum_VarW - VarW_t(:,:,T)) * transpose(A) - B * Sum_Cor_inp2 - (B * Sum_Cor_inp2)' + B * Sum_Cor_inp3 * B'; 105 | 106 | if settings.switch_nondiagQ == 0 107 | Inv_Q=zeros(dim,dim); 108 | for i=1:dim 109 | Inv_Q(i,i)=(T-1)/(Qaux(i,i)); 110 | end 111 | Q=inv(Inv_Q); 112 | elseif settings.switch_nondiagQ == 1 113 | Q = Qaux / (T-1); 114 | end 115 | %% optimize Init_X 116 | Init_X=Xsmth_t(:,1); 117 | %% optimize Init_Cov 118 | Init_Cov=VarW_t(:,:,1)-Xsmth_t(:,1)*transpose(Xsmth_t(:,1)); 119 | %% optimize C and R 120 | if settings.switch_biasobs == 1 121 | 122 | C_bias = Sum_CorYX_bias * inv(Sum_VarW_Scale_bias); 123 | 124 | Raux = Sum_VarY - C_bias * transpose(Sum_CorYX_bias) - Sum_CorYX_bias * transpose(C_bias)... 125 | + C_bias * Sum_VarW_Scale_bias * transpose(C_bias); 126 | 127 | Inv_R = zeros(dim_Y,dim_Y); 128 | for j = 1:dim_Y 129 | Inv_R(j,j)=(T/Scale_dif)/(Raux(j,j)); 130 | end 131 | R=inv(Inv_R); 132 | 133 | C = C_bias(:,2:dim+1); 134 | bias = C_bias(:,1); 135 | 136 | elseif settings.switch_biasobs == 0 137 | C = Sum_CorYX * inv(Sum_VarW_Scale); 138 | Raux = Sum_VarY - C * transpose(Sum_CorYX) - Sum_CorYX * transpose(C) + C * Sum_VarW_Scale * transpose(C); 139 | Inv_R = zeros(dim_Y,dim_Y); 140 | for j = 1:dim_Y 141 | Inv_R(j,j) = (T/Scale_dif)/(Raux(j,j)); 142 | end 143 | R = inv(Inv_R); 144 | 145 | C = C; 146 | bias = zeros(dim_Y,1); 147 | end 148 | %% optimize Theta 149 | for i=1:N 150 | % numerical optimization given the cost fn derived at 151 | % Obj_Func_Fast_Matlab.m 152 | myf = @(a) Obj_Func_Fast_Matlab(a, Xsmth_t, Covsmth_t, N_Obs(i,:), deltan, T, dim); 153 | options = optimoptions(@fminunc,'Algorithm','trust-region','GradObj','on','Display','Iter'); 154 | [temp,~,~,output_opt] = fminunc(myf, Previous_Theta(:,i)' , options); 155 | %fprintf('number of function counts : %d',output_opt.funcCount) 156 | Theta(:,i) = temp'; 157 | 158 | end 159 | 160 | 161 | end 162 | -------------------------------------------------------------------------------- /filtering_maximization/Maximization_nondiag_bias.m: -------------------------------------------------------------------------------- 1 | function [ A,B,Q,Init_X,Init_Cov,C,bias,D,R,Theta ] = Maximization_nondiag_bias( Xsmth_t,Covsmth_t,VarW_t,CorW_t,Y_Obs,N_Obs,Previous_Theta,settings ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %This code plays the role of maximization step in the multiscale EM (this function is exactly like Maximization_diag_bias.m, except the R matrix will be nondiag) 9 | %algorithm, for the details read: 10.1109/TNSRE.2019.2913218 10 | % INPUTS : 11 | % - Xsmth_t: smoothed values of states (non-causal inference) (dim * T) 12 | % - Covsmth_t: covariance of error of smoothed values of states (non-causal inference) (dim * dim * T) 13 | % - VarW_t: variance of latent states (dim * dim * T) 14 | % - CorW_t: cross-covariance of latent states from adjacant steps (dim * dim * T-1) 15 | % - Y_Obs: time series of the LFPs, its dimension is (dim_Y*T), where dim_Y is the number of channles and T is 16 | % the total number of samples! if time-scale of LFP is k (available 17 | % at k,2k,3k,...), it is recommended to put the samples in between as NaN, however the model will never use them 18 | % - N_Obs: time series of spikes with size N*T, N is number of neurons 19 | % - Previous_Theta: spike modulation matrix from previous iteration, used as initial value of this iteration 20 | % - settings: consists of some settings, with following fields 21 | % - dim_input: arbitrary dimension of input 22 | % - scale_dif : scale difference in spikes and LFPs, k, i.e., spikes are available at every time-step and lfp are available 23 | % only at k, 2k, 3k, ... 24 | % - delta :time step of the spike bins (or real time-scale of sampling/dynamics) (in seconds) 25 | % - Input: arbitrary input (usually zeros(dim_input, T)) 26 | % -switch_nondiagQ: (Always put this as 1, for fast learning!) if it is 1 it means the state noise is non-diagonal, if it is 0 the state noise is diagonal 27 | % -switch_biasobs: whether to fit bias for linear observations, if 1, it learns a linear bias for Y_Obs 28 | % OUTPUTS: 29 | % - A: state transition matrix 30 | % - B: state-input matrix (default: zeros/not learned) 31 | % - Q: state noise covariance matrix 32 | % - Init_X: initial estimation of the latent state at t = 1 33 | % - Init_Cov: initial estimation of the latent state estimation error at t = 1 34 | % - C: observation emission matrix 35 | % - bias: bias of lfp observation 36 | % - D: observation-input matrix (default: zeros/not learned) 37 | % - R: observation noise covariance matrix 38 | % - Theta: parameters of spike modulation -> [\beta_c;alpha_c] in each column for every neuron 39 | %% get some values 40 | [dim,T] = size(Xsmth_t); 41 | dim_Y = size(Y_Obs, 1); 42 | % optimization of B and D are not implemented 43 | B = zeros(dim,settings.dim_input); 44 | D = zeros(dim_Y,settings.dim_input); 45 | % add some place holder 46 | Xsmth_t_bias = [ones(1,settings.T);Xsmth_t]; 47 | % get dim_input 48 | dim_input = settings.dim_input; 49 | Scale_dif = settings.Scale_dif; 50 | N = settings.N; 51 | deltan = settings.delta; 52 | Input = settings.Input; 53 | Theta = zeros(1 + dim,N); 54 | %% create some place holders (the main role of this place holders is to keep track of the sum of all inferrred values from expectation step in time) 55 | Sum_VarW = zeros(dim,dim); 56 | Sum_VarW_bias = zeros(dim + 1,dim + 1); 57 | Sum_CorW = zeros(dim,dim); 58 | Sum_CorYX = zeros(dim_Y,dim); 59 | Sum_CorYX_bias = zeros(dim_Y,dim + 1); 60 | Sum_VarY = zeros(dim_Y,dim_Y); 61 | Sum_VarW_Scale = zeros(dim,dim); 62 | Sum_VarW_Scale_bias = zeros(dim + 1,dim + 1); 63 | Sum_Cor_inp2_Scale = zeros(dim_input,dim); 64 | Sum_Cor_inpout = zeros(dim_Y,dim_input); 65 | Sum_Cor_inp3_Scale = zeros(dim_input,dim_input); 66 | Sum_Cor_inp=zeros(dim_input,dim); 67 | Sum_Cor_inp2=zeros(dim_input,dim); 68 | Sum_Cor_inp3=zeros(dim_input,dim_input); 69 | VarW_t_bias=zeros(dim + 1,dim + 1,T); 70 | %% start the sumations 71 | for i=1:T 72 | 73 | VarW_t_bias(:,:,i) = [1,Xsmth_t(:,i)';Xsmth_t(:,i),VarW_t(:,:,i)]; 74 | 75 | if floor(i/Scale_dif)-i/Scale_dif == 0 76 | Y_Obs_sub = Y_Obs(:,i) - D * Input(:,i); 77 | Sum_CorYX = Sum_CorYX + Y_Obs_sub * transpose(Xsmth_t(:,i)); 78 | Sum_CorYX_bias = Sum_CorYX_bias + Y_Obs_sub * transpose(Xsmth_t_bias(:,i)); 79 | Sum_VarW_Scale = Sum_VarW_Scale + VarW_t(:,:,i); 80 | Sum_VarW_Scale_bias = Sum_VarW_Scale_bias + VarW_t_bias(:,:,i); 81 | Sum_VarY = Sum_VarY + Y_Obs_sub * transpose(Y_Obs_sub); 82 | Sum_Cor_inp2_Scale = Sum_Cor_inp2_Scale + Input(:,i) * Xsmth_t(:,i)'; 83 | Sum_Cor_inpout = Sum_Cor_inpout + Y_Obs(:,i) * Input(:,i)'; 84 | Sum_Cor_inp3_Scale = Sum_Cor_inp3_Scale + Input(:,i) * (Input(:,i))'; 85 | end 86 | if i==T 87 | Sum_VarW = Sum_VarW + VarW_t(:,:,i); 88 | Sum_VarW_bias = Sum_VarW_bias + VarW_t_bias(:,:,i); 89 | else 90 | Sum_VarW = Sum_VarW + VarW_t(:,:,i); 91 | Sum_VarW_bias = Sum_VarW_bias + VarW_t_bias(:,:,i); 92 | Sum_CorW = Sum_CorW + CorW_t(:,:,i); 93 | 94 | end 95 | if i~=1 96 | Sum_Cor_inp = Sum_Cor_inp + Input(:,i) * Xsmth_t(:,i-1)'; 97 | Sum_Cor_inp2 = Sum_Cor_inp2 + Input(:,i) * Xsmth_t(:,i)'; 98 | Sum_Cor_inp3 = Sum_Cor_inp3 + Input(:,i) * (Input(:,i))'; 99 | end 100 | 101 | end 102 | %% optimize A 103 | 104 | A=(Sum_CorW-B*Sum_Cor_inp)*inv(Sum_VarW-VarW_t(:,:,T)); 105 | %% optimize Q 106 | 107 | Qaux=(Sum_VarW - VarW_t(:,:,1)) - A * transpose(Sum_CorW - B * Sum_Cor_inp) - (Sum_CorW - B * Sum_Cor_inp) * transpose(A)... 108 | + A * (Sum_VarW - VarW_t(:,:,T)) * transpose(A) - B * Sum_Cor_inp2 - (B * Sum_Cor_inp2)' + B * Sum_Cor_inp3 * B'; 109 | 110 | if settings.switch_nondiagQ == 0 111 | Inv_Q=zeros(dim,dim); 112 | for i=1:dim 113 | Inv_Q(i,i)=(T-1)/(Qaux(i,i)); 114 | end 115 | Q=inv(Inv_Q); 116 | 117 | elseif settings.switch_nondiagQ == 1 118 | Q = Qaux / (T-1); 119 | end 120 | 121 | %% optimize Init_X 122 | Init_X=Xsmth_t(:,1); 123 | %% optimize Init_Cov 124 | Init_Cov=VarW_t(:,:,1)-Xsmth_t(:,1)*transpose(Xsmth_t(:,1)); 125 | 126 | %% optimize C and R 127 | if settings.switch_biasobs == 1 128 | 129 | C_bias = Sum_CorYX_bias * inv(Sum_VarW_Scale_bias); 130 | Raux = Sum_VarY - C_bias * transpose(Sum_CorYX_bias) - Sum_CorYX_bias * transpose(C_bias) + C_bias * Sum_VarW_Scale_bias * transpose(C_bias); 131 | Inv_R=zeros(dim_Y,dim_Y); 132 | for j = 1:dim_Y 133 | Inv_R(j,j) = (T/Scale_dif)/(Raux(j,j)); 134 | end 135 | R = Raux/(T/Scale_dif); 136 | 137 | C = C_bias(:,2:dim+1); 138 | bias = C_bias(:,1); 139 | elseif settings.switch_biasobs == 0 140 | C = Sum_CorYX * inv(Sum_VarW_Scale); 141 | Raux = Sum_VarY - C * transpose(Sum_CorYX) - Sum_CorYX * transpose(C) + C * Sum_VarW_Scale * transpose(C); 142 | 143 | Inv_R = zeros(dim_Y,dim_Y); 144 | for j = 1:dim_Y 145 | Inv_R(j,j) = (T/Scale_dif)/(Raux(j,j)); 146 | end 147 | R = Raux/(T/Scale_dif); 148 | 149 | C = C; 150 | bias = zeros(dim_Y,1); 151 | end 152 | 153 | 154 | %% optimize Theta 155 | for i=1:N 156 | 157 | myf= @(a) Obj_Func_Fast_Matlab(a,Xsmth_t,Covsmth_t,N_Obs(i,:),deltan,T,dim); 158 | options=optimoptions(@fminunc,'Algorithm','trust-region','GradObj','on','Display','Iter'); 159 | Theta(:,i)=(fminunc(myf,Previous_Theta(:,i)',options))'; 160 | 161 | end 162 | 163 | end 164 | -------------------------------------------------------------------------------- /filtering_maximization/Obj_Func_Fast_Matlab.m: -------------------------------------------------------------------------------- 1 | function [ value,varargout ] = Obj_Func_Fast_Matlab( a, Xsmth_t,Covsmth_t,N_Obs,delta,T,dim ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % this function obtains the objective function to estimate the best 9 | % parameters of the spiking activity (spiking modulation matrix) 10 | % for more details look at the paragraph right after equation (21) at 10.1109/TNSRE.2019.2913218 11 | % the format in this function is compatible with fminunc 12 | % INPUTS: 13 | % - a: represents theta 14 | % - Xsmth_t: smoothed values of states (non-causal inference) (dim * T) 15 | % - Covsmth_t: covariance of error of smoothed values of states (non-causal inference) (dim * dim * T) 16 | % - N_Obs: time series of spikes with size N*T, N is number of neurons 17 | % - delta: time step 18 | % - T: total number of observations: equal to size(N_Obs,2) 19 | % - dim: dimension of latent state, equals to size(Xsmth_t, 1) 20 | % OUTPUTS: 21 | % outputs are compatible with the fminunc function (objective function and gradients) 22 | % - value: objective function value 23 | % - Genvector: derivative of objective fn with respect to spike params 24 | %% calculate objective fn 25 | theta = a; 26 | aux_vec = reshape(Covsmth_t, dim, dim * T); 27 | aux_vec2 = theta(2:dim + 1) * aux_vec; 28 | aux_vec2prime = reshape(aux_vec2',dim,T)'; 29 | aux_vec3 = (aux_vec2prime * theta(2:dim + 1)')'; 30 | vec = (-N_Obs) .* (log(delta) + theta(1) + theta(2:dim + 1) * Xsmth_t) + delta * exp((theta(1) + theta(2:dim+1) * Xsmth_t + 0.5 * aux_vec3)); 31 | value=sum(vec); % value of objective function 32 | %% calculates objective fn's deriative w.r.t spike params 33 | if nargout>1 34 | % calculates gradients 35 | aux_vec1 = delta * exp(theta(1) + theta(2:dim + 1) * Xsmth_t + 0.5 * aux_vec3) * (Xsmth_t' + aux_vec2prime); 36 | aux_vec10 = zeros(1,dim); 37 | for i = 1:dim 38 | aux_vec10(i) = sum( N_Obs.*Xsmth_t(i,:)); 39 | end 40 | vector = aux_vec1 - aux_vec10; 41 | Genvector = zeros(1,dim + 1); 42 | Genvector(2:dim + 1) = vector; 43 | 44 | aux_vec11 = delta * exp((theta(1) + theta(2:dim + 1) * Xsmth_t + 0.5 * aux_vec3)) - N_Obs; 45 | Genvector(1) = sum(aux_vec11); 46 | varargout{1} = Genvector; 47 | end 48 | 49 | -------------------------------------------------------------------------------- /filtering_maximization/PointProcessFilter.m: -------------------------------------------------------------------------------- 1 | function [x_esti,W_esti,ill_level] = PointProcessFilter(x_onestep,W_onestep,N_t,parameter,delta) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This function uses the discrete spike point process framework to update one-step predictions. 9 | % The model of the discrete spike point process is a logarithm-linear model. ( log(firing rate)=parameter(1)+sum[x_onestep(i)*parameter(i+1)] ) 10 | % 11 | % PLEASE NOTE THE FOLLOWING IMPORTANT THING: when the inverse condition is really too bad, the PPF won't update the x_onestep and W_onestep and will just keep 12 | % them as the outputs. 13 | % 14 | % about output: x_esti is the ML estimation of the state in the next step based on Gaussian approximation (it's a column vector) 15 | % W_esti is the ML estimation of the covariance matrix in the next step based on Gaussian approximation 16 | % ill_level is the condition number of "inv(W_onestep)+partial_sum", we want to check that whether it's good enough. 17 | % 18 | % about input : x_onestep, W_onestep are results of one-step estimation based on OFC model. (x_onestep is a column vector. Its length is n) 19 | % N_t is the spike signal. It's a vector with length "C". (it's a row vector) 20 | % parameter is a matrix recording all parameters for each neuron in the log linear model. It's dimension is (n+1)*C. Every column is a parameter 21 | % set of one observe sequence. The first element is the basic firing rate and the rest are coefficients of the state variables. 22 | % delta is the mini time interval of each spike. 23 | 24 | % Set up the ill condition tolerance. 25 | ill_tole = 10^(-9); 26 | 27 | % First, calculate the W_esti. 28 | submatrix = parameter(2:end,:); % discard the constant part (first row) in the parameter matrix. 29 | num_state = size(submatrix,1); 30 | 31 | %inner_product_x = [1;x_onestep]; % for inner product sum in the exponential function (It's a col). 32 | weighted = exp( x_onestep' * submatrix + parameter(1,:) ); 33 | 34 | partial_sum = ((ones(num_state,1) * weighted).*submatrix) * submatrix.'*delta; % This is a summation process. Matrix can speed up the program. 35 | 36 | if(rcond(W_onestep) > ill_tole) 37 | temp_W = inv(W_onestep) + partial_sum; 38 | ill_level = rcond(temp_W); 39 | % Now, check that whether the matrix's condition is good or not. 40 | if( ill_level > ill_tole ) 41 | % now we solve the x_esti. 42 | W_esti = inv(temp_W); 43 | partial_sum = submatrix * (N_t - weighted * delta).'; % this one is the summation part of different neurons. 44 | x_esti = x_onestep + W_esti * partial_sum; 45 | else 46 | W_esti = W_onestep; 47 | x_esti = x_onestep; 48 | ill_level = rcond(W_esti); 49 | end 50 | else 51 | W_esti = W_onestep; 52 | x_esti = x_onestep; 53 | ill_level = rcond(W_esti); 54 | end 55 | 56 | 57 | end 58 | -------------------------------------------------------------------------------- /functions_main/EM_multiscale_unsupervised_function.m: -------------------------------------------------------------------------------- 1 | function [ results,settings,ITER ] = EM_multiscale_unsupervised_function( Y_Obs,N_Obs,handles ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This function learns the unsupervised latent state space model from multiscale spiking and 9 | % LFP activity. The details of the derivation could be found in this paper: 10 | % 10.1109/TNSRE.2019.2913218 11 | % 12 | % In general, the state-space model is learned through an iterative expectation-maximization algorithm 13 | % the state space model is as follows: 14 | % 15 | % x_{t + 1} = A * x_{t} + q_t; COV(q_t) = Q 16 | % y_{t} = C * x_{t} + r_t; COV(r_t) = R 17 | % p(N_{t}|x_{t}) = (\lambda(x_{t})) ^ (N_{t}) * exp( -\lambda(x_{t}) * \Delta ); \lambda(x_{t}) = exp(-\alpha*x_{t} + \beta), \Delta: time-step 18 | % 19 | % An example sample use of this algorithm can be found in ''. 20 | % 21 | % INPUTS : 22 | % (1) Y_Obs: time series of the LFPs, its dimension is (dim_Y*T), where dim_Y is the number of channles and T is 23 | % the total number of samples! if time-scale of LFP is k (available 24 | % at k,2k,3k,...), it is recommended to put the samples in between as NaN, however the model will never use them 25 | % (2) N_Obs: time series of spikes with size N*T, N is number of neurons 26 | % (3) handles: consists some options for the EM algorithm and for additional options in the future (struct with following fields) 27 | % 28 | % -scale_dif_inp : scale difference in spikes and LFPs, k, i.e., spikes are available at every time-step and lfp are available 29 | % only at k, 2k, 3k, ... 30 | % -delta_inp :time step of the spike bins (or real time-scale of sampling/dynamics) (in seconds) 31 | % -dim_hid: dimension of hidden state 32 | % -num_iter: number of iterations to run the EM algorithm for 33 | % -switch_nondiag: if it is 1 it means the observation noise will be learned as a non-diagonal covariance matrix, if it is 0 the observation noise is diagonal 34 | % -switch_nondiagQ: (Always put this as 1, for fast learning!) if it is 1 it means the state noise is non-diagonal, if it is 0 the state noise is diagonal 35 | % -init_type: what type of initialization to use ('random' or 'subid'), if it is 'random' the model randomly initialize the parameters at iter 1, if not 36 | % if not it uses subspace identification to 37 | % initialize parameters, for now always set to 'random' 38 | % -switch_biasobs: whether to fit bias for linear observations, if 1, it learns a linear bias for Y_Obs 39 | % -save_iter: which iterations to save at ITER output: 'all' or 'startandend'. 'all' saves all iterations, 'startandend' saves only the first and last 40 | % iteration 41 | % -spike_bs_init: initialization for spike baseline: 'random' or 'meanFR', whehter to initialize \beta for spikes with random value or mean firing rate of 42 | % observations 43 | % OUTPUTS: 44 | % (1)results : final results of state space with smoothed and filtered states using that state space, an struct with the following fields 45 | % - A: state transition matrix 46 | % - B: state-input matrix (default: zeros/not learned) 47 | % - Q: state noise covariance matrix 48 | % - C: observation emission matrix 49 | % - D: observation-input matrix (default: zeros/not learned) 50 | % - R: observation noise covariance matrix 51 | % - Init_X: initial estimation of the latent state at t = 1 52 | % - Init_Cov: initial estimation of the latent state estimation error at t = 1 53 | % - Theta: parameters of spike modulation -> [\beta_c;alpha_c] in each column for every neuron 54 | % - Bias: bias of lfp observation 55 | % - X_update: filtered (causal inference) values of latent state in training with the final parameters 56 | % - X_smoothed: smoothed (non-causal inference) values of latent state in training with the final parameters 57 | % (2)settings: settings used to run the EM algorithm (similar to handles) 58 | % (3)ITER: saves the parameters of intermediate steps as well with the following fields 59 | % -iter: struct array with following fields at each index 60 | % - similar to results: A, B, Q, C, D, R, Init_X, Init_Cov, Theta, Bias, X_update, X_smoothed 61 | % 62 | 63 | %% check some default values! 64 | if ~isfield(handles,'switch_nondiagQ') 65 | handles.switch_nondiagQ = 0; 66 | display('switch nondiagQ is set as its default value to 0 --> in the inner function') 67 | end 68 | if ~isfield(handles,'switch_biasobs') 69 | handles.switch_biasobs = 0; 70 | display('switch biasobs is set as its default value to 0 --> in the inner function') 71 | end 72 | 73 | if ~isfield(handles,'init_type') 74 | handles.init_type = 'random'; 75 | display('init_type is set as its default value to random --> in the inner function') 76 | end 77 | %% extract from handles 78 | dim_hid = handles.dim_hid; 79 | scale_dif_inp = handles.scale_dif_inp; 80 | delta_inp = handles.delta_inp; 81 | num_iter = handles.num_iter; 82 | switch_nondiagQ = handles.switch_nondiagQ; 83 | switch_nondiag = handles.switch_nondiag; 84 | init_type = handles.init_type; 85 | switch_biasobs = handles.switch_biasobs;%% build settings 86 | if strcmp(handles.save_iter,'all') 87 | save_iter_number = 1:num_iter; 88 | elseif strcmp(handles.save_iter,'startandend') 89 | save_iter_number = [1,num_iter]; 90 | end 91 | spike_bs_init = handles.spike_bs_init; 92 | %% set the settings, will also be used in decoder and smoother and maximizer 93 | settings = struct; 94 | %[settings.dim,~] = dim_Object;% dimension of states 95 | settings.dim_input = 2; % the implementation of input is missing, arbitrarily we put input dim as 2 96 | settings.dim_st = dim_hid; % dimension of latent state 97 | settings.T = size(Y_Obs,2);%number of samples 98 | settings.dim_Y = size(Y_Obs,1);% # of linear/ECoG Channels 99 | settings.N = size(N_Obs,1);% nummber of neurons 100 | settings.Scale_dif = scale_dif_inp; % scale difference in ourr multiscale measurement #spikes/linear obs 101 | settings.delta = delta_inp; % time step 102 | settings.it = num_iter; % number of iterations 103 | settings.Input = zeros(settings.dim_input,settings.T); 104 | settings.switch_nondiagQ = switch_nondiagQ; 105 | settings.switch_biasobs = switch_biasobs; 106 | %% initialization 107 | %%%%% STEP2: INITIALIZATION%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 108 | %%%%%here we initialized the parameters to be fitted 109 | if strcmp(init_type,'random') 110 | %Initialization.A = diag(random('Normal',0.6,0.1,settings.dim_st,1)); 111 | % This is a good set of initialization, you could change depending on 112 | % your need 113 | Initialization.A = eye(settings.dim_st); 114 | Initialization.Q = diag(random('Uniform',0.025,0.07,1,settings.dim_st)); 115 | Initialization.C = (1)*random('Normal',2,0.4,settings.dim_Y,settings.dim_st); 116 | Initialization.R = diag(random('Uniform',0.1,0.5,1,settings.dim_Y)); 117 | elseif strcmp(init_type,'subspace') 118 | [Initialization.A,~,Initialization.C,~,~,~,~,~,Initialization.Q,Initialization.R,Ss] = subid(Y_Obs,[],dim_hid-3,dim_hid); % third dimension is horizon 119 | Initialization.A = real(Initialization.A^(1/scale_dif_inp)); % to change the to the dynamics time scale 120 | end 121 | 122 | Initialization.B = zeros(settings.dim_st,settings.dim_input); 123 | Initialization.D = zeros(settings.dim_Y,settings.dim_input); 124 | Initialization.Init_X = zeros(settings.dim_st,1); 125 | Initialization.Init_Cov = 0.001*eye(settings.dim_st,settings.dim_st); 126 | Initialization.Theta = NaN(settings.dim_st+1,settings.N); 127 | 128 | if strcmp(spike_bs_init,'meanFR') 129 | Initialization.Theta(1,:) = log(mean(N_Obs,2)/settings.delta); 130 | elseif strcmp(spike_bs_init,'random') 131 | Initialization.Theta(1,:) = 1.5*ones(1,settings.N); 132 | end 133 | Initialization.Theta(2:settings.dim_st+1,:) = random('Normal',0.007,0.0015,settings.dim_st,settings.N); 134 | Initialization.Bias=zeros(settings.dim_Y,1); 135 | % set the 3-dimensional tensor of parameters in time... 136 | A(:,:,1) = Initialization.A; 137 | B(:,:,1) = Initialization.B; 138 | Q(:,:,1) = Initialization.Q; 139 | C(:,:,1) = Initialization.C; 140 | D(:,:,1) = Initialization.D; 141 | Init_X(:,1) = Initialization.Init_X; 142 | Init_Cov(:,:,1) = Initialization.Init_Cov; 143 | R(:,:,1) = Initialization.R; 144 | Theta(:,:,1) = Initialization.Theta; 145 | Bias(:,1) = Initialization.Bias; 146 | %% starts EM 147 | %%%%%% STEP4:EM ALGORITHM%%%%%%% 148 | fprintf('EM algorithm starts \n'); 149 | % multiscale decoder 150 | [Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next] = Decoder(A(:,:,1),B(:,:,1),Q(:,:,1),Init_X(:,1),Init_Cov(:,:,1),C(:,:,1),D(:,:,1),R(:,:,1),Theta(:,:,1),Y_Obs-repmat(Bias(:,1),1,settings.T),N_Obs,settings); 151 | % fixed interval smoother 152 | [ Xsmth_t_next,Covsmth_t_next,VarW_t_next,CorW_t_next ] = FIS_modified( Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next,A(:,:,1),settings ); 153 | %% build ITER struct 154 | ITER = struct; 155 | ITER.iter = struct; 156 | ITER.iter(1).A = A(:,:,1); 157 | ITER.iter(1).B = B(:,:,1); 158 | ITER.iter(1).Q = Q(:,:,1); 159 | ITER.iter(1).C = C(:,:,1); 160 | ITER.iter(1).Init_X = Init_X(:,1); 161 | ITER.iter(1).Init_Cov = Init_Cov(:,:,1); 162 | ITER.iter(1).R = R(:,:,1); 163 | ITER.iter(1).Bias = Bias(:,1); 164 | ITER.iter(1).theta = Theta(:,:,1); 165 | ITER.iter(1).X_smoothed = Xsmth_t_next; 166 | ITER.iter(1).x_update = Xupd_t_next; 167 | 168 | for i=2:settings.it 169 | fprintf('iteration %d starts \n',i); 170 | %% Maximization Step 171 | if switch_nondiag == 0 172 | [ A(:,:,i),B(:,:,i),Q(:,:,i),Init_X(:,i),Init_Cov(:,:,i),C(:,:,i),Bias(:,i),D(:,:,i),R(:,:,i),Theta(:,:,i) ] = Maximization_diag_bias( Xsmth_t_next,Covsmth_t_next,VarW_t_next,CorW_t_next,Y_Obs,N_Obs,Theta(:,:,i-1),settings); 173 | elseif switch_nondiag == 0 174 | [ A(:,:,i),B(:,:,i),Q(:,:,i),Init_X(:,i),Init_Cov(:,:,i),C(:,:,i),Bias(:,i),D(:,:,i),R(:,:,i),Theta(:,:,i) ] = Maximization_nondiag_bias( Xsmth_t_next,Covsmth_t_next,VarW_t_next,CorW_t_next,Y_Obs,N_Obs,Theta(:,:,i-1),settings); 175 | end 176 | %% Expectation Step 177 | % multiscale filter 178 | [Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next] = Decoder(A(:,:,i),B(:,:,i),Q(:,:,i),Init_X(:,i),Init_Cov(:,:,i),C(:,:,i),D(:,:,i),R(:,:,i),Theta(:,:,i),Y_Obs-repmat(Bias(:,i),1,settings.T),N_Obs,settings); 179 | % fixed interval smoother 180 | [ Xsmth_t_next,Covsmth_t_next,VarW_t_next,CorW_t_next ] = FIS_modified( Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next,A(:,:,i),settings ); 181 | %% continue to build ITER struct 182 | ITER.iter(i).A = A(:,:,i); 183 | ITER.iter(i).B = B(:,:,i); 184 | ITER.iter(i).Q = Q(:,:,i); 185 | ITER.iter(i).C = C(:,:,i); 186 | ITER.iter(i).Init_X = Init_X(:,i); 187 | ITER.iter(i).Init_Cov = Init_Cov(:,:,i); 188 | ITER.iter(i).R = R(:,:,i); 189 | ITER.iter(i).Bias = Bias(:,i); 190 | ITER.iter(i).theta = Theta(:,:,i); 191 | if ismember(i,save_iter_number) 192 | ITER.iter(i).X_smoothed = Xsmth_t_next; 193 | ITER.iter(i).x_update = Xupd_t_next; 194 | end 195 | end 196 | 197 | %% saving results 198 | results=struct; 199 | % set the learned parameters 200 | results.A = A(:,:,num_iter); 201 | results.B = B(:,:,num_iter); 202 | results.Q = Q(:,:,num_iter); 203 | results.C = C(:,:,num_iter); 204 | results.D = D(:,:,num_iter); 205 | results.R = R(:,:,num_iter); 206 | results.Init_X = Init_X(:,num_iter); 207 | results.Init_Cov = Init_Cov(:,:,num_iter); 208 | results.Theta = Theta(:,:,num_iter); 209 | results.Bias=Bias(:,num_iter); 210 | % get the inferred states 211 | [Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next] = Decoder(A(:,:,num_iter),B(:,:,num_iter),Q(:,:,num_iter),Init_X(:,num_iter),Init_Cov(:,:,num_iter),C(:,:,num_iter),D(:,:,num_iter),R(:,:,num_iter),Theta(:,:,num_iter),Y_Obs-repmat(Bias(:,num_iter),1,settings.T),N_Obs,settings); 212 | [ results.X_smoothed,~,~,~ ] = FIS_modified( Xupd_t_next,Xpred_t_next,Covupd_t_next,Covpred_t_next,A(:,:,num_iter),settings ); 213 | results.X_update = Xupd_t_next; 214 | 215 | end 216 | 217 | -------------------------------------------------------------------------------- /generate_state_space/Acont_fromEIG.m: -------------------------------------------------------------------------------- 1 | function [ Acont ] = Acont_fromEIG( EIG ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %this function computes Acont matrix from EIG (eigevnalues) 9 | % INPUTS : (1) EIG : vector of all eigenvalues 10 | 11 | % OUTPUTS : (1) Acont : A matrix in continuous time 12 | 13 | dim = length(EIG); 14 | Acont = zeros (dim,dim); 15 | j = 1; 16 | while j <=dim 17 | if imag(EIG(j)) == 0 % if eigenvalue is real 18 | Acont(j,j) = EIG(j); 19 | j = j+1; 20 | else 21 | Acont(j:j+1,j:j+1) = [real( EIG(j) ),imag( EIG(j) );-imag( EIG(j)),real( EIG(j) ) ]; 22 | j = j+2; 23 | end 24 | end 25 | 26 | end 27 | 28 | -------------------------------------------------------------------------------- /generate_state_space/Generate_Poles_discreteplane.m: -------------------------------------------------------------------------------- 1 | function [ EIG,EIG_dis,modes ] = Generate_Poles_discreteplane( phandles ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % this function generate poles for the modes of the matrix A given some 9 | % handles 10 | % 11 | % INPUTS: -phandles: 12 | % -phandles.freq : available frequencies 13 | % -phandles.drawing : 'random': gets from a uniform dist of 14 | % phandles.TC_range, 'fixed': from fixed values of phandles.decay 15 | % -phandles.TC_range : range of the time decay 16 | % -phandles.decayL available decays 17 | % OUTPUTS: - EIG: vector of eigenvalues in real plane 18 | % - EIG: vecotr of eigenvalues in discrete plane 19 | % - modes: struct array contatining modes 20 | 21 | %% get some values 22 | freq = phandles.freq; 23 | drawing = phandles.drawing; 24 | delta = phandles.delta; 25 | 26 | %% construct modes 27 | modes = struct; 28 | count_eig = 1; 29 | for j = 1:length(freq) 30 | if freq(j) == 0 31 | modes(j).modetype = 'single'; 32 | modes(j).eignum = count_eig; 33 | count_eig = count_eig+1; 34 | else 35 | modes(j).modetype = 'double'; 36 | modes(j).eignum = [count_eig,count_eig+1]; 37 | count_eig = count_eig+2; 38 | end 39 | if strcmp(drawing,'random') 40 | TC_range = phandles.TC_range; 41 | decay = random('Uniform',TC_range(1),TC_range(2)); 42 | elseif strcmp(drawing,'fixed') 43 | decay = phandles.decay(j); 44 | end 45 | modes(j).decay = decay; 46 | modes(j).freq = freq(j); 47 | 48 | 49 | end 50 | %% discrete 51 | EIG_dis = []; 52 | for mode = 1:length(modes) 53 | if strcmp(modes(mode).modetype,'single') 54 | 55 | eig_val = 1/exp(delta/modes(mode).decay) ; 56 | 57 | EIG_dis = [EIG_dis,eig_val]; 58 | 59 | elseif strcmp(modes(mode).modetype,'double') 60 | 61 | eig_val_norm = 1/exp(delta/modes(mode).decay) ; 62 | eig_val_angle = 2 * pi * delta * modes(mode).freq; 63 | 64 | eig_val_real = eig_val_norm * cos(eig_val_angle); 65 | eig_val_imag = eig_val_norm * sin(eig_val_angle); 66 | 67 | EIG_dis =[EIG_dis, ( eig_val_real + 1i* ( eig_val_imag ) )]; 68 | EIG_dis =[EIG_dis,( eig_val_real - 1i* ( eig_val_imag ) )]; 69 | 70 | end 71 | 72 | end 73 | %% construct continuous eigenvalues 74 | EIG = ((EIG_dis)-1) / delta; 75 | end 76 | 77 | -------------------------------------------------------------------------------- /generate_state_space/NeuronSigSim_Multi_Open.m: -------------------------------------------------------------------------------- 1 | function [rec_spike,rec_exp_RV] = NeuronSigSim_Multi_Open(x,beta,delta,trials) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This program is a simulation of spike neuron signal under open loop framework. It can do multiple times simulation with multiple neurons all at once. 9 | % The first spike value is always zero (consider it as a determinant initial state) 10 | % 11 | % Input: 12 | % x: the recorded (planned) kinematics along the timeline. Its dimension is (num of state)*(time length). The rows are go throught each different dimension 13 | % sequentially with their related dynamics parameters (ex: dx > vx > ax > dy > vy > ay > dz > vz > ... and so on). 14 | % beta: the coefficients matching each states. Its dimension is (num of state+1)*(neuron num). Every column represents a coefficient set of one neuron. The 15 | % first coeffi. row beta(1,:) is the baseline parameter. 16 | % delta: the spike time interval in second. 17 | % trials: the number of trials under the same planning dynamics and beta coefficients. 18 | % 19 | % Output: 20 | % rec_spike: It records the neuron spike signal of each neuron along the time in each trial. Its dimension is (trials)*(neuron num)*(time length). 21 | % rec_exp_RV: exponential R.V. record. Its dimension is (trials)*(neuron num)*(time length). 22 | 23 | time_length = size(x,2); 24 | neuron_num = size(beta,2); 25 | 26 | rec_spike = zeros(trials,neuron_num,time_length); % I assume the spike value at time=0 (rec_spike(1)) is always 0. 27 | rec_exp_RV = zeros(trials,neuron_num,time_length); 28 | 29 | for(i=1:1:trials) 30 | for(j=1:1:neuron_num) 31 | [rec_spike(i,j,:),rec_exp_RV(i,j,:)]=NeuronSigSim_Open(x,beta(:,j),delta); 32 | end 33 | end 34 | 35 | 36 | end -------------------------------------------------------------------------------- /generate_state_space/NeuronSigSim_Open.m: -------------------------------------------------------------------------------- 1 | function [rec_spike,rec_exp_RV] = NeuronSigSim_Open(x,beta,delta) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb), Han Lin Hsieh and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This program is a simulation of spike neuron signal under open loop framework. Only one trial and one neuron. 9 | % The first spike value is always zero (consider it as a determinant initial state) 10 | % 11 | % Input: 12 | % x: the recorded (planned) kinematics along the timeline. Its dimension is (num of state)*(time length). The rows are go throught each different dimension 13 | % sequentially with their related dynamics parameters (ex: dx > vx > ax > dy > vy > ay > dz > vz > ... and so on). 14 | % beta: the coefficients matching each states. It's a column vector with length (num of state)+1. The first coeffi. beta(1) is the baseline parameter. 15 | % delta: the spike time interval in second. 16 | % 17 | % Output: 18 | % rec_spike: It records the neuron spike signal along the time so its length must be the same as "time length". It is a row vector. 19 | % rec_exp_RV: exponential R.V. record. I use -1 as the initial value for distinguishing the ones been assigned with the ones not. 20 | 21 | % First, I set up some initial parameters. 22 | 23 | time_length = size(x,2); 24 | rec_spike = zeros(1,time_length); % I assume the spike value at time=0 (rec_spike(1)) is always 0. 25 | rec_exp_RV = repmat(-1,[1,time_length]); 26 | 27 | spike_index = 1; 28 | 29 | % Now, I use a loop to find the next spike index continuously till it over the number of state. 30 | 31 | two_points_integral = [0,0]; % set up the vector for recording two boundary points in the trapezoid integral. 32 | two_points_integral(1) = exp([1;x(:,spike_index)].'*beta); % set up the initial value for the integration. 33 | 34 | while(spike_index < time_length) 35 | 36 | exp_RV = exprnd(1); 37 | rec_exp_RV(spike_index+1) = exp_RV; 38 | partial_sum = 0; 39 | 40 | while (partial_sum < exp_RV && spike_index < time_length) 41 | spike_index = spike_index+1; 42 | two_points_integral(2) = exp([1;x(:,spike_index)]'*beta); % I update the second term in the integral one step. 43 | partial_sum = partial_sum+trapz(two_points_integral)*delta; 44 | two_points_integral(1) = two_points_integral(2); 45 | end 46 | 47 | if (partial_sum >= exp_RV) 48 | rec_spike(spike_index)=1; 49 | end 50 | 51 | end 52 | 53 | 54 | end -------------------------------------------------------------------------------- /generate_state_space/build_statespace_realiz.m: -------------------------------------------------------------------------------- 1 | function [ REALIZ,TRUE,comp] = build_statespace_realiz( OPTIONS,vhandles ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %This function generates state space realization from fixed settings 9 | % INPUTS : (1)OPTIONS: a struct with following fields 10 | % -delta: time step 11 | % -scale: scale of observations, 'MS', 'Spike', 'LFP' 12 | % -dim_inp: arbitrary dimension of input 13 | % -Ttrain: number of training samples 14 | % -dim_Y: number of lfp features 15 | % -normalization_c: how to normalize C matrix for emmision 16 | % matrix of lfp features 17 | % -scale_dif: scale difference between spiking and lfp: k 18 | % spikes are available every time step and lfp are available 19 | % every k steps, k, 2k, 3k, ... 20 | % -dim_N: number of spiking neurons 21 | % -spike_modulation: set to 'modesonly' 22 | % (2)vhandles: 23 | % -dim: dimension of latent state 24 | % -Apolehandles 25 | % Apolehandles.freq: frequency of modes 26 | % Apolehandles.drawing : 'fixed' 27 | % Apolehandles.decay: decay of modes 28 | % Apolehandles.delta : OPTIONS.delta 29 | % -v_R_range 30 | % v_R_range.Rmean: mean value of variance (diagonal 31 | % entries) of noise covariance matrix 32 | % v_R_range.Rstd: mean value of variance (diagonal 33 | % entries) of noise covariance matrix 34 | % -v_c_mod 35 | % v_c_mod.mag: coeficient multiplied by C matrix 36 | % -v_theta_mod 37 | % v_theta_mod.modulation_depth_mean: mean of modulation vector of modes 38 | % for different neurons 39 | % v_theta_mod.modulation_depth_std: std of modulation vector of modes 40 | % for different neurons 41 | % v_theta_mod.modulation_mean_mean: mean of mean firing 42 | % rate for different neurons 43 | % v_theta_mod.modulation_mean_std: std of mean firing 44 | % rate for different neurons 45 | % 46 | % OUTPUTS: (1) REALIZ: a struct contating realized values of spike and lfp, with these fields 47 | % - states: latent states time series 48 | % - input: input time series 49 | % - N_Obs: spiking time series 50 | % - FR_Obs: firing rate time series 51 | % - Y_Obs: lfp features time series 52 | % (2) TRUE: struct contatining true model parameters 53 | % (3) comp: struct contatining eigenvalues and some featuers of the state space model 54 | %% get the state space parameters range 55 | OPTIONS.dim_hid = vhandles.dim;%dimension of system 56 | Apolehandles = vhandles.Apolehandles; 57 | v_R_range = vhandles.v_R_range; 58 | v_theta_mod = vhandles.v_theta_mod; 59 | v_c_mod = vhandles.v_c_mod; 60 | %% set up A 61 | phandles = Apolehandles; 62 | phandles.drawing = 'fixed'; 63 | [EIG_A,EIG_Adis,MODES_A_othermodes] = Generate_Poles_discreteplane(phandles);% generate poles 64 | Acont = Acont_fromEIG( EIG_A); 65 | Adis2 = exp( Acont * OPTIONS.delta ); 66 | [~,Adis_othermodes] = cdf2rdf(eye(OPTIONS.dim_hid),diag(EIG_Adis)); 67 | Adis = Adis_othermodes; 68 | %% set up Q and B 69 | Q_othermodes = diag(random('Normal',0.2,0.05,1,OPTIONS.dim_hid)); 70 | B_othermodes = 0 * randn(OPTIONS.dim_hid,OPTIONS.dim_inp); 71 | %% set up input 72 | Input_othermodes = zeros(OPTIONS.dim_inp,OPTIONS.Ttrain); 73 | %% set Q B Input for total modes 74 | Q = Q_othermodes; 75 | B = B_othermodes; 76 | Input = Input_othermodes; 77 | %% generate the forward states 78 | start_states = zeros(OPTIONS.dim_hid,1); 79 | states_othermodes = state_generator( Adis,B,Q,Input,start_states,OPTIONS.Ttrain ); 80 | %% build states 81 | states = states_othermodes; 82 | %% plotting states 83 | if 0 84 | figure; 85 | for di = 1:OPTIONS.dim_hid 86 | st = 1; 87 | fi = 32000; 88 | mode = floor((di-1)/2) + 1; 89 | %X = sin(2*pi*0.5*(st:fi)*OPTIONS.delta); 90 | X = states(di,st:fi); 91 | %figure; 92 | subplot(OPTIONS.dim_hid,2,2*di-1) 93 | plot((st:fi),X); 94 | title(sprintf('dimension %d--mode %d -- \n freq. = %.3g decay = %.3g',di,mode,MODES_A_othermodes(mode).freq,MODES_A_othermodes(mode).decay)); 95 | subplot(OPTIONS.dim_hid,2,2*di) 96 | X = states(di,:)-mean(states(di,:)); % states(di,:)-mean(states(di,:)); 97 | [pxx,f] = pwelch(X,20000,[],20000,(1/OPTIONS.delta)); 98 | %[pxx,f] = pmtm(X,4,512*64,(1/OPTIONS.delta)); 99 | plot(f,pow2db(pxx)) 100 | [maxpxx,index_max] = min(-pxx); 101 | maxpxx = -maxpxx; 102 | p.LineWidth = 2; 103 | box off; 104 | xlabel('freq'); 105 | xlim([0,10]) 106 | ylabel('Power in db'); 107 | title(sprintf(' max in %.3g freq with %.2g db power',f(index_max),pow2db(maxpxx))); 108 | 109 | end 110 | 111 | end 112 | 113 | %% start setting up observation model 114 | if strcmp(OPTIONS.scale,'LFP') || strcmp(OPTIONS.scale,'MS') 115 | %% set up parameters for observations matrix 116 | %1. set up C and R and bias and D 117 | C_temp = random('Uniform',0.5,6,OPTIONS.dim_Y,OPTIONS.dim_hid); %linear observation matrix 118 | %C_temp = random('Normal',0,3,OPTIONS.dim_Y,OPTIONS.dim_hid); %linear observation matrix 119 | %OPTION#1 120 | if strcmp(OPTIONS.normalization_c,'std') 121 | std_states = sqrt(diag(cov(states'))); 122 | elseif strcmp(OPTIONS.normalization_c,'peaktopeak') 123 | %OPTIONS#2 124 | std_states = sqrt ( ( max(states,[],2)-min(states,[],2) )/2 ) ; 125 | end 126 | %contribution_states_in_C = 1 * ones(1,OPTIONS.dim_hid); 127 | contribution_states_in_C = 1./std_states'; 128 | contribution_states_in_C = v_c_mod.mag * contribution_states_in_C; % to level up the observations dominance 129 | C = C_temp .* repmat(contribution_states_in_C,OPTIONS.dim_Y,1); 130 | 131 | %% R and bias and D 132 | R = diag(random('Normal',v_R_range.Rmean,v_R_range.Rstd,OPTIONS.dim_Y,1)); 133 | bias = zeros(OPTIONS.dim_Y,1); 134 | D = zeros(OPTIONS.dim_Y,OPTIONS.dim_inp); 135 | %% generate linear observations from parameters 136 | Y_Obs = linear_obs_generator( states,C,D,R,Input,bias ); 137 | 138 | end 139 | %% generate spike modulation matrix and spiking activity 140 | if strcmp(OPTIONS.scale,'LFP') 141 | 142 | N_Obs = NaN(1,OPTIONS.Ttrain); 143 | FR_Obs = NaN(1,OPTIONS.Ttrain); 144 | 145 | elseif strcmp(OPTIONS.scale,'MS') || strcmp(OPTIONS.scale,'Spike') 146 | 147 | if strcmp(OPTIONS.spike_modulation,'modesonly') 148 | handles_theta = struct; 149 | handles_theta.modulation_depth_mean = v_theta_mod.modulation_depth_mean; 150 | handles_theta.modulation_depth_std = v_theta_mod.modulation_depth_std; 151 | 152 | handles_theta.modulation_mean_mean = v_theta_mod.modulation_mean_mean; 153 | handles_theta.modulation_mean_std = v_theta_mod.modulation_mean_std; 154 | 155 | 156 | [ Theta_kin,Theta_modes ] = theta_generator_onlymodes( states,OPTIONS.dim_N,OPTIONS.spike_modulation,handles_theta ); 157 | Theta = zeros(OPTIONS.dim_hid+1,OPTIONS.dim_N); 158 | Theta(1,:) = Theta_kin(1,:); 159 | Theta(2:end,:) = Theta_modes; 160 | end 161 | 162 | 163 | [N_Obs,FR_Obs] = spike_generator(Theta,states,OPTIONS.delta); 164 | max_FR_pern = NaN(OPTIONS.dim_N,1); 165 | N_Obs = squeeze(N_Obs); 166 | % plot max firing rate 167 | for j = 1:OPTIONS.dim_N 168 | max_FR_pern(j) = max(FR_Obs(j,:)); 169 | 170 | end 171 | if 0 172 | figure; 173 | plot(1:OPTIONS.dim_N, max_FR_pern) 174 | title('max_FR') 175 | end 176 | 177 | end 178 | 179 | if strcmp(OPTIONS.scale,'Spike') 180 | 181 | Y_Obs = NaN(1,OPTIONS.Ttrain); 182 | 183 | end 184 | 185 | %% sets up TRUE struct 186 | TRUE = struct; 187 | TRUE.A = Adis; 188 | TRUE.B = B; 189 | TRUE.Q = Q; 190 | if strcmp(OPTIONS.scale,'MS') || strcmp(OPTIONS.scale,'LFP') 191 | TRUE.C = C; 192 | TRUE.D = D; 193 | TRUE.R = R; 194 | TRUE.bias = bias; 195 | end 196 | if strcmp(OPTIONS.scale,'MS') || strcmp(OPTIONS.scale,'Spike') 197 | TRUE.Theta = Theta; 198 | end 199 | TRUE.vhandles = vhandles; 200 | %% sets up comp struct 201 | 202 | comp = struct; 203 | %comp.MODES_A = MODES_A; 204 | comp.EIG_A = eig(Adis); 205 | comp.EIG_Adis = eig(Adis); 206 | comp.MODES_A_othermodes = MODES_A_othermodes; 207 | comp.info = struct; 208 | comp.info.Apolehandles = Apolehandles; 209 | if strcmp(OPTIONS.scale,'MS') || strcmp(OPTIONS.scale,'Spike') 210 | comp.FR = struct; 211 | comp.FR.FR_Obs = FR_Obs; 212 | end 213 | %% sets up REALIZ 214 | REALIZ = struct; 215 | REALIZ.states = states; 216 | REALIZ.input = Input; 217 | REALIZ.Y_Obs = Y_Obs; 218 | REALIZ.N_Obs = N_Obs; 219 | REALIZ.FR_Obs = FR_Obs; 220 | 221 | 222 | end 223 | 224 | -------------------------------------------------------------------------------- /generate_state_space/generate_spike_field_from_statespace.m: -------------------------------------------------------------------------------- 1 | function [ REALIZ, TRUE ] = generate_spike_field_from_statespace( OPTIONS ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % This function generates the spike lfp activtiy from a simulated state space 9 | % INPUTS: - OPTIONS: a struct contatining options of the state space 10 | % -delta: time step 11 | % -scale: scale of observations, 'MS', 'Spike', 'LFP' 12 | % -dim_inp: arbitrary dimension of input 13 | % -Ttrain: number of training samples 14 | % -dim_Y: number of lfp features 15 | % -normalization_c: how to normalize C matrix for emmision 16 | % matrix of lfp features 17 | % -scale_dif: scale difference between spiking and lfp: k 18 | % spikes are available every time step and lfp are available 19 | % every k steps, k, 2k, 3k, ... 20 | % -dim_N: number of spiking neurons 21 | % -spike_modulation: set to 'modesonly' 22 | % 23 | % OUTPUTS: - REALIZ: a struct contating realized values of spike and lfp, with these fields 24 | % - states: latent states time series 25 | % - input: input time series 26 | % - N_Obs: spiking time series 27 | % - FR_Obs: firing rate time series 28 | % - Y_Obs: lfp features time series 29 | % - TRUE: struct contatining true model parameters 30 | % - comp: struct contatining eigenvalues and some featuers of the state space model 31 | %% generate the eigenvalues (change this if you want to change eigenvalues) 32 | % generate eigenvalues (way 1) 33 | % dim_can = 8; 34 | % [eigVals,res] = selectRandomPoles([30*3 30*2 30], [0.995 0.96 0.90], pi/500 * ones(3,1), [], dim_can); 35 | % res.poles(res.sysPoleInd, :); %poles 36 | % decay_can = ((OPTIONS.delta)) ./ ( log(1./res.allMags(res.sysPoleInd, :)) ); 37 | % freq_can = res.allTheta(res.sysPoleInd, :)/ (2 * pi * OPTIONS.delta); 38 | % generate eigenvalues (way 2, fixed) 39 | dim_can = 8; 40 | decay_can = [0.6,0.07,0.1,0.8]; 41 | freq_can = [0.3,2.8,1,2]; 42 | Apolehandles = struct;Apolehandles.freq = freq_can; 43 | Apolehandles.decay = decay_can;Apolehandles.drawing = 'fixed';Apolehandles.delta = OPTIONS.delta; 44 | %% generate range of R, Q, C and Theta (read build_statespace_realiz.m for more info) 45 | R_range_can = struct;R_range_can.Rmean = 1500;R_range_can.Rstd = 100; 46 | Theta_mod_can = struct;Theta_mod_can.modulation_depth_mean = 1.8; 47 | Theta_mod_can.modulation_depth_std = 0.1; Theta_mod_can.modulation_mean_mean = 2; 48 | Theta_mod_can.modulation_mean_std = 0.1; 49 | C_mod_can = struct;C_mod_can.mag = 3; 50 | vhandles = struct; 51 | vhandles.dim = dim_can; 52 | vhandles.Apolehandles = Apolehandles; 53 | vhandles.v_R_range = R_range_can; 54 | vhandles.v_c_mod = C_mod_can; 55 | vhandles.v_theta_mod = Theta_mod_can; 56 | %% generate system 57 | [ REALIZ,TRUE,~] = build_statespace_realiz( OPTIONS,vhandles ); 58 | 59 | end 60 | 61 | -------------------------------------------------------------------------------- /generate_state_space/linear_obs_generator.m: -------------------------------------------------------------------------------- 1 | function [ Y_Obs ] = linear_obs_generator( states,C,D,R,input,bias ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % generates linear observations from 9 | % Y_Obs(t) = C * states(t) + D * input(t) + bias + r(t); cov(r(t)) = R 10 | if isequal(D,zeros(size(D))) 11 | input = zeros( size(D,2) , size(states,2) ); 12 | end 13 | 14 | Y_Obs = NaN(size(C,1),size(states,2)); 15 | dim_Y = size(C,1); 16 | for i = 1: size(states,2) 17 | Y_Obs(:,i) = C * states(:,i) + D*input(:,i) + transpose(mvnrnd(zeros(dim_Y,1),R)) + bias; 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /generate_state_space/spike_generator.m: -------------------------------------------------------------------------------- 1 | function [ N_Obs,FR ] = spike_generator(Theta,states,delta) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %This function generates N_Obs and Firing Rate from states 9 | % INPUTS: -Theta: spike modulation matrix, (dim + 1, N), where dim is the dimension 10 | % of the latent state, N is the number of neurons 11 | % - states: latent states, (dim, T) 12 | % - delta: time step 13 | % 14 | % OUTPUT: - N_Obs: discrete spikes, (N, T) 15 | % - FR: firing rate of spikes, (N, T) 16 | 17 | %% get some values 18 | N=size(Theta,2); 19 | Tmain = size(states,2); 20 | [dim,~]=size(states); 21 | FR=NaN(N,Tmain); 22 | %% generate firing rate 23 | for i=1:Tmain 24 | for j=1:N 25 | FR(j,i)=exp(Theta(1,j)' + Theta(2:dim+1,j)' * states(:,i)) * delta; 26 | end 27 | end 28 | %% generate spikes 29 | %use this function to generate spikes 30 | [N_Obs,~]=NeuronSigSim_Multi_Open(states,Theta,delta,1); 31 | 32 | 33 | end 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /generate_state_space/state_generator.m: -------------------------------------------------------------------------------- 1 | function [ states ] = state_generator( A,B,Q,Input,init,T ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | % this function generates a time series of states using 9 | % states(t+1) = A * states(t) + B * Input(t) + q(t) 10 | % INPUTS: (1)A: state transition matrix (dim, dim) 11 | % (2)B: the input-state matrix (dim, dim_input) 12 | % (3)Input: the input time series (dim_input, T) 13 | % (2)Q: state noise covariance matrix (dim, dim) 14 | % (3)init: init has to be a cloumn vector, of initial state (dim, 1) 15 | % (4)T: number of time samples (1) 16 | % 17 | % OUTPUTS: (1)states: states time series (dim, T) 18 | 19 | % if B is zero inoput is not important 20 | %% 21 | if isequal(B,zeros(size(B))) 22 | Input = zeros( size(B,2) , T ); 23 | end 24 | 25 | %% generate states 26 | dim = size(A,1); 27 | states(:,1) = init; 28 | for i = 2:T 29 | states(:,i) = A * states(:,i-1) + + B * Input(:,i) + transpose(mvnrnd(zeros(dim,1),Q)); 30 | end 31 | 32 | end 33 | 34 | -------------------------------------------------------------------------------- /generate_state_space/theta_generator_onlymodes.m: -------------------------------------------------------------------------------- 1 | function [ Theta_meanFR,Theta_modes ] = theta_generator_onlymodes( modes,N,type,handles ) 2 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 | % Copyright (c) 2020 University of Southern California 4 | % See full notice in LICENSE.md 5 | % Hamidreza Abbaspourazad (@salarabb) and Maryam M. Shanechi 6 | % Shanechi Lab, University of Southern California 7 | % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | %This function sets some random parameters for spike modulation 9 | % INPUTS: - modes: states time series 10 | % - N: number of neurons 11 | % - type: set to 'modesonly' 12 | % - handles: 13 | % - modulation_depth_mean: mean of modulation vector of modes 14 | % for different neurons 15 | % - modulation_depth_std: std of modulation vector of modes 16 | % for different neurons 17 | % - modulation_mean_mean: mean of mean firing 18 | % rate for different neurons 19 | % - modulation_mean_std: std of mean firing 20 | % rate for different neurons 21 | % 22 | % OUTPUT: - Theta_meanFR: only first row will be used,i.e., mean FR, (dim + 1, N) 23 | % - Theta_modes: contains the 24 | % modulation parameters, (dim, N) 25 | % Theta can be derived as: 26 | % Theta = zeros(dim,N); 27 | % Theta(1,:) = Theta_meanFR(1,:); 28 | % Theta(2:end,:) = Theta_modes; 29 | %% get handles 30 | modulation_depth_mean = handles.modulation_depth_mean; 31 | modulation_depth_std = handles.modulation_depth_std; 32 | 33 | modulation_mean_mean = handles.modulation_mean_mean; 34 | modulation_mean_std = handles.modulation_mean_std; 35 | %% get the number of modes 36 | [dim_modes,T] = size(modes); 37 | num_modes = dim_modes/2; 38 | if dim_modes/2 - floor(dim_modes/2) ~=0 39 | error('modes need to come in double form!') 40 | end 41 | %% contribution 42 | max_modes = zeros(num_modes,1); 43 | for i=1:T 44 | for mode = 1:num_modes 45 | if norm(modes([(mode*2-1):mode*2],i))>max_modes(mode) 46 | max_modes(mode)=norm(modes([(mode*2-1):mode*2],i)); 47 | end 48 | end 49 | end 50 | 51 | Theta_meanFR = NaN(1,N); 52 | Theta_meanFR(1,:) = random('Normal',modulation_mean_mean,modulation_mean_std,1,N);% fo increasing centerout decoding performance 53 | Theta_modes = zeros(dim_modes,N); 54 | 55 | if strcmp(type,'modesonly') 56 | 57 | Nmodes = N; 58 | if Nmodes ~= 0 59 | %modes neurons 60 | mode_divider = (Nmodes-mod(Nmodes,num_modes))/num_modes; 61 | anglemode = linspace(0,2*pi,mode_divider); 62 | 63 | for i=1:Nmodes 64 | if dim_modes == 1 65 | v=random('Normal',0.9,0.1); 66 | Theta_modes(2,i) = v*nm; 67 | %Theta(2,i)=1.1559; 68 | else 69 | 70 | 71 | mode = ceil(i/mode_divider); 72 | if i > mode_divider * num_modes 73 | mode = num_modes; 74 | end 75 | whichangle = mod(i,mode_divider); 76 | if whichangle == 0 77 | whichangle = mode_divider; 78 | end 79 | v = [ cos(anglemode(whichangle)),sin(anglemode(whichangle)) ]; 80 | 81 | contrib_mode = random('Normal',modulation_depth_mean,modulation_depth_std,1,1); 82 | mode_eff = v * modes([(mode*2-1):mode*2],:); 83 | 84 | max_mode_eff = 0; 85 | for t=1:T 86 | if norm(mode_eff(:,t))>max_mode_eff 87 | max_mode_eff=norm(mode_eff(:,t)); 88 | end 89 | 90 | end 91 | nm = contrib_mode/max_mode_eff; 92 | 93 | Theta_modes([mode*2-1,mode*2],i) = (v)*nm; 94 | 95 | 96 | end 97 | end 98 | 99 | end 100 | 101 | else 102 | error('no correct input type for the theta generator function') 103 | end 104 | 105 | 106 | end 107 | 108 | --------------------------------------------------------------------------------