Пример #1
0
GSparseMatrix* GSparseMatrix::subMatrix(int row, int col, int height, int width)
{
	if(row < 0 || col < 0 || row + height >= (int)m_rows || col + width >= (int)m_cols || height < 0 || width < 0)
		ThrowError("out of range");
	GSparseMatrix* pSub = new GSparseMatrix(height, width);
	for(int y = 0; y < height; y++)
	{
		for(int x = 0; x < width; x++)
			pSub->set(y, x, get(row + y, col + x));
	}
	return pSub;
}
Пример #2
0
/***********************************************************************//**
 * @brief Test matrix copy
 ***************************************************************************/
void TestGSparseMatrix::copy_matrix(void)
{
    // Copy matrix
	GSparseMatrix test = m_test;
    
    // Test if original and compied matrices are correct
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test), "Test matrix copy operator",
                "Found:\n"+test.print()+"\nExpected:\n"+m_test.print());

    // Return
    return;
}
Пример #3
0
/***********************************************************************//**
 * @brief GSparseMatrix to GMatrix storage class convertor
 *
 * @param[in] matrix Sparse matrix (GSparseMatrix).
 *
 * This constructor converts a sparse matrix (of type GSparseMatrix) into a
 * generic matrix. As the result is generic, the conversion will succeed in
 * all cases. 
 ***************************************************************************/
GMatrix::GMatrix(const GSparseMatrix& matrix) : GMatrixBase(matrix)
{
    // Initialise class members for clean destruction
    init_members();

    // Construct matrix
    alloc_members(matrix.rows(), matrix.cols());

    // Fill matrix
    for (int col = 0; col < matrix.cols(); ++col) {
        for (int row = 0; row < matrix.rows(); ++row) {
            (*this)(row, col) = matrix(row, col);
        }
    }

    // Return
    return;
}
Пример #4
0
// virtual
void GNaiveBayes::trainSparse(GSparseMatrix& features, GMatrix& labels)
{
	if(features.rows() != labels.rows())
		throw Ex("Expected the features and labels to have the same number of rows");
	size_t featureDims = features.cols();
	GUniformRelation featureRel(featureDims, 2);
	beginIncrementalLearning(featureRel, labels.relation());
	GVec fullRow(featureDims);
	for(size_t n = 0; n < features.rows(); n++)
	{
		features.fullRow(fullRow, n);
		for(size_t i = 0; i < featureDims; i++)
		{
			if(fullRow[i] < 1e-6)
				fullRow[i] = 0.0;
			else
				fullRow[i] = 1.0;
		}
		trainIncremental(fullRow, labels[n]);
	}
}
Пример #5
0
/***********************************************************************//**
 * @brief GSparseMatrix to GSymMatrix storage class convertor
 *
 * @param[in] matrix Sparse matrix (GSparseMatrix).
 *
 * @exception GException::matrix_not_symmetric
 *            Sparse matrix is not symmetric.
 *
 * Converts a sparse matrix into the symmetric storage class. If the input
 * matrix is not symmetric, an exception is thrown.
 ***************************************************************************/
