Skip to content

Commit bd870b3

Browse files
author
allywarner
committed
Test Network, Test Changes, Algorithm Updates
1 parent 989663e commit bd870b3

File tree

4 files changed

+181
-75
lines changed

4 files changed

+181
-75
lines changed

src/Core/Algorithms/Math/ComputePCA.cc

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,67 +21,86 @@
2121
DEALINGS IN THE SOFTWARE.
2222
*/
2323

24+
25+
//ComputePCA Algorithm: Computes Principal Component Analysis.
26+
2427
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
2528
#include <Core/Algorithms/Math/ComputePCA.h>
2629
#include <Core/Datatypes/DenseMatrix.h>
2730
#include <Core/Datatypes/DenseColumnMatrix.h>
2831
#include <Core/Datatypes/MatrixTypeConversions.h>
2932
#include <Eigen/SVD>
30-
3133
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
3234

3335
using namespace SCIRun;
3436
using namespace SCIRun::Core::Algorithms;
3537
using namespace SCIRun::Core::Datatypes;
3638
using namespace SCIRun::Core::Algorithms::Math;
3739

38-
void ComputePCAAlgo::run(MatrixHandle input, DenseMatrixHandle& LeftPrinMat, DenseMatrixHandle& PrinVals, DenseMatrixHandle& RightPrinMat) const
39-
{
40+
//Let's do some math.
41+
//Algorithm:
42+
void ComputePCAAlgo::run(MatrixHandle input, DenseMatrixHandle& LeftPrinMat, DenseMatrixHandle& PrinVals, DenseMatrixHandle& RightPrinMat) const{
43+
44+
//Throws an error if one or both of the input matrix dimensions is zero.
45+
if (input->nrows() == 0 || input->ncols() == 0){
46+
47+
THROW_ALGORITHM_INPUT_ERROR("Input has a zero dimension.");
48+
}
49+
50+
//Input matrix: nxm
4051
if (matrix_is::dense(input))
4152
{
53+
//First, we have to center the data.
54+
auto denseInputCentered = centerData(input);
4255

43-
auto denseInput = matrix_cast::as_dense(input);
44-
45-
auto rows = denseInput->rows();
46-
47-
auto centerMatrix = Eigen::MatrixXd::Identity(rows,rows) - (1/rows)*Eigen::MatrixXd::Constant(rows,rows,1);
48-
49-
auto denseInputCentered = centerMatrix * *denseInput;
50-
56+
//After the data is centered, then we compute SVD on the centered matrix.
57+
//Centered Matrix = U*S*Vt, Vt = V transpose
5158
Eigen::JacobiSVD<DenseMatrix::EigenBase> svd_mat(denseInputCentered, Eigen::ComputeFullU | Eigen::ComputeFullV);
5259

60+
//U: Left principal matrix, nxn, orthogonal
5361
LeftPrinMat = boost::make_shared<DenseMatrix>(svd_mat.matrixU());
5462

63+
//S: Principal values nxm, diagonal
5564
PrinVals = boost::make_shared<DenseMatrix>(svd_mat.singularValues());
5665

66+
//V: Right singular mxm, orthognol
5767
RightPrinMat = boost::make_shared<DenseMatrix>(svd_mat.matrixV());
5868
}
5969
else
6070
{
71+
//Throw an error if the matrix is not dense.
72+
//Sparse matrices not supported at this time.
6173
THROW_ALGORITHM_INPUT_ERROR("ComputePCA works for dense matrix input only.");
6274
}
6375
}
6476

65-
DenseMatrix ComputePCAAlgo::centerData(DenseMatrixHandle input_matrix)
77+
//Centers input matrix.
78+
DenseMatrix ComputePCAAlgo::centerData(MatrixHandle input_matrix)
6679
{
80+
//Casts the matrix as dense.
6781
auto denseInput = matrix_cast::as_dense(input_matrix);
6882

83+
//Counts the number of rows in the input matrix.
6984
auto rows = denseInput->rows();
7085

86+
//Calulates the centering matrix (C).
87+
// C = Identity(nxn) - 1/n * matrix of ones(nxn)
7188
auto centerMatrix = Eigen::MatrixXd::Identity(rows,rows) - (1.0/rows)*Eigen::MatrixXd::Constant(rows,rows,1);
7289

90+
//Multiplying the input matrix by the centering matrix.
7391
auto denseInputCentered = centerMatrix * *denseInput;
74-
75-
return denseInputCentered.eval();
92+
93+
return denseInputCentered;
7694
}
7795

96+
//Run the algorithm.
7897
AlgorithmOutput ComputePCAAlgo::run_generic(const AlgorithmInput& input) const
7998
{
8099
auto input_matrix = input.get<Matrix>(Variables::InputMatrix);
81100

82101
DenseMatrixHandle LeftPrinMat;
83-
DenseMatrixHandle RightPrinMat;
84102
DenseMatrixHandle PrinVals;
103+
DenseMatrixHandle RightPrinMat;
85104

86105
run(input_matrix, LeftPrinMat, PrinVals, RightPrinMat);
87106

@@ -94,6 +113,7 @@ AlgorithmOutput ComputePCAAlgo::run_generic(const AlgorithmInput& input) const
94113
return output;
95114
}
96115

116+
//Outputs:
97117
AlgorithmOutputName ComputePCAAlgo::LeftPrincipalMatrix("LeftPrincipalMatrix");
98118
AlgorithmOutputName ComputePCAAlgo::PrincipalValues("PrincipalValues");
99119
AlgorithmOutputName ComputePCAAlgo::RightPrincipalMatrix("RightPrincipalMatrix");

src/Core/Algorithms/Math/ComputePCA.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
DEALINGS IN THE SOFTWARE.
2222
*/
2323

24+
//ComputePCA Algortithm Header
2425
#ifndef ComputePCA_ComputePCA_h_1
2526
#define ComputePCA_ComputePCA_h_1
2627

@@ -43,7 +44,7 @@ namespace SCIRun {
4344
static AlgorithmOutputName RightPrincipalMatrix;
4445
void run(Datatypes::MatrixHandle input_matrix, Datatypes::DenseMatrixHandle& LeftPrinMat, Datatypes::DenseMatrixHandle& PrinVals, Datatypes::DenseMatrixHandle& RightPrinMat) const;
4546
virtual AlgorithmOutput run_generic(const AlgorithmInput& input) const;
46-
static Datatypes::DenseMatrix centerData(Datatypes::DenseMatrixHandle input_matrix);
47+
static Datatypes::DenseMatrix centerData(Datatypes::MatrixHandle input_matrix);
4748
};
4849

4950
}}}}

