// create matrix of attribute values and compute singular value decomposition
double [][] trainValues = new double[m_numAttributes][m_numInstances];
for (int i = 0; i < m_numAttributes; i++) {
trainValues[i] = m_trainInstances.attributeToDoubleArray(i);
}
Matrix trainMatrix = new Matrix(trainValues);
// svd requires rows >= columns, so transpose data if necessary
if (m_numAttributes < m_numInstances) {
m_transpose = true;
trainMatrix = trainMatrix.transpose();
}
SingularValueDecomposition trainSVD = trainMatrix.svd();
m_u = trainSVD.getU(); // left singular vectors
m_s = trainSVD.getS(); // singular values
m_v = trainSVD.getV(); // right singular vectors
// find actual rank to use
int maxSingularValues = trainSVD.rank();
for (int i = 0; i < m_s.getRowDimension(); i++) {
m_sumSquaredSingularValues += m_s.get(i, i) * m_s.get(i, i);
}
if (maxSingularValues == 0) { // no nonzero singular values (shouldn't happen)
// reset values from computation
m_s = null;
m_u = null;
m_v = null;
m_sumSquaredSingularValues = 0.0;
throw new Exception("SVD computation produced no non-zero singular values.");
}
if (m_rank > maxSingularValues || m_rank <= 0) { // adjust rank if too high or too low
m_actualRank = maxSingularValues;
} else if (m_rank < 1.0) { // determine how many singular values to include for desired coverage
double currentSumOfSquaredSingularValues = 0.0;
for (int i = 0; i < m_s.getRowDimension() && m_actualRank == -1; i++) {
currentSumOfSquaredSingularValues += m_s.get(i, i) * m_s.get(i, i);
if (currentSumOfSquaredSingularValues / m_sumSquaredSingularValues >= m_rank) {
m_actualRank = i + 1;
}
}
} else {
m_actualRank = (int) m_rank;
}
// lower matrix ranks, adjust for transposition (if necessary), and
// compute matrix for transforming future instances
if (m_transpose) {
Matrix tempMatrix = m_u;
m_u = m_v;
m_v = tempMatrix;
}
m_u = m_u.getMatrix(0, m_u.getRowDimension() - 1, 0, m_actualRank - 1);
m_s = m_s.getMatrix(0, m_actualRank - 1, 0, m_actualRank - 1);