Skip to content

Commit 9cf4afa

Browse files
committed
Merge pull request #1132 from allywarner/ComputeSVDBranch
Closes #1134
2 parents 040e492 + a7fda38 commit 9cf4afa

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

src/Core/Algorithms/Math/Tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ SET(Algorithms_Math_Tests_SRCS
4040
SelectSubMatrixTests.cc
4141
GetMatrixSliceAlgoTests.cc
4242
ComputePCAtest.cc
43+
ComputeSVDtest.cc
4344
)
4445

4546
#SET(Engine_Network_Tests_HEADERS
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
For more information, please see: http://software.sci.utah.edu
3+
4+
The MIT License
5+
6+
Copyright (c) 2015 Scientific Computing and Imaging Institute,
7+
University of Utah.
8+
9+
License for the specific language governing rights and limitations under
10+
Permission is hereby granted, free of charge, to any person obtaining a
11+
copy of this software and associated documentation files (the "Software"),
12+
to deal in the Software without restriction, including without limitation
13+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
14+
and/or sell copies of the Software, and to permit persons to whom the
15+
Software is furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included
18+
in all copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26+
DEALINGS IN THE SOFTWARE.
27+
*/
28+
29+
//ComputeSVD algorithm test.
30+
#include <gtest/gtest.h>
31+
#include <Core/Datatypes/DenseMatrix.h>
32+
#include <Core/Datatypes/MatrixComparison.h>
33+
#include <Testing/Utils/MatrixTestUtilities.h>
34+
#include <Core/Algorithms/Math/ComputeSVD.h>
35+
#include <Eigen/SVD>
36+
37+
using namespace SCIRun::Core::Datatypes;
38+
using namespace SCIRun::Core::Algorithms::Math;
39+
using namespace SCIRun::TestUtils;
40+
41+
namespace
42+
{
43+
//Matrix for input.
44+
DenseMatrixHandle inputMatrix()
45+
{
46+
//Hard coded for testing.
47+
int column1 [12] = {5,4,7,8,10,10,4,6,8,7,9,6};
48+
int column2 [12] = {7,6,9,8,5,7,9,3,4,5,5,9};
49+
50+
//Puts the columns into a matrix.
51+
DenseMatrixHandle inputM(boost::make_shared<DenseMatrix>(12,2));
52+
for (int i = 0; i < inputM->rows(); i++){
53+
(*inputM)(i,0) = column1[i];
54+
(*inputM)(i,1) = column2[i];
55+
}
56+
return inputM;
57+
}
58+
}
59+
60+
//Checks if the outputs are correct.
61+
//U,S,V: U: Left singular matrix, S: Singular values, V: Right singular matrix.
62+
TEST(ComputeSVDtest, checkOutputs)
63+
{
64+
ComputeSVDAlgo algo;
65+
66+
DenseMatrixHandle m1(inputMatrix());
67+
DenseMatrixHandle LeftSingularMatrix_U;
68+
DenseMatrixHandle SingularValues_S;
69+
DenseMatrixHandle RightSingularMatrix_V;
70+
71+
//Runs algorithm.
72+
algo.run(m1,LeftSingularMatrix_U,SingularValues_S,RightSingularMatrix_V);
73+
74+
//Testing if the results are null.
75+
ASSERT_NE(nullptr,LeftSingularMatrix_U);
76+
ASSERT_NE(nullptr,SingularValues_S);
77+
ASSERT_NE(nullptr,RightSingularMatrix_V);
78+
79+
//Check the dimensions of the matrices that were created for output.
80+
81+
//Rows
82+
ASSERT_EQ(12,LeftSingularMatrix_U->rows());
83+
ASSERT_EQ(2,SingularValues_S->rows());
84+
ASSERT_EQ(2,RightSingularMatrix_V->rows());
85+
86+
//Columns
87+
ASSERT_EQ(12,LeftSingularMatrix_U->cols());
88+
ASSERT_EQ(1,SingularValues_S->cols());
89+
ASSERT_EQ(2,RightSingularMatrix_V->cols());
90+
91+
//Eigen does not create a diagonal matrix when it computes SVD, it just has a column with the singular values, so we must put it into a diagonal matrix to be able to do some matrix multiplication later.
92+
DenseMatrix sDiag = Eigen::MatrixXd::Constant(12,2,0);
93+
sDiag.diagonal() = SingularValues_S->col(0);
94+
95+
//Multiplying back together and comparing to the centered matrix. They should be equal to each other with some tolerance.
96+
DenseMatrix product = (*LeftSingularMatrix_U) * sDiag * (*RightSingularMatrix_V).transpose();
97+
98+
auto expected = *inputMatrix();
99+
100+
//Comparing each element in the matrices.
101+
for (int i = 0; i < product.rows(); ++i) {
102+
for (int j = 0; j < product.cols(); ++j)
103+
ASSERT_NEAR(expected(i,j), product(i,j), 1e-5);
104+
}
105+
106+
}
107+
108+
//Tests for input with a dimension of zero.
109+
TEST(ComputeSVDtest, ThrowsForZeroDimensionInput)
110+
{
111+
ComputeSVDAlgo algo;
112+
113+
DenseMatrixHandle m1(new DenseMatrix(5, 0));
114+
DenseMatrixHandle m2(new DenseMatrix(0, 5));
115+
DenseMatrixHandle m3(new DenseMatrix(0, 0));
116+
117+
DenseMatrixHandle LeftSingularMatrix_U;
118+
DenseMatrixHandle SingularValues_S;
119+
DenseMatrixHandle RightSingularMatrix_V;
120+
121+
//Runs algorithm and expects an error.
122+
EXPECT_ANY_THROW(algo.run(m1,LeftSingularMatrix_U,SingularValues_S,RightSingularMatrix_V));
123+
EXPECT_ANY_THROW(algo.run(m2,LeftSingularMatrix_U,SingularValues_S,RightSingularMatrix_V));
124+
EXPECT_ANY_THROW(algo.run(m3,LeftSingularMatrix_U,SingularValues_S,RightSingularMatrix_V));
125+
126+
}

