CNTK/Tests/UnitTests/MathTests/MatrixSparseDenseInteractio...

321 строка
14 KiB
C++

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
//
// Tests for sparse and dense matrix interaction should go here
//
#include "stdafx.h"
#include <random>
#include "../../../Source/Math/Matrix.h"
using namespace Microsoft::MSR::CNTK;
namespace Microsoft { namespace MSR { namespace CNTK { namespace Test {
// original dimensions: 4096, 2048, 128
// some test fail with large dimensions (e.g. MatrixSparseElementWisePower)
// reducing to:
const size_t dim1 = 256;
const size_t dim2 = 2048;
const size_t dim3 = 128;
BOOST_AUTO_TEST_SUITE(GPUMatrixSuite)
BOOST_FIXTURE_TEST_CASE(MatrixChangeModesBetweenDenseAndSparse, RandomSeedFixture)
{
// This test should fail if you don't have CUDA GPU
Matrix<float> mA(c_deviceIdZero);
mA.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
float n0 = mA.MatrixNorm0();
BOOST_CHECK_EQUAL(MatrixType::DENSE, mA.GetMatrixType());
mA.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
BOOST_CHECK_EQUAL(MatrixType::SPARSE, mA.GetMatrixType());
float n1 = mA.MatrixNorm0();
BOOST_CHECK_EQUAL(n0, n1);
mA.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, false);
BOOST_CHECK_EQUAL(MatrixType::DENSE, mA.GetMatrixType());
}
BOOST_FIXTURE_TEST_CASE(MatrixSparseTimesDense, RandomSeedFixture)
{
// DENSE
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -300.0f, 0.1f, IncrementCounter()), 0);
// MATRIX mAsparse becomes sparse
Matrix<float> mAsparse(mAdense.DeepClone());
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
// DENSE
Matrix<float> mB = Matrix<float>::RandomGaussian(dim2, dim3, c_deviceIdZero, 1.0f, 4.0f, IncrementCounter());
Matrix<float> mC = Matrix<float>::RandomGaussian(dim1, dim3, c_deviceIdZero, 1.0f, 2.0f, IncrementCounter());
Matrix<float> mD(mC.DeepClone());
float alpha = 0.3f;
float beta = 2.0f;
bool transposeA = false, transposeB = false;
// DENSE * DENSE
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mB, transposeB, beta, mC);
// SPARSE * DENSE
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAsparse, transposeA, mB, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
// SPARSE * DENSE -> SPARSE
beta = 1; // note that dense only allow beta == 1, while sparse CPU support arbitrary beta
transposeA = false;
transposeB = true; // only support !transposeA && tranposeB for Dense * CSC -> SBC
Matrix<float> mA1sparseCSC(mAdense.DeepClone());
mA1sparseCSC.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSC, true);
Matrix<float> mA2dense(c_deviceIdZero);
mA2dense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -300.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mA2sparseCSC(mA2dense.DeepClone());
mA2sparseCSC.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSC, true);
// dense for comparison
mC.Resize(dim1, dim1);
mC.SetValue(0.0f);
Matrix<float>::MultiplyAndAdd(mAdense, transposeA, mAdense, transposeB, mC);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mA2dense, transposeB, beta, mC);
// make sure (dense * sparse -> dense) == (dense * dense -> dense)
mD.Resize(dim1, dim1);
mD.SetValue(0.0f);
Matrix<float>::MultiplyAndAdd(mAdense, transposeA, mA1sparseCSC, transposeB, mD);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mA2sparseCSC, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
// test on sparse
mD.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseBlockCol, false);
Matrix<float>::MultiplyAndAdd(mAdense, transposeA, mA1sparseCSC, transposeB, mD);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mA2sparseCSC, transposeB, beta, mD);
// copy mD to dense and compare
Matrix<float> mE = Matrix<float>::Zeros(dim1, dim1, c_deviceIdZero);
Matrix<float>::ScaleAndAdd(1, mD, mE);
BOOST_CHECK(mE.IsEqualTo(mC, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(MatrixDenseTimesSparse, RandomSeedFixture)
{
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSC, true);
Matrix<float> mB = Matrix<float>::RandomGaussian(dim2, dim1, c_deviceIdZero, 1.0f, 4.0f, IncrementCounter());
Matrix<float> mC = Matrix<float>::RandomGaussian(dim2, dim2, c_deviceIdZero, 1.0f, 2.0f, IncrementCounter());
Matrix<float> mD(mC.DeepClone());
bool transposeA = false, transposeB = false;
float alpha = 0.3f;
float beta = 0.0f;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAdense, transposeB, beta, mC);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
alpha = 3.3f;
beta = 1.3f;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAdense, transposeB, beta, mC);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(CPUMatrixDenseTimesSparse, RandomSeedFixture)
{
Matrix<float> mAdense(CPUDEVICE);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSC, true);
Matrix<float> mB = Matrix<float>::RandomGaussian(dim2, dim1, CPUDEVICE, 1, 4, IncrementCounter());
Matrix<float> mC = Matrix<float>::RandomGaussian(dim2, dim2, CPUDEVICE, 1, 2, IncrementCounter());
Matrix<float> mD(mC.DeepClone());
bool transposeA = false, transposeB = false;
float alpha = 0.3f;
float beta = 0.0f;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAdense, transposeB, beta, mC);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
alpha = 3.3f;
beta = 1.3f;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAdense, transposeB, beta, mC);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mD);
BOOST_CHECK(mD.IsEqualTo(mC, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(CPUMatrixDenseTimesSparseAsSparse, RandomSeedFixture)
{
#if 0
// TODO commented temporarily since 'IsEqualTo' is not yet implemented
Matrix<float> mAdense(CPUDEVICE);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense);
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSC, true);
Matrix<float> mB = Matrix<float>::RandomGaussian(dim1, dim2, 1, 4, IncrementCounter(), CPUDEVICE);
Matrix<float> mC = Matrix<float>::RandomGaussian(dim1, dim1, 1, 2, IncrementCounter(), CPUDEVICE);
Matrix<float> mDblock(CPUDEVICE);
mDblock.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseBlockCol, true);
float alpha = 0.3f;
float beta = 1.2f;
bool transposeA = false, transposeB = true;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mDblock);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mB, transposeA, mAsparse, transposeB, beta, mC);
// TODO IsEqualTo not yet implemented for sparse block matrix
// switch type from sparse block matrix is also not yet implemented
// --> (mDblock.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, true);)
BOOST_CHECK(mDblock.IsEqualTo(mC, c_epsilonFloatE4));
#endif
}
BOOST_FIXTURE_TEST_CASE(MatrixSparseTimesSparse, RandomSeedFixture)
{
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float> mBdense(c_deviceIdZero);
mBdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim2, dim1, c_deviceIdZero, -5.0f, 0.4f, IncrementCounter()), 0);
Matrix<float> mBsparse(mBdense.DeepClone());
mBsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float> mCdense(c_deviceIdZero);
mCdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim1, c_deviceIdZero, -4.0f, 0.2f, IncrementCounter()), 0);
Matrix<float> mCsparse(mCdense.DeepClone());
mCsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
bool transposeA = false, transposeB = false;
float alpha = 2.4f;
float beta = 0.0f;
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mBdense, transposeB, beta, mCdense);
Matrix<float>::MultiplyAndWeightedAdd(alpha, mAsparse, transposeA, mBsparse, transposeB, beta, mCsparse);
mCsparse.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, true);
BOOST_CHECK(mCsparse.IsEqualTo(mCdense, c_epsilonFloatE4));
// TODO: as soon as beta != 0.0 the 'MultiplyAndWeightedAdd' fails with stack overflow (also in the first test 5 lines above)
// alpha = 2.4f;
// beta = 3.4f;
// mCsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
// Matrix<float>::MultiplyAndWeightedAdd(alpha, mAdense, transposeA, mBdense, transposeB, beta, mCdense);
// Matrix<float>::MultiplyAndWeightedAdd(alpha, mAsparse, transposeA, mBsparse, transposeB, beta, mCsparse);
// mCsparse.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, true);
// BOOST_CHECK(mCsparse.IsEqualTo(mCdense, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(MatrixSparsePlusSparse, RandomSeedFixture)
{
std::mt19937 rng(0);
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
Matrix<float> mBdense(c_deviceIdZero);
mBdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -5.0f, 0.4f, IncrementCounter()), 0);
Matrix<float> mBsparse(mBdense.DeepClone());
float alpha = 1.0f * rng() / rng.max();
Matrix<float>::ScaleAndAdd(alpha, mAdense, mBdense);
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
mBsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float>::ScaleAndAdd(alpha, mAsparse, mBsparse);
mBsparse.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, true);
BOOST_CHECK(mBsparse.IsEqualTo(mBdense, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(MatrixDensePlusSparse, RandomSeedFixture)
{
std::mt19937 rng(0);
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mBdense(c_deviceIdZero);
mBdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -5.0f, 0.4f, IncrementCounter()), 0);
Matrix<float> mBsparse(mBdense.DeepClone());
float alpha = 1.0f * rng() / rng.max();
Matrix<float>::ScaleAndAdd(alpha, mAdense, mBdense);
mBsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float>::ScaleAndAdd(alpha, mAdense, mBsparse);
mBsparse.SwitchToMatrixType(MatrixType::DENSE, matrixFormatDense, true);
BOOST_CHECK(mBsparse.IsEqualTo(mBdense, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(MatrixSparsePlusDense, RandomSeedFixture)
{
std::mt19937 rng(0);
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
Matrix<float> mBdense(c_deviceIdZero);
mBdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -5.0f, 0.4f, IncrementCounter()), 0);
Matrix<float> Bd1(mBdense.DeepClone());
float alpha = 1.0f * rng() / rng.max();
Matrix<float>::ScaleAndAdd(alpha, mAdense, mBdense);
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float>::ScaleAndAdd(alpha, mAsparse, Bd1);
BOOST_CHECK(Bd1.IsEqualTo(mBdense, c_epsilonFloatE4));
}
BOOST_FIXTURE_TEST_CASE(MatrixSparseElementWisePower, RandomSeedFixture)
{
Matrix<float> mAdense(c_deviceIdZero);
mAdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -3.0f, 0.1f, IncrementCounter()), 0);
Matrix<float> mAsparse(mAdense.DeepClone());
mAsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
Matrix<float> mBdense(c_deviceIdZero);
mBdense.AssignTruncateBottomOf(Matrix<float>::RandomUniform(dim1, dim2, c_deviceIdZero, -5.0f, 0.4f, IncrementCounter()), 0);
Matrix<float> mBsparse(mBdense.DeepClone());
mBsparse.SwitchToMatrixType(MatrixType::SPARSE, matrixFormatSparseCSR, true);
mAdense ^= 2.3f;
mAsparse ^= 2.3f;
// TODO randomly fails ....
// BOOST_CHECK(mAsparse.IsEqualTo(mAdense, c_epsilonFloatE3));
// BOOST_CHECK(mAdense.IsEqualTo(mAsparse, c_epsilonFloatE3));
// mBdense.AssignElementPowerOf(mAdense, 3.2f);
// mBsparse.AssignElementPowerOf(mAsparse, 3.2f);
// BOOST_CHECK(mBsparse.IsEqualTo(mBdense, c_epsilonFloatE3));
// BOOST_CHECK(mBdense.IsEqualTo(mBsparse, c_epsilonFloatE3));
BOOST_CHECK(1);
}
BOOST_AUTO_TEST_SUITE_END()
}
} } }