private void init(double[,] value, int k, NonnegativeFactorizationAlgorithm algorithm,
double[,] h0, double[,] w0, int attempts, int maxIterations, double errorTolerance,
double changeTolerance)
{
#region Initial argument checking
if (value == null)
{
throw new ArgumentNullException("value");
}
if (k < 0 || k > value.GetLength(1))
{
throw new ArgumentException(
"The approximation rank k should be a positive integer equal or less than the number of columns of the matrix to be decomposed.",
"k");
}
if (h0 != null)
{
if (h0.GetLength(0) != k || h0.GetLength(1) != value.GetLength(1))
throw new ArgumentException(
"The initial coefficient matrix should have the same number of columns as the value matrix and the same number of rows as the desired rank approximation.",
"h0");
}
if (w0 != null)
{
if (w0.GetLength(0) != value.GetLength(0) || w0.GetLength(1) != k)
throw new ArgumentException(
"The initial weight matrix should have the same number of rows as the value matrix and the same number of columns as the desired rank approximation.",
"w0");
}
if (attempts <= 0)
{
throw new ArgumentException("The number of parallel attempts should be greater than zero.", "attempts");
}
if (maxIterations <= 0)
{
throw new ArgumentException("The maximum number of iterations should be greater than zero.",
"maxIterations");
}
if (errorTolerance <= 0)
{
throw new ArgumentException("The error tolerance convergence threshold should be greater than zero.",
"errorTolerance");
}
if (changeTolerance <= 0)
{
throw new ArgumentException(
"The maximum absolute factor change convergence threshold should be greater than zero.",
"changeTolerance");
}
#endregion
// Initialization
double[,] X = value;
n = X.GetLength(0);
m = X.GetLength(1);
this.k = k;
W = w0;
H = h0;
// First, check for special case
if (W == null && H == null)
{
// In the case both W and H are to
// be found, the answer is direct.
if (k == m)
{
W = X;
H = Matrix.Identity(k);
}
else if (k == n)
{
W = Matrix.Identity(k);
H = X;
}
}
// Next we will attempt several factorizations
// and return the one which produces the best
// approximation norm.
// TODO: This should be parallelized.
double bestNorm = Double.PositiveInfinity;
double[,] bestW = null;
double[,] bestH = null;
// Perform several parallel attempts
for (int t = 0; t < attempts; t++)
{
// Perform round initialization to random values
if (W == null || t > 0) W = Matrix.Random(n, k);
if (H == null || t > 0) H = Matrix.Random(k, m);
double norm = Double.PositiveInfinity;
// Perform a factorization
norm = nnmf(X, ref W, ref H, algorithm,
maxIterations, errorTolerance, changeTolerance);
// Check if this has been the
// best factorization so far
if (norm < bestNorm)
{
bestNorm = norm;
bestW = W;
bestH = H;
}
}
// Select the best factorization
// and normalize the results
H = bestH;
W = bestW;
for (int i = 0; i < k; i++)
{
double norm = 0.0, v;
for (int j = 0; j < m; j++)
{
v = H[i, j];
norm += v*v;
}
if (norm == 0) // If any of the norms equals 0
{
// Then the algorithm has converged to a solution
// with a different rank than the rank specified.
norm = 1;
}
else
{
norm = System.Math.Sqrt(norm);
}
// Normalize H and re-scale W
for (int j = 0; j < m; j++)
H[i, j] /= norm;
for (int j = 0; j < n; j++)
W[j, i] *= norm;
}
// Then order by the norms of W
var wnorm = new double[k];
var index = new int[k];
for (int j = 0; j < k; j++)
{
double norm = 0.0, d;
for (int i = 0; i < n; i++)
{
d = W[i, j];
norm += d*d;
}
// Set as minus to order
// in descending order
wnorm[j] = -norm;
index[j] = j;
}
Array.Sort(wnorm, index);
W = W.Submatrix(null, index);
H = H.Submatrix(index, null);
}