private double nnmf(double[,] value,
ref double[,] w0, ref double[,] h0, NonnegativeFactorizationAlgorithm alg,
int maxIterations, double normChangeThreshold, double maxFactorChangeThreshold)
{
double[,] v = value;
double[,] h = h0;
double[,] w = w0;
double[,] z = null;
double dnorm0 = 0.0; // previous iteration norm
// Main loop
for (int iteration = 0; iteration < maxIterations; iteration++)
{
// Check which algorithm should be used
if (alg == NonnegativeFactorizationAlgorithm.MultiplicativeUpdate)
{
// Multiplicative update formula
h = new double[k,m];
w = new double[n,k];
if (z == null) z = new double[k,k];
// Update H
for (int i = 0; i < k; i++)
{
for (int j = i; j < k; j++)
{
double s = 0.0;
for (int l = 0; l < n; l++)
s += w0[l, i]*w0[l, j];
z[i, j] = z[j, i] = s;
}
for (int j = 0; j < m; j++)
{
double d = 0.0;
for (int l = 0; l < k; l++)
d += z[i, l]*h0[l, j];
double s = 0.0;
for (int l = 0; l < n; l++)
s += w0[l, i]*v[l, j];
h[i, j] = h0[i, j]*s/(d + Special.Epslon(s));
}
}
// Update W
for (int j = 0; j < k; j++)
{
for (int i = j; i < k; i++)
{
double s = 0.0;
for (int l = 0; l < m; l++)
s += h[i, l]*h[j, l];
z[i, j] = z[j, i] = s;
}
for (int i = 0; i < n; i++)
{
double d = 0.0;
for (int l = 0; l < k; l++)
d += w0[i, l]*z[j, l];
double s = 0.0;
for (int l = 0; l < m; l++)
s += v[i, l]*h[j, l];
w[i, j] = w0[i, j]*s/(d + Special.Epslon(s));
}
}
}
else
{
// Alternating least squares update
h = w0.Solve(v);
makepositive(h);
w = v.Divide(h);
makepositive(w);
}
// Reconstruct matrix A using W and H
double[,] r = w.Multiply(h);
// Get norm of difference
double dnorm = normdiff(v, r);
// Get maximum change in factors
double dw = maxdiff(w0, w)/(sqrteps + maxabs(w0));
double dh = maxdiff(h0, h)/(sqrteps + maxabs(h0));
double delta = System.Math.Max(dw, dh);
if (iteration > 0) // Check for convergence
{
if (delta <= maxFactorChangeThreshold ||
dnorm <= normChangeThreshold*dnorm0)
break;
}
// Remember previous iteration results
dnorm0 = dnorm;
w0 = w;
h0 = h;
}
return dnorm0;
}