src/Core/Algorithms/Math/Tests/ComputePCAtest.cc

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,26 @@
2727
*/
2828

2929
#include <gtest/gtest.h>
30-
3130
#include <Core/Datatypes/DenseMatrix.h>
3231
#include <Core/Datatypes/MatrixComparison.h>
3332
#include <Testing/Utils/MatrixTestUtilities.h>
3433
#include <Core/Algorithms/Math/ComputePCA.h>
34+
#include <Eigen/SVD>
3535

3636
using namespace SCIRun::Core::Datatypes;
3737
using namespace SCIRun::Core::Algorithms::Math;
3838
using namespace SCIRun::TestUtils;
3939

40-
4140
namespace
4241
{
43-
//Matrix for input
42+
//Matrix for input.
4443
DenseMatrixHandle inputMatrix()
4544
{
45+
//Hard coded for testing.
4646
int column1 [12] = {5,4,7,8,10,10,4,6,8,7,9,6};
4747
int column2 [12] = {7,6,9,8,5,7,9,3,4,5,5,9};
4848

49+
//Puts the columns into a matrix.
4950
DenseMatrixHandle inputM(boost::make_shared<DenseMatrix>(12,2));
5051
for (int i = 0; i < inputM->rows(); i++){
5152
(*inputM)(i,0) = column1[i];
@@ -54,34 +55,24 @@ namespace
5455
return inputM;
5556
}
5657

57-
//A after it is centered
58+
//The input matrix after centering.
5859
DenseMatrixHandle centeredInputMatrix()
5960
{
60-
61+
//Hard coded for testing.
6162
double centeredColumn1 [12] = {-2.0,-3.0,-0.0,1.0,3.0,3.0,-3.0,-1.0,1.0,-0.0,2.0,-1.0};
6263
double centeredColumn2 [12] = {0.583333,-0.416667,2.583333,1.583333,-1.416667,0.583333,2.583333,-3.416667,-2.416667,-1.416667,-1.416667,2.583333};
6364

65+
//Puts the columns into a matrix.
6466
DenseMatrixHandle centeredM(boost::make_shared<DenseMatrix>(12,2));
6567
for (int i = 0; i < centeredM->rows(); i++){
6668
(*centeredM)(i,0) = centeredColumn1[i];
6769
(*centeredM)(i,1) = centeredColumn2[i];
6870
}
6971
return centeredM;
70-
7172
}
72-
73-
//Outputs: U,S,V
74-
DenseMatrixHandle outputU()
75-
{}
76-
DenseMatrixHandle outputS()
77-
{}
78-
DenseMatrixHandle outputV()
79-
{}
80-
8173
}
8274

83-
84-
75+
//Checks if the algorithm centers the data correctly.
8576
TEST(ComputePCAtest, CenterData)
8677
{
8778
ComputePCAAlgo algo;
@@ -92,33 +83,77 @@ TEST(ComputePCAtest, CenterData)
9283

9384
auto expected = *centeredInputMatrix();
9485

86+
//Checking every element with a tolerance of 1e-5.
9587
for (int i = 0; i < centered.rows(); ++i) {
9688
for (int j = 0; j < centered.cols(); ++j)
97-
EXPECT_NEAR(expected(i,j), centered(i,j), 1e-5);
89+
ASSERT_NEAR(expected(i,j), centered(i,j), 1e-5);
90+
}
91+
}
92+
93+
//Checks if the outputs are correct.
94+
//U,S,V: U: Left principal matrix, S: principal values, V: Right principal matrix.
95+
TEST(ComputePCAtest, checkOutputs)
96+
{
97+
ComputePCAAlgo algo;
98+
99+
DenseMatrixHandle m1(inputMatrix());
100+
DenseMatrixHandle LeftPrinMat_U;
101+
DenseMatrixHandle PrinVals_S;
102+
DenseMatrixHandle RightPrinMat_V;
103+
104+
//Runs algorithm.
105+
algo.run(m1,LeftPrinMat_U,PrinVals_S,RightPrinMat_V);
106+
107+
//Testing if the results are null.
108+
ASSERT_NE(nullptr,LeftPrinMat_U);
109+
ASSERT_NE(nullptr,PrinVals_S);
110+
ASSERT_NE(nullptr,RightPrinMat_V);
111+
112+
//Check the dimensions of the matrices that were created for output.
113+
114+
//Rows
115+
ASSERT_EQ(12,LeftPrinMat_U->rows());
116+
ASSERT_EQ(2,PrinVals_S->rows());
117+
ASSERT_EQ(2,RightPrinMat_V->rows());
118+
119+
//Columns
120+
ASSERT_EQ(12,LeftPrinMat_U->cols());
121+
ASSERT_EQ(1,PrinVals_S->cols());
122+
ASSERT_EQ(2,RightPrinMat_V->cols());
123+
124+
//Eigen does not create a diagonal matrix when it computes SVD, it just has a column with the principal values, so we must put it into a diagonal matrix to be able to do some matrix multiplication later.
125+
DenseMatrix sDiag = Eigen::MatrixXd::Constant(12,2,0);
126+
sDiag.diagonal() = PrinVals_S->col(0);
127+
128+
//Multiplying back together and comparing to the centered matrix. They should be equal to each other with some tolerance.
129+
auto product = (*LeftPrinMat_U) * sDiag * (*RightPrinMat_V).transpose();
130+
131+
auto expected = *centeredInputMatrix();
132+
133+
//Comparing each element in the matrices.
134+
for (int i = 0; i < product.rows(); ++i) {
135+
for (int j = 0; j < product.cols(); ++j)
136+
ASSERT_NEAR(expected(i,j), product(i,j), 1e-5);
98137
}
138+
99139
}
100140

101-
//TEST(ComputePCAtest, output1)
102-
//{
103-
// ComputePCAAlgorithm algo;
104-
//
105-
// DenseMatrixHandle m1(testMatrix());
106-
// ComputePCAAlgorithm::Outputs result = algo.run(ComputePCAAlgorithm::Inputs(m1),ComputePCAAlgorithm);
107-
//
108-
//}
109-
//
110-
//TEST(ComputePCAtest, output2)
111-
//{
112-
// ComputePCAAlgorithm algo;
113-
//
114-
// DenseMatrixHandle m1(testMatrix());
115-
//
116-
//}
117-
//
118-
//TEST(ComputePCAtest, output3)
119-
//{
120-
// ComputePCAAlgorithm algo;
121-
//
122-
// DenseMatrixHandle m1(testMatrix());
123-
//
124-
//}
141+
//Tests for input with a dimension of zero.
142+
TEST(ComputePCAtest, ThrowsForZeroDimensionInput)
143+
{
144+
ComputePCAAlgo algo;
145+
146+
DenseMatrixHandle m1(new DenseMatrix(5, 0));
147+
DenseMatrixHandle m2(new DenseMatrix(0, 5));
148+
DenseMatrixHandle m3(new DenseMatrix(0, 0));
149+
150+
DenseMatrixHandle LeftPrinMat_U;
151+
DenseMatrixHandle PrinVals_S;
152+
DenseMatrixHandle RightPrinMat_V;
153+
154+
//Runs algorithm and expects an error.
155+
EXPECT_ANY_THROW(algo.run(m1,LeftPrinMat_U,PrinVals_S,RightPrinMat_V));
156+
EXPECT_ANY_THROW(algo.run(m2,LeftPrinMat_U,PrinVals_S,RightPrinMat_V));
157+
EXPECT_ANY_THROW(algo.run(m3,LeftPrinMat_U,PrinVals_S,RightPrinMat_V));
158+
159+
}

0 commit comments

Comments
 (0)