GSymMatrix::GSymMatrix(const GSparseMatrix& matrix)
{
    // Initialise class members for clean destruction
    init_members();

    // Allocate matrix memory
    alloc_members(matrix.rows(), matrix.cols());

    // Fill matrix
    for (int col = 0; col < matrix.cols(); ++col) {
        for (int row = col; row < matrix.rows(); ++row) {
            double value_ll = matrix(row,col);
            double value_ur = matrix(col,row);
            if (value_ll != value_ur) {
                throw GException::matrix_not_symmetric(G_CAST_SPARSEMATRIX,
                                                       matrix.rows(),
                                                       matrix.cols());
            }
            (*this)(row, col) = matrix(row, col);
        }
    }

    // Return
    return;
}
Пример #6
0
GSparseMatrix* GRecommenderLib::loadSparseData(const char* szFilename)
{
	// Load the dataset by extension
	PathData pd;
	GFile::parsePath(szFilename, &pd);
	if(_stricmp(szFilename + pd.extStart, ".arff") == 0)
	{
		// Convert a 3-column dense ARFF file to a sparse matrix
		GMatrix data;
		data.loadArff(szFilename);
		if(data.cols() != 3)
			throw Ex("Expected 3 columns: 0) user or row-index, 1) item or col-index, 2) value or rating");
		double m0 = data.columnMin(0);
		double r0 = data.columnMax(0) - m0;
		double m1 = data.columnMin(1);
		double r1 = data.columnMax(1) - m1;
		if(m0 < 0 || m0 > 1e10 || r0 < 2 || r0 > 1e10)
			throw Ex("Invalid row indexes");
		if(m1 < 0 || m1 > 1e10 || r1 < 2 || r1 > 1e10)
			throw Ex("Invalid col indexes");
		GSparseMatrix* pMatrix = new GSparseMatrix(size_t(m0 + r0) + 1, size_t(m1 + r1) + 1, UNKNOWN_REAL_VALUE);
		std::unique_ptr<GSparseMatrix> hMatrix(pMatrix);
		for(size_t i = 0; i < data.rows(); i++)
		{
			GVec& row = data.row(i);
			pMatrix->set(size_t(row[0]), size_t(row[1]), row[2]);
		}
		return hMatrix.release();
	}
	else if(_stricmp(szFilename + pd.extStart, ".sparse") == 0)
	{
		GDom doc;
		doc.loadJson(szFilename);
		return new GSparseMatrix(doc.root());
	}
	throw Ex("Unsupported file format: ", szFilename + pd.extStart);
	return NULL;
}
Пример #7
0
GSparseMatrix* loadSparseData(const char* szFilename)
{
	// Load the dataset by extension
	PathData pd;
	GFile::parsePath(szFilename, &pd);
	if(_stricmp(szFilename + pd.extStart, ".arff") == 0)
	{
		// Convert a 3-column dense ARFF file to a sparse matrix
		GMatrix* pData = GMatrix::loadArff(szFilename);
		if(pData->cols() != 3)
			ThrowError("Expected 3 columns: 0) user or row-index, 1) item or col-index, 2) value or rating");
		double m0, r0, m1, r1;
		pData->minAndRange(0, &m0, &r0);
		pData->minAndRange(1, &m1, &r1);
		if(m0 < 0 || m0 > 1e10 || r0 < 2 || r0 > 1e10)
			ThrowError("Invalid row indexes");
		if(m1 < 0 || m1 > 1e10 || r1 < 2 || r1 > 1e10)
			ThrowError("Invalid col indexes");
		GSparseMatrix* pMatrix = new GSparseMatrix(size_t(m0 + r0) + 1, size_t(m1 + r1) + 1, UNKNOWN_REAL_VALUE);
		Holder<GSparseMatrix> hMatrix(pMatrix);
		for(size_t i = 0; i < pData->rows(); i++)
		{
			double* pRow = pData->row(i);
			pMatrix->set(size_t(pRow[0]), size_t(pRow[1]), pRow[2]);
		}
		return hMatrix.release();
	}
	else if(_stricmp(szFilename + pd.extStart, ".sparse") == 0)
	{
		GDom doc;
		doc.loadJson(szFilename);
		return new GSparseMatrix(doc.root());
	}
	ThrowError("Unsupported file format: ", szFilename + pd.extStart);
	return NULL;
}
Пример #8
0
// static
void GSparseMatrix::test()
{
	// Make the data
	GSparseMatrix sm(4, 4);
	sm.set(0, 0, 2.0); sm.set(0, 2, 3.0);
	sm.set(1, 0, 1.0); sm.set(2, 3, -2.0);
	sm.set(2, 2, 5.0);
	sm.set(3, 1, -3.0); sm.set(3, 3, -1.0);
	GData* fm = sm.toFullMatrix();
	Holder<GData> hFM(fm);

	// Do it with the full matrix
	GData* pU;
	double* pDiag;
	GData* pV;
	fm->singularValueDecomposition(&pU, &pDiag, &pV);
	Holder<GData> hU(pU);
	ArrayHolder<double> hDiag(pDiag);
	Holder<GData> hV(pV);

	// Do it with the sparse matrix
	GSparseMatrix* pSU;
	double* pSDiag;
	GSparseMatrix* pSV;
	sm.singularValueDecomposition(&pSU, &pSDiag, &pSV);
	Holder<GSparseMatrix> hSU(pSU);
	ArrayHolder<double> hSDiag(pSDiag);
	Holder<GSparseMatrix> hSV(pSV);

	// Check the results
	GData* pV2 = pSV->toFullMatrix();
	Holder<GData> hV2(pV2);
	double err = pV2->sumSquaredDifference(*pV, false);
	if(err > 1e-6)
		ThrowError("Failed");
}
Пример #9
0
/***********************************************************************//**
 * @brief Test Cholesky decomposition
 ***************************************************************************/
