openGPMP
Open Source Mathematics Package
svd.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  *
3  * Project
4  * _____ _____ __ __ _____
5  * / ____| __ \| \/ | __ \
6  * ___ _ __ ___ _ __ | | __| |__) | \ / | |__) |
7  * / _ \| '_ \ / _ \ '_ \| | |_ | ___/| |\/| | ___/
8  *| (_) | |_) | __/ | | | |__| | | | | | | |
9  * \___/| .__/ \___|_| |_|\_____|_| |_| |_|_|
10  * | |
11  * |_|
12  *
13  * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
14  *
15  * This software is licensed as described in the file LICENSE, which
16  * you should have received as part of this distribution. The terms
17  * among other details are referenced in the official documentation
18  * seen here : https://akielaries.github.io/openGPMP/ along with
19  * important files seen in this project.
20  *
21  * You may opt to use, copy, modify, merge, publish, distribute
22  * and/or sell copies of the Software, and permit persons to whom
23  * the Software is furnished to do so, under the terms of the
24  * LICENSE file. As this is an Open Source effort, all implementations
25  * must be of the same methodology.
26  *
27  *
28  *
29  * This software is distributed on an AS IS basis, WITHOUT
30  * WARRANTY OF ANY KIND, either express or implied.
31  *
32  ************************************************************************/
33 #include <cmath>
34 #include <iostream>
35 #include <openGPMP/linalg/svd.hpp>
36 #include <stdexcept>
37 
38 gpmp::linalg::SVD::SVD(std::vector<std::vector<double>> &matrix) {
39  if (matrix.empty() || matrix[0].empty()) {
40  throw std::invalid_argument("Error: Matrix for SVD is empty.");
41  }
42 
43  computeSVD(matrix);
44 }
45 
46 std::vector<double> gpmp::linalg::SVD::getSingularValues() const {
47  return singularValues_;
48 }
49 
50 std::vector<std::vector<double>>
52  return leftSingularVectors_;
53 }
54 
55 std::vector<std::vector<double>>
57  return rightSingularVectors_;
58 }
59 
60 void gpmp::linalg::SVD::computeSVD(std::vector<std::vector<double>> &matrix) {
61  size_t m = matrix.size();
62  size_t n = matrix[0].size();
63 
64  singularValues_.resize(std::min(m, n));
65  leftSingularVectors_ =
66  std::vector<std::vector<double>>(m, std::vector<double>(m, 0.0));
67  rightSingularVectors_ =
68  std::vector<std::vector<double>>(n, std::vector<double>(n, 0.0));
69 
70  std::vector<double> superdiagonal(n - 1, 0.0);
71 
72  bidiagonalize(const_cast<std::vector<std::vector<double>> &>(matrix),
73  singularValues_,
74  superdiagonal);
75 
76  // left/right singular vectors as identity matrices
77  for (size_t i = 0; i < m; ++i) {
78  leftSingularVectors_[i][i] = 1.0;
79  }
80 
81  for (size_t i = 0; i < n; ++i) {
82  rightSingularVectors_[i][i] = 1.0;
83  }
84 }
85 
86 void gpmp::linalg::SVD::bidiagonalize(std::vector<std::vector<double>> &matrix,
87  std::vector<double> &diagonal,
88  std::vector<double> &superdiagonal) {
89  size_t m = matrix.size();
90  size_t n = matrix[0].size();
91 
92  diagonal.resize(std::min(m, n));
93  superdiagonal.resize(std::min(m, n) - 1);
94 
95  for (size_t k = 0; k < std::min(m, n); ++k) {
96  double alpha = 0.0;
97  for (size_t i = k; i < m; ++i) {
98  alpha += matrix[i][k] * matrix[i][k];
99  }
100  alpha = (matrix[k][k] > 0) ? -std::sqrt(alpha) : std::sqrt(alpha);
101 
102  double beta = alpha * (alpha - matrix[k][k]);
103  diagonal[k] = -alpha;
104 
105  // make a copy of the current column of the matrix
106  std::vector<double> columnCopy(m - k);
107  for (size_t i = k; i < m; ++i) {
108  columnCopy[i - k] = matrix[i][k];
109  }
110 
111  computeHouseholderReflection(columnCopy, superdiagonal, beta);
112  applyHouseholder(matrix, superdiagonal, beta, k, k + 1);
113  }
114 }
115 
117  const std::vector<double> &x,
118  std::vector<double> &v,
119  double &beta) {
120  size_t n = x.size();
121  double sigma = 0.0;
122 
123  for (size_t i = 1; i < n; ++i) {
124  sigma += x[i] * x[i];
125  }
126 
127  v.resize(n);
128  v[0] = 1.0;
129  for (size_t i = 1; i < n; ++i) {
130  v[i] = x[i] / sigma;
131  }
132 
133  beta = 2.0 / (sigma + 1.0);
134 }
135 
137  std::vector<std::vector<double>> &matrix,
138  const std::vector<double> &v,
139  double beta,
140  size_t fromRow,
141  size_t fromCol) {
142  size_t m = matrix.size();
143  size_t n = matrix[0].size();
144 
145  for (size_t i = fromRow; i < m; ++i) {
146  double dotProduct = 0.0;
147  for (size_t j = fromCol; j < n; ++j) {
148  dotProduct += matrix[i][j] * v[j - fromCol];
149  }
150 
151  dotProduct *= beta;
152 
153  for (size_t j = fromCol; j < n; ++j) {
154  matrix[i][j] -= dotProduct * v[j - fromCol];
155  }
156  }
157 }
void computeHouseholderReflection(const std::vector< double > &x, std::vector< double > &v, double &beta)
Definition: svd.cpp:116
std::vector< double > getSingularValues() const
Get the singular values of the matrix.
Definition: svd.cpp:46
void bidiagonalize(std::vector< std::vector< double >> &matrix, std::vector< double > &diagonal, std::vector< double > &superdiagonal)
Definition: svd.cpp:86
SVD(std::vector< std::vector< double >> &matrix)
Constructor to compute SVD for a given matrix.
Definition: svd.cpp:38
std::vector< std::vector< double > > getLeftSingularVectors() const
Get the left singular vectors of the matrix.
Definition: svd.cpp:51
void applyHouseholder(std::vector< std::vector< double >> &matrix, const std::vector< double > &v, double beta, size_t fromRow, size_t fromCol)
Definition: svd.cpp:136
std::vector< std::vector< double > > getRightSingularVectors() const
Get the right singular vectors of the matrix.
Definition: svd.cpp:56
void computeSVD(std::vector< std::vector< double >> &matrix)
Definition: svd.cpp:60