78 const int nCh =
static_cast<int>(matData.rows());
79 const int nSamples =
static_cast<int>(matData.cols());
81 if (nComponents <= 0 || nComponents > nCh) {
88 VectorXd vecMean = matData.rowwise().mean();
89 MatrixXd matCentered = matData.colwise() - vecMean;
94 MatrixXd matWhitening, matDewhitening;
95 MatrixXd matWhite = whiten(matCentered, nComponents, matWhitening, matDewhitening);
108 std::mt19937 rng(
static_cast<unsigned>(randomSeed));
109 std::normal_distribution<double> dist(0.0, 1.0);
111 MatrixXd W_ica(nComponents, nComponents);
112 bool bConverged =
true;
114 for (
int i = 0; i < nComponents; ++i) {
116 VectorXd w(nComponents);
117 for (
int k = 0; k < nComponents; ++k) {
122 bool compConverged =
false;
123 for (
int iter = 0; iter < maxIter; ++iter) {
125 RowVectorXd u = w.transpose() * matWhite;
126 RowVectorXd g = u.array().tanh();
127 RowVectorXd gPrime = 1.0 - g.array().square();
130 VectorXd wNew = (matWhite * g.transpose()) / nSamples
134 gramSchmidt(wNew, W_ica, i);
137 double norm = wNew.norm();
140 for (
int k = 0; k < nComponents; ++k) {
149 double delta = std::abs(wNew.dot(w)) - 1.0;
152 if (std::abs(delta) < tol) {
153 compConverged =
true;
158 if (!compConverged) {
160 qWarning() <<
"ICA::run: component" << i <<
"did not converge within" << maxIter <<
"iterations.";
163 W_ica.row(i) = w.transpose();
173 MatrixXd matUnmixing = W_ica * matWhitening;
174 MatrixXd matMixing = matDewhitening * W_ica.transpose();
179 MatrixXd matSources = matUnmixing * matCentered;
185 result.
vecMean = std::move(vecMean);
204 const QVector<int>& excludedComponents)
206 if (excludedComponents.isEmpty()) {
217 MatrixXd matCentered = matData.colwise() - result.
vecMean;
220 const int nExcl = excludedComponents.size();
221 const int nCh =
static_cast<int>(result.
matMixing.rows());
222 const int nSamps =
static_cast<int>(matCentered.cols());
224 MatrixXd partialMixing(nCh, nExcl);
225 MatrixXd partialSources(nExcl, nSamps);
227 for (
int i = 0; i < nExcl; ++i) {
228 int idx = excludedComponents[i];
229 if (idx >= 0 && idx <
static_cast<int>(result.
matUnmixing.rows())) {
230 partialMixing.col(i) = result.
matMixing.col(idx);
231 partialSources.row(i) = result.
matUnmixing.row(idx) * matCentered;
233 partialMixing.col(i).setZero();
234 partialSources.row(i).setZero();
238 return matData - partialMixing * partialSources;
static IcaResult run(const Eigen::MatrixXd &matData, int nComponents=-1, int maxIter=200, double tol=1e-4, int randomSeed=42)