void TestGSparseMatrix::matrix_cholesky(void)
{
    // Setup matrix for Cholesky decomposition
    GSparseMatrix chol_test(5,5);
    chol_test(0,0) = 1.0;
    chol_test(0,1) = 0.2;
    chol_test(0,2) = 0.2;
    chol_test(0,3) = 0.2;
    chol_test(0,4) = 0.2;
    chol_test(1,0) = 0.2;
    chol_test(2,0) = 0.2;
    chol_test(3,0) = 0.2;
    chol_test(4,0) = 0.2;
    chol_test(1,1) = 1.0;
    chol_test(2,2) = 1.0;
    chol_test(3,3) = 1.0;
    chol_test(4,4) = 1.0;

    // Try to solve now (should not work)
    test_try("Try Cholesky solver without factorisation");
    try {
        GVector vector(5);
        vector = chol_test.cholesky_solver(vector);
        test_try_failure("Expected GException::matrix_not_factorised exception.");
    }
    catch (GException::matrix_not_factorised &e) {
        test_try_success();
    }
    catch (std::exception &e) {
        test_try_failure(e);
    }

    // Perform Cholesky decomposition
    GSparseMatrix cd = cholesky_decompose(chol_test);

    // Perform inplace Cholesky decomposition
    cd = chol_test;
    cd.cholesky_decompose();

    // Test Cholesky solver (first test)
    GVector e0(5);
    GVector a0(5);
    e0[0] = 1.0;
    a0[0] = 1.0;
    a0[1] = 0.2;
    a0[2] = 0.2;
    a0[3] = 0.2;
    a0[4] = 0.2;
    GVector s0 = cd.cholesky_solver(a0);
    double res = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 1");

    // Test Cholesky solver (second test)
	e0[0] = 0.0;
	e0[1] = 1.0;
	a0[0] = 0.2;
	a0[1] = 1.0;
	a0[2] = 0.0;
	a0[3] = 0.0;
	a0[4] = 0.0;
	s0    = cd.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 2");

    // Test Cholesky solver (third test)
	e0[1] = 0.0;
	e0[2] = 1.0;
	a0[1] = 0.0;
	a0[2] = 1.0;
	s0    = cd.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 3");

    // Test Cholesky solver (forth test)
	e0[2] = 0.0;
	e0[3] = 1.0;
	a0[2] = 0.0;
	a0[3] = 1.0;
	s0    = cd.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 4");

    // Test Cholesky solver (fifth test)
	e0[3] = 0.0;
	e0[4] = 1.0;
	a0[3] = 0.0;
	a0[4] = 1.0;
	s0    = cd.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 5");

	// Setup matrix for Cholesky decomposition with zero row/col
	GSparseMatrix chol_test_zero(6,6);
	chol_test_zero(0,0) = 1.0;
	chol_test_zero(0,1) = 0.2;
	chol_test_zero(0,2) = 0.2;
	chol_test_zero(0,4) = 0.2;
	chol_test_zero(0,5) = 0.2;
	chol_test_zero(1,0) = 0.2;
	chol_test_zero(2,0) = 0.2;
	chol_test_zero(4,0) = 0.2;
	chol_test_zero(5,0) = 0.2;
	chol_test_zero(1,1) = 1.0;
	chol_test_zero(2,2) = 1.0;
	chol_test_zero(4,4) = 1.0;
	chol_test_zero(5,5) = 1.0;

    // Test compressed Cholesky decomposition
	GSparseMatrix cd_zero = chol_test_zero;
	cd_zero.cholesky_decompose();

    // Test compressed Cholesky solver (first test)
	e0 = GVector(6);
	a0 = GVector(6);
	e0[0] = 1.0;
	a0[0] = 1.0;
	a0[1] = 0.2;
	a0[2] = 0.2;
	a0[4] = 0.2;
	a0[5] = 0.2;
	s0    = cd_zero.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 1");

    // Test compressed Cholesky solver (second test)
	e0[0] = 0.0;
	e0[1] = 1.0;
	a0[0] = 0.2;
	a0[1] = 1.0;
	a0[2] = 0.0;
	a0[4] = 0.0;
	a0[5] = 0.0;
	s0    = cd_zero.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 2");

    // Test compressed Cholesky solver (third test)
	e0[1] = 0.0;
	e0[2] = 1.0;
	a0[1] = 0.0;
	a0[2] = 1.0;
	s0    = cd_zero.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 3");

    // Test compressed Cholesky solver (forth test)
	e0[2] = 0.0;
	e0[4] = 1.0;
	a0[2] = 0.0;
	a0[4] = 1.0;
	s0    = cd_zero.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 4");

    // Test compressed Cholesky solver (fifth test)
	e0[4] = 0.0;
	e0[5] = 1.0;
	a0[4] = 0.0;
	a0[5] = 1.0;
	s0    = cd_zero.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 5");

	// Setup matrix for Cholesky decomposition with zero row/col (unsymmetric case)
	GSparseMatrix chol_test_zero2(6,5);
	chol_test_zero2(0,0) = 1.0;
	chol_test_zero2(0,1) = 0.2;
	chol_test_zero2(0,2) = 0.2;
	chol_test_zero2(0,3) = 0.2;
	chol_test_zero2(0,4) = 0.2;
	chol_test_zero2(1,0) = 0.2;
	chol_test_zero2(2,0) = 0.2;
	chol_test_zero2(4,0) = 0.2;
	chol_test_zero2(5,0) = 0.2;
	chol_test_zero2(1,1) = 1.0;
	chol_test_zero2(2,2) = 1.0;
	chol_test_zero2(4,3) = 1.0;
	chol_test_zero2(5,4) = 1.0;

    // Test compressed Cholesky decomposition (unsymmetric case)
	GSparseMatrix cd_zero2 = chol_test_zero2;
	cd_zero2.cholesky_decompose();

    // Test compressed Cholesky solver (unsymmetric case)
	e0 = GVector(5);
	a0 = GVector(6);
	e0[0] = 1.0;
	a0[0] = 1.0;
	a0[1] = 0.2;
	a0[2] = 0.2;
	a0[4] = 0.2;
	a0[5] = 0.2;
	s0    = cd_zero2.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, 
               "Test unsymmetric compressed cholesky_solver() method - 1");

    // Test compressed Cholesky solver (unsymmetric case)
	e0[0] = 0.0;
	e0[1] = 1.0;
	a0[0] = 0.2;
	a0[1] = 1.0;
	a0[2] = 0.0;
	a0[4] = 0.0;
	a0[5] = 0.0;
	s0    = cd_zero2.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, 
               "Test unsymmetric compressed cholesky_solver() method - 2");

    // Test compressed Cholesky solver (unsymmetric case)
	e0[1] = 0.0;
	e0[2] = 1.0;
	a0[1] = 0.0;
	a0[2] = 1.0;
	s0    = cd_zero2.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, 
               "Test unsymmetric compressed cholesky_solver() method - 3");

    // Test compressed Cholesky solver (unsymmetric case)
	e0[2] = 0.0;
	e0[3] = 1.0;
	a0[2] = 0.0;
	a0[4] = 1.0;
	s0    = cd_zero2.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, 
               "Test unsymmetric compressed cholesky_solver() method - 4");

    // Test compressed Cholesky solver (unsymmetric case)
	e0[3] = 0.0;
	e0[4] = 1.0;
	a0[4] = 0.0;
	a0[5] = 1.0;
	s0    = cd_zero2.cholesky_solver(a0);
	res   = max(abs(s0-e0));
    test_value(res, 0.0, 1.0e-15, 
               "Test unsymmetric compressed cholesky_solver() method - 5");

	// Test Cholesky inverter (inplace)
	GSparseMatrix unit(5,5);
	unit(0,0) = 1.0;
	unit(1,1) = 1.0;
	unit(2,2) = 1.0;
	unit(3,3) = 1.0;
	unit(4,4) = 1.0;
	GSparseMatrix chol_test_inv = chol_test;
	chol_test_inv.cholesky_invert();
    GSparseMatrix ci_product   = chol_test * chol_test_inv;
    GSparseMatrix ci_residuals = ci_product - unit;
	res = (abs(ci_residuals)).max();
    test_value(res, 0.0, 1.0e-15, "Test inplace Cholesky inverter");
            
	// Test Cholesky inverter
	chol_test_inv = cholesky_invert(chol_test);
    ci_product    = chol_test * chol_test_inv;
    ci_residuals  = ci_product - unit;
	res           = (abs(ci_residuals)).max();
    test_value(res, 0.0, 1.0e-15, "Test Cholesky inverter");

    // Test Cholesky inverter for compressed matrix
    unit = GSparseMatrix(6,6);
    unit(0,0) = 1.0;
    unit(1,1) = 1.0;
    unit(2,2) = 1.0;
    unit(4,4) = 1.0;
    unit(5,5) = 1.0;
    GSparseMatrix chol_test_zero_inv = chol_test_zero;
    chol_test_zero_inv.cholesky_invert();
    GSparseMatrix ciz_product   = chol_test_zero * chol_test_zero_inv;
    GSparseMatrix ciz_residuals = ciz_product - unit;
    res = (abs(ciz_residuals)).max();
    test_value(res, 0.0, 1.0e-15, "Test compressed matrix Cholesky inverter");

    // Return
    return;
}
Пример #10
0
/***********************************************************************//**
 * @brief Test matrix arithmetics
 *
 * Tests matrix arithmetics.
 ***************************************************************************/
