71 m_nChannels =
static_cast<int>(data.rows());
74 p = selectOrderBIC(data);
77 fitLevinsonDurbin(data, p);
105 QVector<MatrixXcd> vecH;
106 vecH.reserve(
static_cast<int>(freqs.size()));
108 const int nCh = m_nChannels;
109 const MatrixXcd matI = MatrixXcd::Identity(nCh, nCh);
110 const std::complex<double> j(0.0, 1.0);
112 for(
int fi = 0; fi < freqs.size(); ++fi) {
113 MatrixXcd matA = matI;
115 for(
int k = 0; k < m_order; ++k) {
116 const double phase = -2.0 *
M_PI * freqs(fi) * (k + 1);
117 const std::complex<double> expVal = std::exp(j * phase);
118 matA -= m_coeffs[k].cast<std::complex<double>>() * expVal;
121 vecH.append(matA.inverse());
132 QVector<MatrixXcd> vecS;
133 vecS.reserve(vecH.size());
135 const MatrixXcd matSigma = m_noiseCov.cast<std::complex<double>>();
137 for(
int fi = 0; fi < vecH.size(); ++fi) {
138 vecS.append(vecH[fi] * matSigma * vecH[fi].adjoint());
146void MvarModel::fitLevinsonDurbin(
const MatrixXd& data,
int p)
149 m_nChannels =
static_cast<int>(data.rows());
151 const int nCh = m_nChannels;
152 const int nSamples =
static_cast<int>(data.cols());
153 const int nObs = nSamples - p;
156 qWarning() <<
"MvarModel::fitLevinsonDurbin - Not enough samples for model order" << p;
158 m_noiseCov = MatrixXd::Identity(nCh, nCh);
163 MatrixXd dataCentered = data;
164 for(
int i = 0; i < nCh; ++i) {
165 dataCentered.row(i).array() -= dataCentered.row(i).mean();
174 MatrixXd matY = dataCentered.rightCols(nObs);
175 MatrixXd matZ(nCh * p, nObs);
177 for(
int k = 0; k < p; ++k) {
178 matZ.middleRows(k * nCh, nCh) = dataCentered.middleCols(p - 1 - k, nObs);
182 MatrixXd matZZT = matZ * matZ.transpose();
183 MatrixXd matYZT = matY * matZ.transpose();
184 MatrixXd matA = matYZT * matZZT.ldlt().solve(MatrixXd::Identity(nCh * p, nCh * p));
189 for(
int k = 0; k < p; ++k) {
190 m_coeffs.append(matA.middleCols(k * nCh, nCh));
194 MatrixXd matE = matY - matA * matZ;
195 m_noiseCov = (matE * matE.transpose()) /
static_cast<double>(nObs);
200int MvarModel::selectOrderBIC(
const MatrixXd& data,
int maxOrder)
const
202 const int nCh =
static_cast<int>(data.rows());
203 const int nSamples =
static_cast<int>(data.cols());
206 maxOrder = qMin(maxOrder, nSamples / (nCh + 1));
212 MatrixXd dataCentered = data;
213 for(
int i = 0; i < nCh; ++i) {
214 dataCentered.row(i).array() -= dataCentered.row(i).mean();
217 double bestBIC = std::numeric_limits<double>::max();
220 for(
int p = 1; p <= maxOrder; ++p) {
221 const int nObs = nSamples - p;
222 if(nObs <= nCh * p) {
227 MatrixXd matY = dataCentered.rightCols(nObs);
228 MatrixXd matZ(nCh * p, nObs);
230 for(
int k = 0; k < p; ++k) {
231 matZ.middleRows(k * nCh, nCh) = dataCentered.middleCols(p - 1 - k, nObs);
235 MatrixXd matZZT = matZ * matZ.transpose();
236 MatrixXd matYZT = matY * matZ.transpose();
237 MatrixXd matA = matYZT * matZZT.ldlt().solve(MatrixXd::Identity(nCh * p, nCh * p));
240 MatrixXd matE = matY - matA * matZ;
241 MatrixXd matSigma = (matE * matE.transpose()) /
static_cast<double>(nObs);
244 double detSigma = matSigma.determinant();
245 if(detSigma <= 0.0) {
249 const int nParams = p * nCh * nCh;
250 double bic = nObs * std::log(detSigma) + nParams * std::log(
static_cast<double>(nObs));
MvarModel class declaration.
Functional connectivity metrics (coherence, PLV, cross-correlation, etc.).
Eigen::MatrixXd noiseCov() const
QVector< Eigen::MatrixXcd > spectralMatrix(const Eigen::VectorXd &freqs) const
QVector< Eigen::MatrixXcd > transferFunction(const Eigen::VectorXd &freqs) const
void fit(const Eigen::MatrixXd &data, int p=0)
QVector< Eigen::MatrixXd > coefficients() const