50 double dFMin,
double dFMax,
55 const int nAtoms = 2 * iNFreqs;
56 MatrixXd dict = MatrixXd::Zero(nAtoms, iNSamples);
58 VectorXd timeVec(iNSamples);
59 for (
int t = 0; t < iNSamples; ++t) {
60 timeVec(t) =
static_cast<double>(t) / dSFreq;
64 for (
int f = 0; f < iNFreqs; ++f) {
67 double logMin = std::log(dFMin);
68 double logMax = std::log(dFMax);
69 freq = std::exp(logMin + (logMax - logMin) * f / (iNFreqs - 1));
71 freq = (dFMin + dFMax) / 2.0;
75 double sigma = 3.0 / (2.0 *
M_PI * freq);
77 double tCenter = timeVec(iNSamples / 2);
79 for (
int t = 0; t < iNSamples; ++t) {
80 double dt = timeVec(t) - tCenter;
81 double envelope = std::exp(-0.5 * dt * dt / (sigma * sigma));
82 dict(2 * f, t) = envelope * std::cos(2.0 *
M_PI * freq * timeVec(t));
83 dict(2 * f + 1, t) = envelope * std::sin(2.0 *
M_PI * freq * timeVec(t));
87 double normCos = dict.row(2 * f).norm();
89 dict.row(2 * f) /= normCos;
91 double normSin = dict.row(2 * f + 1).norm();
93 dict.row(2 * f + 1) /= normSin;
102 const MatrixXd& matData,
107 const int nChannels = matGain.rows();
108 const int nSources = matGain.cols();
109 const int nTimes = matData.cols();
111 if (nChannels == 0 || nSources == 0 || nTimes == 0) {
114 const int nAtoms = 2 * params.
iNFreqs;
116 if (matData.rows() != nChannels) {
117 qWarning() <<
"[InvTfMxne::compute] Dimension mismatch: data rows" << matData.rows()
118 <<
"!= gain rows" << nChannels;
135 const double gNorm = JacobiSVD<MatrixXd>(matGain).singularValues()(0);
136 const double phiNorm = JacobiSVD<MatrixXd>(Phi).singularValues()(0);
137 const double lipschitz = gNorm * gNorm * phiNorm * phiNorm;
140 const auto prox = [&](MatrixXd& V) {
141 const double threshL1 = params.
dAlphaTime / lipschitz;
142 const double threshL21 = params.
dAlphaSpace / lipschitz;
143 V = V.array().sign() * (V.array().abs() - threshL1).max(0.0);
144 for (
int j = 0; j < nSources; ++j) {
145 const double groupNorm = V.row(j).norm();
146 if (groupNorm > threshL21)
147 V.row(j) *= 1.0 - threshL21 / groupNorm;
152 const auto objectiveOf = [&](
const MatrixXd& V) {
153 return 0.5 * (matData - matGain * V * Phi).squaredNorm() + params.
dAlphaSpace * V.rowwise().norm().sum() + params.
dAlphaTime * V.cwiseAbs().sum();
156 MatrixXd
Z = MatrixXd::Zero(nSources, nAtoms);
159 double prevObj = objectiveOf(
Z);
161 MatrixXd zNew =
Y + matGain.transpose() * (matData - matGain *
Y * Phi) * Phi.transpose() / lipschitz;
163 const double tNew = 0.5 * (1.0 + std::sqrt(1.0 + 4.0 * tk * tk));
164 Y = zNew + ((tk - 1.0) / tNew) * (zNew -
Z);
168 const double objective = objectiveOf(
Z);
169 if (std::abs(prevObj - objective) <= params.
dTolerance * std::abs(objective))
173 MatrixXd residual = matData - matGain *
Z * Phi;
176 MatrixXd
X =
Z * Phi;
179 QVector<int> activeVertices;
180 for (
int j = 0; j < nSources; ++j) {
181 if (
Z.row(j).norm() > 1e-12) {
182 activeVertices.append(j);
188 if (params.
bDebias && !activeVertices.isEmpty()) {
189 MatrixXd Gactive(nChannels, activeVertices.size());
190 for (
int i = 0; i < activeVertices.size(); ++i) {
191 Gactive.col(i) = matGain.col(activeVertices[i]);
194 finalX = Gactive.bdcSvd<ComputeThinU | ComputeThinV>().solve(matData);
195 }
else if (!activeVertices.isEmpty()) {
196 finalX = MatrixXd(activeVertices.size(), nTimes);
197 for (
int i = 0; i < activeVertices.size(); ++i) {
198 finalX.row(i) =
X.row(activeVertices[i]);
201 finalX = MatrixXd::Zero(0, nTimes);
205 VectorXi vertices(activeVertices.size());
206 for (
int i = 0; i < activeVertices.size(); ++i) {
207 vertices(i) = activeVertices[i];
211 static_cast<float>(1.0 / params.
dSFreq));
217 if (!activeVertices.isEmpty()) {
219 for (
int i = 0; i < activeVertices.size(); ++i) {
static Eigen::MatrixXd buildGaborDictionary(int iNSamples, int iNFreqs, double dFMin, double dFMax, double dSFreq)
Build a Gabor dictionary (tight frame) for time-frequency decomposition.
static InvTfMxneResult compute(const Eigen::MatrixXd &matGain, const Eigen::MatrixXd &matData, const InvTfMxneParams ¶ms=InvTfMxneParams())
Compute the TF-MxNE inverse solution.