void TestGSparseMatrix::matrix_arithmetics(void)
{
	// -GSparseMatrix
	GSparseMatrix test = -m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, -1.0, 0.0), "Test -GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix += GSparseMatrix
	test  = m_test;
	test += m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 2.0, 0.0), "Test GSparseMatrix += GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix -= GSparseMatrix
	test  = m_test;
	test -= m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 0.0, 0.0), "Test GSparseMatrix -= GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix *= 3.0
	test  = m_test;
	test *= 3.0;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 3.0, 0.0), "Test GSparseMatrix *= 3.0",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix /= 3.0
	test  = m_test;
	test /= 3.0;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 1.0/3.0, 0.0), "Test GSparseMatrix /= 3.0",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix + GSparseMatrix
	test = m_test + m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 2.0, 0.0), "Test GSparseMatrix + GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix - GSparseMatrix
	test = m_test - m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 0.0, 0.0), "Test GSparseMatrix - GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix * 3.0
	test = m_test * 3.0;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 3.0, 0.0), "Test GSparseMatrix * 3.0",
                "Unexpected result matrix:\n"+test.print());

	// 3.0 * GSparseMatrix
	test = 3.0 * m_test;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 3.0, 0.0), "Test 3.0 * GSparseMatrix",
                "Unexpected result matrix:\n"+test.print());

	// GSparseMatrix / 3.0
	test = m_test / 3.0;
    test_assert(check_matrix(m_test), "Test source matrix");
    test_assert(check_matrix(test, 1.0/3.0, 0.0), "Test GSparseMatrix / 3.0",
                "Unexpected result matrix:\n"+test.print());

    // Test invalid matrix addition
    test_try("Test invalid matrix addition");
    try {
        test  = m_test;
        test += m_bigger;
        test_try_failure("Expected GException::matrix_mismatch exception.");
    }
    catch (GException::matrix_mismatch &e) {
        test_try_success();
    }
    catch (std::exception &e) {
        test_try_failure(e);
    }

    // Return
    return;
}
Пример #11
0
/***********************************************************************//**
 * @brief Test value assignment
 ***************************************************************************/