src/Modules/Math/Tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ SET(Modules_Math_Tests_SRCS
3939
AddKnownsToLinearSystemTests.cc
4040
PortCachingUnitTest.cc
4141
ComputePCAtest.cc
42+
ComputeSVDtest.cc
4243
)
4344

4445
#SET(Engine_Network_Tests_HEADERS
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
For more information, please see: http://software.sci.utah.edu
3+
4+
The MIT License
5+
6+
Copyright (c) 2015 Scientific Computing and Imaging Institute,
7+
University of Utah.
8+
9+
License for the specific language governing rights and limitations under
10+
Permission is hereby granted, free of charge, to any person obtaining a
11+
copy of this software and associated documentation files (the "Software"),
12+
to deal in the Software without restriction, including without limitation
13+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
14+
and/or sell copies of the Software, and to permit persons to whom the
15+
Software is furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included
18+
in all copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26+
DEALINGS IN THE SOFTWARE.
27+
*/
28+
29+
//ComputeSVD module test.
30+
#include <Testing/ModuleTestBase/ModuleTestBase.h>
31+
#include <Modules/Legacy/Math/ComputeSVD.h>
32+
#include <Core/Datatypes/DenseColumnMatrix.h>
33+
#include <Core/Datatypes/DenseMatrix.h>
34+
#include <Testing/Utils/MatrixTestUtilities.h>
35+
36+
using namespace SCIRun;
37+
using namespace SCIRun::Modules::Math;
38+
using namespace SCIRun::Dataflow::Networks;
39+
using namespace SCIRun::Core::Datatypes;
40+
using namespace SCIRun::Testing;
41+
using namespace SCIRun::TestUtils;
42+
43+
class ComputeSVDtest : public ModuleTest
44+
{
45+
};
46+
47+
//Checks for null input.
48+
TEST_F(ComputeSVDtest, CheckInputNull)
49+
{
50+
auto svdMod = makeModule("ComputeSVD");
51+
MatrixHandle nullMatrix;
52+
stubPortNWithThisData(svdMod, 0, nullMatrix);
53+
54+
EXPECT_THROW(svdMod -> execute(), NullHandleOnPortException);
55+
}
56+
57+
//Checks for dense input.
58+
TEST_F(ComputeSVDtest, CheckInputDense)
59+
{
60+
auto svdMod = makeModule("ComputeSVD");
61+
MatrixHandle denseMatrix = MAKE_DENSE_MATRIX_HANDLE((1,2,5,6)(3,4,7,9)(7,8,9,1)(2,3,5,9));
62+
stubPortNWithThisData(svdMod, 0, denseMatrix);
63+
64+
EXPECT_NO_THROW(svdMod -> execute());
65+
}
66+
67+
//ComputePCA currently only supports dense input, but we will leave these if we want to change it later.
68+
//There is a check for this in the algorithm.
69+
//Checks for column input.
70+
TEST_F(ComputeSVDtest, CheckInputColumn)
71+
{
72+
auto svdMod = makeModule("ComputeSVD");
73+
DenseColumnMatrixHandle columnInput(new DenseColumnMatrix(5));
74+
(*columnInput) << 1,2,3,4,5;
75+
stubPortNWithThisData(svdMod, 0, columnInput);
76+
77+
EXPECT_NO_THROW(svdMod -> execute());
78+
}
79+
80+
//Checks for sparse input.
81+
TEST_F(ComputeSVDtest, CheckInputSparse)
82+
{
83+
auto svdMod = makeModule("ComputeSVD");
84+
MatrixHandle sparseMatrix = MAKE_SPARSE_MATRIX_HANDLE((0,0,0,0)(5,8,0,0)(0,0,3,0)(0,6,0,0));
85+
stubPortNWithThisData(svdMod, 0, sparseMatrix);
86+
87+
EXPECT_NO_THROW(svdMod -> execute());
88+
}
89+

0 commit comments

Comments
 (0)