void TestGSparseMatrix::assign_values(void)
{
    // Setup 3x3 matrix
    GSparseMatrix test(3,3);
    
    // Assignment individual values
    for (int i = 0; i < 3; ++i) {
        for (int k = 0; k < 3; ++k) {
            double value = i*2.0 + k*2.0;
            test(i,k)   = value;
        }
    }

    // Check assignment of individual values
    for (int i = 0; i < 3; ++i) {
        for (int k = 0; k < 3; ++k) {
            double value = i*2.0 + k*2.0;
            test_value(test(i,k), value, 1.0e-10, "Test matrix element assignment");
        }
    }

    // Verify range checking
    #ifdef G_RANGE_CHECK
    test_try("Verify range checking");
    try {
        test(3,3) = 1.0;
        test_try_failure("Expected GException::out_of_range exception.");
    }
    catch (GException::out_of_range &e) {
        test_try_success();
    }
    catch (std::exception &e) {
        test_try_failure(e);
    }
    #endif

    // Setup 10x10 matrix and keep for reference
	GSparseMatrix sparse(10,10);
	for (int i = 3; i < 5; ++i) {
        sparse(i,i) = 5.0;
    }
	GSparseMatrix initial   = sparse;
	GSparseMatrix reference = sparse;

    // Insert column into 10 x 10 matrix using large matrix stack and the
    // add_col(GVector) method
	sparse.stack_init(100,50);
	GVector column(10);
    for (int j = 0; j < 10; ++j) {
        int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
        if (col > 9) col -= 10;          // This avoids overflow
        int i_min = (j < 2) ?  0 : j-2;
        int i_max = (j > 8) ? 10 : j+2;
        column = 0.0;
        for (int i = i_min; i < i_max; ++i) {
            column[i]         = (i+1)*1;
            reference(i,col) += column[i];
        }
        sparse.add_col(column, col);
	}
	sparse.stack_destroy();
    test_assert((sparse == reference),
                "Test stack fill with large stack using add_col(GVector) method",
                "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

    // Insert column into 10 x 10 matrix using small matrix stack and the
    // add_col(GVector) method
    sparse = initial;
	sparse.stack_init(100,3);
    for (int j = 0; j < 10; ++j) {
        int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
        if (col > 9) col -= 10;          // This avoids overflow
        int i_min = (j < 2) ?  0 : j-2;
        int i_max = (j > 8) ? 10 : j+2;
        column = 0.0;
        for (int i = i_min; i < i_max; ++i) {
            column[i]         = (i+1)*1;
        }
        sparse.add_col(column, col);
	}
	sparse.stack_destroy();
    test_assert((sparse == reference),
                "Test stack fill with small stack using add_col(GVector) method",
                "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

    // Insert column into 10 x 10 matrix using tiny matrix stack and the
    // add_col(GVector) method
    sparse = initial;
	sparse.stack_init(8,3);
    for (int j = 0; j < 10; ++j) {
        int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
        if (col > 9) col -= 10;          // This avoids overflow
        int i_min = (j < 2) ?  0 : j-2;
        int i_max = (j > 8) ? 10 : j+2;
        column = 0.0;
        for (int i = i_min; i < i_max; ++i) {
            column[i]         = (i+1)*1;
        }
        sparse.add_col(column, col);
	}
	sparse.stack_destroy();
    test_assert((sparse == reference),
                "Test stack fill with tiny stack using add_col(GVector) method",
                "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

    // Insert column into 10 x 10 matrix using no matrix stack and the
    // add_col(GVector) method
    sparse = initial;
    for (int j = 0; j < 10; ++j) {
        int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
        if (col > 9) col -= 10;          // This avoids overflow
        int i_min = (j < 2) ?  0 : j-2;
        int i_max = (j > 8) ? 10 : j+2;
        column = 0.0;
        for (int i = i_min; i < i_max; ++i) {
            column[i]         = (i+1)*1;
        }
        sparse.add_col(column, col);
	}
	sparse.stack_destroy();
    test_assert((sparse == reference),
                "Test fill using add_col(GVector) method",
                "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

	// Set-up workspace for compressed column adding
	double* wrk_data = new double[10];
	int*    wrk_row  = new int[10];

    // Compressed tests
    test_try("Verify compressed column add_col() method");
    try {

        // Insert column into 10 x 10 matrix using large matrix stack and the
        // compressed column add_col() method
        sparse    = initial;
        reference = initial;
        sparse.stack_init(100,50);
        for (int j = 0; j < 10; ++j) {
            int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
            if (col > 9) col -= 10;          // This avoids overflow
            int inx   = 0;
            int i_min = (j < 3) ?  0 : j-3;
            int i_max = (j > 8) ? 10 : j+2;
            for (int i = i_min; i < i_max; ++i) {
                wrk_data[inx]     = (i+1)*3.7;
                wrk_row[inx]      = i;
                reference(i,col) += wrk_data[inx];
                inx++;
            }
            sparse.add_col(wrk_data, wrk_row, inx, col);
        }
        sparse.stack_destroy();
        test_assert((sparse == reference),
                    "Test stack fill with large stack using compressed add_col() method",
                    "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

        // Insert column into 10 x 10 matrix using small matrix stack and the
        // compressed column add_col() method
        sparse = initial;
        sparse.stack_init(100,2);
        for (int j = 0; j < 10; ++j) {
            int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
            if (col > 9) col -= 10;          // This avoids overflow
            int inx   = 0;
            int i_min = (j < 3) ?  0 : j-3;
            int i_max = (j > 8) ? 10 : j+2;
            for (int i = i_min; i < i_max; ++i) {
                wrk_data[inx] = (i+1)*3.7;
                wrk_row[inx]  = i;
                inx++;
            }
            sparse.add_col(wrk_data, wrk_row, inx, col);
        }
        sparse.stack_destroy();
        test_assert((sparse == reference),
                    "Test stack fill with small stack using compressed add_col() method",
                    "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

        // Insert column into 10 x 10 matrix using tiny matrix stack and the
        // compressed column add_col() method
        sparse = initial;
        sparse.stack_init(3,2);
        for (int j = 0; j < 10; ++j) {
            int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
            if (col > 9) col -= 10;          // This avoids overflow
            int inx   = 0;
            int i_min = (j < 3) ?  0 : j-3;
            int i_max = (j > 8) ? 10 : j+2;
            for (int i = i_min; i < i_max; ++i) {
                wrk_data[inx] = (i+1)*3.7;
                wrk_row[inx]  = i;
                inx++;
            }
            sparse.add_col(wrk_data, wrk_row, inx, col);
        }
        sparse.stack_destroy();
        test_assert((sparse == reference),
                    "Test stack fill with tiny stack using compressed add_col() method",
                    "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

        // Insert column into 10 x 10 matrix using no matrix stack and the
        // compressed column add_col() method
        sparse = initial;
        for (int j = 0; j < 10; ++j) {
            int col = int(0.8 * j + 0.5);    // This allows that some columns are twice
            if (col > 9) col -= 10;          // This avoids overflow
            int inx   = 0;
            int i_min = (j < 3) ?  0 : j-3;
            int i_max = (j > 8) ? 10 : j+2;
            for (int i = i_min; i < i_max; ++i) {
                wrk_data[inx] = (i+1)*3.7;
                wrk_row[inx]  = i;
                inx++;
            }
            sparse.add_col(wrk_data, wrk_row, inx, col);
        }
        sparse.stack_destroy();
        test_assert((sparse == reference),
                    "Test fill using compressed add_col() method",
                    "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print());

        // Signal success
        test_try_success();
    }
    catch (std::exception &e) {
        test_try_failure(e);
    }

	// Free workspace
	delete [] wrk_data;
	delete [] wrk_row;

    // Return
    return;
}
Пример #12
0
void GSparseMatrix::singularValueDecompositionHelper(GSparseMatrix** ppU, double** ppDiag, GSparseMatrix** ppV, int maxIters)
{
	int m = rows();
	int n = cols();
	if(m < n)
		ThrowError("Expected at least as many rows as columns");
	int i, j, k;
	int l = 0;
	int q, iter;
	double c, f, h, s, x, y, z;
	double norm = 0.0;
	double g = 0.0;
	double scale = 0.0;
	GSparseMatrix* pU = new GSparseMatrix(m, m);
	Holder<GSparseMatrix> hU(pU);
	pU->copyFrom(this);
	double* pSigma = new double[n];
	ArrayHolder<double> hSigma(pSigma);
	GSparseMatrix* pV = new GSparseMatrix(n, n);
	Holder<GSparseMatrix> hV(pV);
	GTEMPBUF(double, temp, n + m);
	double* temp2 = temp + n;

	// Householder reduction to bidiagonal form
	for(int i = 0; i < n; i++)
	{
		// Left-hand reduction
		temp[i] = scale * g;
		l = i + 1;
		g = 0.0;
		s = 0.0;
		scale = 0.0;
		if(i < m)
		{
			Iter kend = pU->colEnd(i);
			for(Iter kk = pU->colBegin(i); kk != kend; kk++)
			{
				if(kk->first >= (unsigned int)i)
					scale += ABS(kk->second);
			}
			if(scale != 0.0)
			{
				for(Iter kk = pU->colBegin(i); kk != kend; kk++)
				{
					if(kk->first >= (unsigned int)i)
					{
						double t = kk->second / scale;
						pU->set(kk->first, i, t);
						s += (t * t);
					}
				}
				f = pU->get(i, i);
				g = -GSparseMatrix_takeSign(sqrt(s), f);
				h = f * g - s;
				pU->set(i, i, f - g);
				if(i != n - 1)
				{
					for(j = l; j < n; j++)
					{
						s = 0.0;
						for(Iter kk = pU->colBegin(i); kk != kend; kk++)
						{
							if(kk->first >= (unsigned int)i)
								s += kk->second * pU->get(kk->first, j);
						}
						f = s / h;
						for(Iter kk = pU->colBegin(i); kk != kend; kk++)
						{
							if(kk->first >= (unsigned int)i)
								pU->set(kk->first, j, pU->get(kk->first, j) + f * kk->second);
						}
					}
				}
				for(Iter kk = pU->colBegin(i); kk != kend; kk++)
				{
					if(kk->first >= (unsigned int)i)
						pU->set(kk->first, i, pU->get(kk->first, i) * scale);
				}
			}
		}
		pSigma[i] = scale * g;

		// Right-hand reduction
		g = 0.0;
		s = 0.0;
		scale = 0.0;
		if(i < m && i != n - 1) 
		{
			Iter kend = pU->rowEnd(i);
			for(Iter kk = pU->rowBegin(i); kk != kend; kk++)
			{
				if(kk->first >= (unsigned int)n)
					break;
				if(kk->first >= (unsigned int)l)
					scale += ABS(kk->second);
			}
			if(scale != 0.0) 
			{
				for(Iter kk = pU->rowBegin(i); kk != kend; kk++)
				{
					if(kk->first >= (unsigned int)n)
						break;
					if(kk->first >= (unsigned int)l)
					{
						double t = kk->second / scale;
						pU->set(i, kk->first, t);
						s += (t * t);
					}
				}
				f = pU->get(i, l);
				g = -GSparseMatrix_takeSign(sqrt(s), f);
				h = f * g - s;
				pU->set(i, l, f - g);
				for(k = l; k < n; k++)
					temp[k] = pU->get(i, k) / h;
				if(i != m - 1) 
				{
					for(j = l; j < m; j++) 
					{
						s = 0.0;
						for(Iter kk = pU->rowBegin(i); kk != kend; kk++)
						{
							if(kk->first >= (unsigned int)n)
								break;
							if(kk->first >= (unsigned int)l)
								s += pU->get(j, kk->first) * kk->second;
						}
						Iter kend2 = pU->rowEnd(j);
						for(Iter kk = pU->rowBegin(j); kk != kend2; kk++)
						{
							if(kk->first >= (unsigned int)n)
								break;
							if(kk->first >= (unsigned int)l)
								pU->set(j, kk->first, pU->get(j, kk->first) + s * temp[kk->first]);
						}
					}
				}
				for(Iter kk = pU->rowBegin(i); kk != kend; kk++)
				{
					if(kk->first >= (unsigned int)n)
						break;
					if(kk->first >= (unsigned int)l)
						pU->set(i, kk->first, kk->second * scale);
				}
			}
		}
		norm = MAX(norm, ABS(pSigma[i]) + ABS(temp[i]));
	}

	// Accumulate right-hand transform
	for(int i = n - 1; i >= 0; i--)
	{
		if(i < n - 1)
		{
			if(g != 0.0)
			{
				Iter jend = pU->rowEnd(i);
				for(Iter jj = pU->rowBegin(i); jj != jend; jj++)
				{
					if(jj->first >= (unsigned int)n)
						break;
					if(jj->first >= (unsigned int)l)
						pV->set(i, jj->first, (jj->second / pU->get(i, l)) / g); // (double-division to avoid underflow)
				}
				for(j = l; j < n; j++)
				{
					s = 0.0;
					Iter kend = pU->rowEnd(i);
					for(Iter kk = pU->rowBegin(i); kk != kend; kk++)
					{
						if(kk->first >= (unsigned int)n)
							break;
						if(kk->first >= (unsigned int)l)
							s += kk->second * pV->get(j, kk->first);
					}
					kend = pV->rowEnd(i);
					for(Iter kk = pV->rowBegin(i); kk != kend; kk++)
					{
						if(kk->first >= (unsigned int)n)
							break;
						if(kk->first >= (unsigned int)l)
							pV->set(j, kk->first, pV->get(j, kk->first) + s * kk->second);
					}
				}
			}
			for(j = l; j < n; j++)
			{
				pV->set(i, j, 0.0);
				pV->set(j, i, 0.0);
			}
		}
		pV->set(i, i, 1.0);
		g = temp[i];
		l = i;
	}

	// Accumulate left-hand transform
	for(i = n - 1; i >= 0; i--)
	{
		l = i + 1;
		g = pSigma[i];
		if(i < n - 1)
		{
			for(j = l; j < n; j++)
				pU->set(i, j, 0.0);
		}
		if(g != 0.0)
		{
			g = 1.0 / g;
			if(i != n - 1)
			{
				for(j = l; j < n; j++)
				{
					s = 0.0;
					Iter kend = pU->colEnd(i);
					for(Iter kk = pU->colBegin(i); kk != kend; kk++)
					{
						if(kk->first >= (unsigned int)l)
							s += kk->second * pU->get(kk->first, j);
					}
					f = (s / pU->get(i, i)) * g;
					if(f != 0.0)
					{
						for(Iter kk = pU->colBegin(i); kk != kend; kk++)
						{
							if(kk->first >= (unsigned int)i)
								pU->set(kk->first, j, pU->get(kk->first, j) + f * kk->second);
						}
					}
				}
			}
			for(j = i; j < m; j++)
				pU->set(j, i, pU->get(j, i) * g);
		} 
		else 
		{
			for(j = i; j < m; j++)
				pU->set(j, i, 0.0);
		}
		pU->set(i, i, pU->get(i, i) + 1.0);
	}

	// Diagonalize the bidiagonal matrix
	for(k = n - 1; k >= 0; k--) // For each singular value
	{
		for(iter = 1; iter <= maxIters; iter++)
		{
			// Test for splitting
			bool flag = true;
			for(l = k; l >= 0; l--)
			{
				q = l - 1;
				if(ABS(temp[l]) + norm == norm)
				{
					flag = false;
					break;
				}
				if(ABS(pSigma[q]) + norm == norm)
					break;
			}

			if(flag)
			{
				c = 0.0;
				s = 1.0;
				for(i = l; i <= k; i++)
				{
					f = s * temp[i];
					temp[i] *= c;
					if(ABS(f) + norm == norm)
						break;
					g = pSigma[i];
					h = GSparseMatrix_pythag(f, g);
					pSigma[i] = h;
					h = 1.0 / h;
					c = g * h;
					s = -f * h;
					Iter jendi = pU->colEnd(i);
					Iter jendq = pU->colEnd(q);
					Iter jji = pU->colBegin(i);
					Iter jjq = pU->colBegin(q);
					int tpos;
					for(tpos = 0; jji != jendi || jjq != jendq; tpos++)
					{
						if(jjq == jendq || (jji != jendi && jji->first < jjq->first))
						{
							temp2[tpos] = jji->first;
							jji++;
						}
						else
						{
							temp2[tpos] = jjq->first;
							if(jji != jendi && jjq->first == jji->first)
								jji++;
							jjq++;
						}
					}
					for(int tpos2 = 0; tpos2 < tpos; tpos2++)
					{
						y = pU->get((unsigned int)temp2[tpos2], q);
						z = pU->get((unsigned int)temp2[tpos2], i);
						pU->set((unsigned int)temp2[tpos2], q, y * c + z * s);
						pU->set((unsigned int)temp2[tpos2], i, z * c - y * s);
					}
				}
			}

			z = pSigma[k];
			if(l == k)
			{
				// Detect convergence
				if(z < 0.0)
				{
					// Singular value should be positive
					pSigma[k] = -z;
					for(j = 0; j < n; j++)
						pV->set(k, j, pV->get(k, j) * -1.0);
				}
				break;
			}
			if(iter >= maxIters)
				ThrowError("failed to converge");

			// Shift from bottom 2x2 minor
			x = pSigma[l];
			q = k - 1;
			y = pSigma[q];
			g = temp[q];
			h = temp[k];
			f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
			g = GSparseMatrix_pythag(f, 1.0);
			f = ((x - z) * (x + z) + h * ((y / (f + GSparseMatrix_takeSign(g, f))) - h)) / x;

			// QR transform
			c = 1.0;
			s = 1.0;
			for(j = l; j <= q; j++)
			{
				i = j + 1;
				g = temp[i];
				y = pSigma[i];
				h = s * g;
				g = c * g;
				z = GSparseMatrix_pythag(f, h);
				temp[j] = z;
				c = f / z;
				s = h / z;
				f = x * c + g * s;
				g = g * c - x * s;
				h = y * s;
				y = y * c;
				Iter pendi = pV->rowEnd(i);
				Iter pendj = pV->rowEnd(j);
				Iter ppi = pV->rowBegin(i);
				Iter ppj = pV->rowBegin(j);
				int tpos;
				for(tpos = 0; ppi != pendi || ppj != pendj; tpos++)
				{
					if(ppj == pendj || (ppi != pendi && ppi->first < ppj->first))
					{
						temp2[tpos] = ppi->first;
						ppi++;
					}
					else
					{
						temp2[tpos] = ppj->first;
						if(ppi != pendi && ppj->first == ppi->first)
							ppi++;
						ppj++;
					}
				}
				for(int tpos2 = 0; tpos2 < tpos; tpos2++)
				{
					x = pV->get(j, (unsigned int)temp2[tpos2]);
					z = pV->get(i, (unsigned int)temp2[tpos2]);
					pV->set(j, (unsigned int)temp2[tpos2], x * c + z * s);
					pV->set(i, (unsigned int)temp2[tpos2], z * c - x * s);
				}
				z = GSparseMatrix_pythag(f, h);
				pSigma[j] = z;
				if(z != 0.0)
				{
					z = 1.0 / z;
					c = f * z;
					s = h * z;
				}
				f = c * g + s * y;
				x = c * y - s * g;
				pendi = pU->colEnd(i);
				pendj = pU->colEnd(j);
				ppi = pU->colBegin(i);
				ppj = pU->colBegin(j);
				for(tpos = 0; ppi != pendi || ppj != pendj; tpos++)
				{
					if(ppj == pendj || (ppi != pendi && ppi->first < ppj->first))
					{
						temp2[tpos] = ppi->first;
						ppi++;
					}
					else
					{
						temp2[tpos] = ppj->first;
						if(ppi != pendi && ppj->first == ppi->first)
							ppi++;
						ppj++;
					}
				}
				for(int tpos2 = 0; tpos2 < tpos; tpos2++)
				{
					y = pU->get((unsigned int)temp2[tpos2], j);
					z = pU->get((unsigned int)temp2[tpos2], i);
					pU->set((unsigned int)temp2[tpos2], j, y * c + z * s);
					pU->set((unsigned int)temp2[tpos2], i, z * c - y * s);
				}
			}
			temp[l] = 0.0;
			temp[k] = f;
			pSigma[k] = x;
		}
	}

	// Sort the singular values from largest to smallest
	for(i = 1; i < n; i++)
	{
		for(j = i; j > 0; j--)
		{
			if(pSigma[j - 1] >= pSigma[j])
				break;
			pU->swapColumns(j - 1, j);
			pV->swapRows(j - 1, j);
			std::swap(pSigma[j - 1], pSigma[j]);
		}
	}

	// Return results
	*ppU = hU.release();
	*ppDiag = hSigma.release();
	*ppV = hV.release();
}