Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add singular_values, svd_U, and svd_V #2286

Merged
merged 42 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
63237ea
implement interp1
Oct 5, 2020
71fe4b6
reverse svd and its test
Nov 21, 2020
f7b127c
clean interp1 and add to function header file
Nov 21, 2020
77178ab
Formatting and such (Issue #2192)
bbbales2 Nov 22, 2020
dc43ce0
update reverse mode on deleting D and add to_arena
Nov 29, 2020
c596c4d
Finish singular_values.hpp, verify tests pass
Dashadower Dec 10, 2020
2ba5173
Remove unnecessary code from singular_values.hpp
Dashadower Dec 10, 2020
81b442a
Use arena_t and remove redundant comments in singular_values.hpp
Dashadower Dec 10, 2020
8a3a324
Couple cleanup things (Issue #2192)
bbbales2 Dec 16, 2020
f674c6c
WIP svd_U function and tests
Dashadower Dec 18, 2020
09e7ecf
Change to use matrix_v for matrix U
Dashadower Dec 18, 2020
ea7e589
Merge remote-tracking branch 'origin/develop' into feature/svd_review
bbbales2 Dec 18, 2020
c097d11
Merging in develop, adding .val_op() and .adj_op()
bbbales2 Dec 18, 2020
034471f
Merge remote-tracking branch 'origin/develop' into feature/svd
bbbales2 Dec 18, 2020
d258e61
Merge branch 'feature/svd' into feature/svd_review
bbbales2 Dec 18, 2020
be43569
Merge remote-tracking branch 'origin/develop' into hyunji-develop
bbbales2 Dec 18, 2020
5f121a0
Merge remote-tracking branch 'hyunji/develop' into feature/svd
bbbales2 Dec 18, 2020
12485ad
Merge branch 'feature/svd' into feature/svd_review
bbbales2 Dec 18, 2020
3b33ec8
Minor tweaks to svd_U and corresponding test suite
Dashadower Dec 20, 2020
156acea
Renamed `singular_values_U` to `svd_U`, add prim version of `svd_U`, …
bbbales2 Dec 20, 2020
7ba6df0
Updated tests again (Issue #2101)
bbbales2 Dec 20, 2020
aa43a00
Add svd_V function
Dashadower Dec 20, 2020
7df7307
Address non-square inputs for svd_U and svd_V
Dashadower Dec 20, 2020
e1c7dcc
Add prim tests for svd_V, svd_U
Dashadower Dec 20, 2020
7ec29e8
Updated svd V test
bbbales2 Dec 22, 2020
a7c21ec
Update tests for prim/fun/{svd_U, svd_V}.hpp. Change rev/fun/{svd_U, …
Dashadower Dec 27, 2020
e057457
Update prim tests
Dashadower Dec 31, 2020
63defd1
Update mix mode tests
Dashadower Jan 2, 2021
bb67570
Merge commit 'be49360ca7eb65addfa7001c04699fd6f1a85330' into HEAD
yashikno Jan 2, 2021
7873afc
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Jan 2, 2021
59aefc0
Fix lint warnings
Dashadower Jan 2, 2021
52889c4
Update doxygen warnings
Dashadower Jan 2, 2021
c4cb965
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Jan 2, 2021
8d41dd4
Update doxygen docs
Dashadower Jan 2, 2021
eb17cfa
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Dashadower Jan 5, 2021
e8713e4
Update tests and functions for svd
Dashadower Jan 5, 2021
f47682b
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Jan 5, 2021
3b025fc
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Dashadower Jan 10, 2021
c3835c6
Add vari support and relevant tests
Dashadower Jan 10, 2021
02c5a81
Removed extra comments, rearranged includes, and removed custom toler…
bbbales2 Jan 10, 2021
091ce12
Merge commit '88040ea5e8e8bc308ee080a8dcab9d5452f40ecf' into HEAD
yashikno Jan 10, 2021
784284f
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Jan 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions stan/math/prim/fun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@
#include <stan/math/prim/fun/sub_row.hpp>
#include <stan/math/prim/fun/subtract.hpp>
#include <stan/math/prim/fun/sum.hpp>
#include <stan/math/prim/fun/svd_U.hpp>
#include <stan/math/prim/fun/svd_V.hpp>
#include <stan/math/prim/fun/symmetrize_from_lower_tri.hpp>
#include <stan/math/prim/fun/tail.hpp>
#include <stan/math/prim/fun/tan.hpp>
Expand Down
1 change: 1 addition & 0 deletions stan/math/prim/fun/Eigen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <Eigen/Sparse>
#include <Eigen/QR>
#include <Eigen/src/Core/NumTraits.h>
#include <Eigen/SVD>

namespace Eigen {

Expand Down
8 changes: 4 additions & 4 deletions stan/math/prim/fun/singular_values.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/err/check_nonzero_size.hpp>

namespace stan {
namespace math {
Expand All @@ -17,12 +18,11 @@ namespace math {
* @param m Specified matrix.
* @return Singular values of the matrix.
*/
template <typename EigMat, require_eigen_matrix_dynamic_t<EigMat>* = nullptr>
template <typename EigMat, require_eigen_matrix_dynamic_t<EigMat>* = nullptr,
require_not_st_var<EigMat>* = nullptr>
Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic, 1> singular_values(
const EigMat& m) {
if (m.size() == 0) {
return {};
}
check_nonzero_size("singular_values", "m", m);

return Eigen::JacobiSVD<Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic,
Eigen::Dynamic> >(m)
Expand Down
32 changes: 32 additions & 0 deletions stan/math/prim/fun/svd_U.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef STAN_MATH_PRIM_FUN_SVD_U_HPP
#define STAN_MATH_PRIM_FUN_SVD_U_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/Eigen.hpp>

namespace stan {
namespace math {

/**
* Given input matrix m, return matrix U where `m = UDV^{T}`
*
* @tparam EigMat type of the matrix
* @param m MxN input matrix
* @return Orthogonal matrix U
*/
template <typename EigMat, require_eigen_matrix_dynamic_t<EigMat>* = nullptr,
require_not_st_var<EigMat>* = nullptr>
Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic, Eigen::Dynamic> svd_U(
const EigMat& m) {
check_nonzero_size("svd_U", "m", m);

return Eigen::JacobiSVD<Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic,
Eigen::Dynamic> >(m,
Eigen::ComputeThinU)
.matrixU();
}

} // namespace math
} // namespace stan

#endif
32 changes: 32 additions & 0 deletions stan/math/prim/fun/svd_V.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef STAN_MATH_PRIM_FUN_SVD_V_HPP
#define STAN_MATH_PRIM_FUN_SVD_V_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/Eigen.hpp>

namespace stan {
namespace math {

/**
* Given input matrix m, return matrix V where `m = UDV^{T}`
*
* @tparam EigMat type of the matrix
* @param m MxN input matrix
* @return Orthogonal matrix V
*/
template <typename EigMat, require_eigen_matrix_dynamic_t<EigMat>* = nullptr,
require_not_st_var<EigMat>* = nullptr>
Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic, Eigen::Dynamic> svd_V(
const EigMat& m) {
check_nonzero_size("svd_V", "m", m);

return Eigen::JacobiSVD<Eigen::Matrix<value_type_t<EigMat>, Eigen::Dynamic,
Eigen::Dynamic> >(m,
Eigen::ComputeThinV)
.matrixV();
}

} // namespace math
} // namespace stan

#endif
3 changes: 3 additions & 0 deletions stan/math/rev/fun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
#include <stan/math/rev/fun/sd.hpp>
#include <stan/math/rev/fun/simplex_constrain.hpp>
#include <stan/math/rev/fun/sin.hpp>
#include <stan/math/rev/fun/singular_values.hpp>
#include <stan/math/rev/fun/svd_U.hpp>
#include <stan/math/rev/fun/svd_V.hpp>
#include <stan/math/rev/fun/sinh.hpp>
#include <stan/math/rev/fun/softmax.hpp>
#include <stan/math/rev/fun/sqrt.hpp>
Expand Down
48 changes: 48 additions & 0 deletions stan/math/rev/fun/singular_values.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef STAN_MATH_REV_FUN_SINGULAR_VALUES_HPP
#define STAN_MATH_REV_FUN_SINGULAR_VALUES_HPP

#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/err/check_nonzero_size.hpp>
#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>

namespace stan {
namespace math {

/**
* Return the singular values of the specified matrix.
*
* Adjoint update equation comes from Equation (4) in Differentiable Programming
* Tensor Networks(H. Liao, J. Liu, et al., arXiv:1903.09650).
*
* @tparam EigMat type of input matrix
* @param m MxN input matrix
* @return Singular values of matrix
*/
template <typename EigMat, require_rev_matrix_t<EigMat>* = nullptr>
inline auto singular_values(const EigMat& m) {
using ret_type = return_var_matrix_t<Eigen::VectorXd, EigMat>;
check_nonzero_size("singular_values", "m", m);

auto arena_m = to_arena(m);

Eigen::JacobiSVD<Eigen::MatrixXd> svd(
arena_m.val(), Eigen::ComputeThinU | Eigen::ComputeThinV);

arena_t<ret_type> singular_values = svd.singularValues();

auto arena_U = to_arena(svd.matrixU());
auto arena_V = to_arena(svd.matrixV());

reverse_pass_callback([arena_m, arena_U, singular_values, arena_V]() mutable {
arena_m.adj()
+= arena_U * singular_values.adj().asDiagonal() * arena_V.transpose();
});

return ret_type(singular_values);
}

} // namespace math
} // namespace stan

#endif
73 changes: 73 additions & 0 deletions stan/math/rev/fun/svd_U.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#ifndef STAN_MATH_REV_FUN_SVD_U_HPP
#define STAN_MATH_REV_FUN_SVD_U_HPP

#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/err/check_nonzero_size.hpp>
#include <stan/math/prim/fun/typedefs.hpp>
#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/value_of.hpp>

namespace stan {
namespace math {

/**
* Given input matrix m, return matrix U where `m = UDV^{T}`
*
* Adjoint update equation comes from Equation (4) in Differentiable Programming
* Tensor Networks(H. Liao, J. Liu, et al., arXiv:1903.09650).
*
* @tparam EigMat type of input matrix
* @param m MxN input matrix
* @return Orthogonal matrix U
*/
template <typename EigMat, require_rev_matrix_t<EigMat>* = nullptr>
inline auto svd_U(const EigMat& m) {
using ret_type = return_var_matrix_t<Eigen::MatrixXd, EigMat>;
check_nonzero_size("svd_U", "m", m);

const int M = std::min(m.rows(), m.cols());
auto arena_m = to_arena(m);

Eigen::JacobiSVD<Eigen::MatrixXd> svd(
arena_m.val(), Eigen::ComputeThinU | Eigen::ComputeThinV);

auto arena_D = to_arena(svd.singularValues());

arena_t<Eigen::MatrixXd> arena_Fp(M, M);

for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
if (j == i) {
arena_Fp(i, j) = 0.0;
} else {
arena_Fp(i, j)
= 1.0 / (arena_D[j] - arena_D[i]) + 1.0 / (arena_D[i] + arena_D[j]);
}
}
}

arena_t<ret_type> arena_U = svd.matrixU();
auto arena_V = to_arena(svd.matrixV());

reverse_pass_callback([arena_m, arena_U, arena_D, arena_V, arena_Fp,
M]() mutable {
Eigen::MatrixXd UUadjT = arena_U.val_op().transpose() * arena_U.adj_op();
arena_m.adj()
+= .5 * arena_U.val_op()
* (arena_Fp.array() * (UUadjT - UUadjT.transpose()).array())
.matrix()
* arena_V.transpose()
+ (Eigen::MatrixXd::Identity(arena_m.rows(), arena_m.rows())
- arena_U.val_op() * arena_U.val_op().transpose())
* arena_U.adj_op() * arena_D.asDiagonal().inverse()
* arena_V.transpose();
});

return ret_type(arena_U);
}

} // namespace math
} // namespace stan

#endif
73 changes: 73 additions & 0 deletions stan/math/rev/fun/svd_V.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#ifndef STAN_MATH_REV_FUN_SVD_V_HPP
#define STAN_MATH_REV_FUN_SVD_V_HPP

#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/err/check_nonzero_size.hpp>
#include <stan/math/prim/fun/typedefs.hpp>
#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/value_of.hpp>

namespace stan {
namespace math {

/**
* Given input matrix m, return matrix V where `m = UDV^{T}`
*
* Adjoint update equation comes from Equation (4) in Differentiable Programming
* Tensor Networks(H. Liao, J. Liu, et al., arXiv:1903.09650).
*
* @tparam EigMat type of input matrix
* @param m MxN input matrix
* @return Orthogonal matrix V
*/
template <typename EigMat, require_rev_matrix_t<EigMat>* = nullptr>
inline auto svd_V(const EigMat& m) {
using ret_type = return_var_matrix_t<Eigen::MatrixXd, EigMat>;
check_nonzero_size("svd_V", "m", m);

const int M = std::min(m.rows(), m.cols());
auto arena_m = to_arena(m);

Eigen::JacobiSVD<Eigen::MatrixXd> svd(
arena_m.val(), Eigen::ComputeThinU | Eigen::ComputeThinV);

auto arena_D = to_arena(svd.singularValues());

arena_t<Eigen::MatrixXd> arena_Fm(M, M);

for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
if (j == i) {
arena_Fm(i, j) = 0.0;
} else {
arena_Fm(i, j)
= 1.0 / (arena_D[j] - arena_D[i]) - 1.0 / (arena_D[i] + arena_D[j]);
}
}
}

auto arena_U = to_arena(svd.matrixU());
arena_t<ret_type> arena_V = svd.matrixV();

reverse_pass_callback([arena_m, arena_U, arena_D, arena_V, arena_Fm,
M]() mutable {
Eigen::MatrixXd VTVadj = arena_V.val_op().transpose() * arena_V.adj_op();
arena_m.adj()
+= 0.5 * arena_U
* (arena_Fm.array() * (VTVadj - VTVadj.transpose()).array())
.matrix()
* arena_V.val_op().transpose()
+ arena_U * arena_D.asDiagonal().inverse()
* arena_V.adj_op().transpose()
* (Eigen::MatrixXd::Identity(arena_m.cols(), arena_m.cols())
- arena_V.val_op() * arena_V.val_op().transpose());
});

return ret_type(arena_V);
}

} // namespace math
} // namespace stan

#endif
22 changes: 12 additions & 10 deletions test/unit/math/mix/fun/singular_values_test.cpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
#include <test/unit/math/test_ad.hpp>
#include <stdexcept>

TEST(MathMixMatFun, singularValues) {
auto f = [](const auto& x) { return stan::math::singular_values(x); };

stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 1e-2;
tols.hessian_fvar_hessian_ = 1e-2;

Eigen::MatrixXd m00(0, 0);
stan::test::expect_ad(f, m00);
EXPECT_THROW(f(m00), std::invalid_argument);

Eigen::MatrixXd m11(1, 1);
m11 << 1.1;
stan::test::expect_ad(tols, f, m11);
stan::test::expect_ad(f, m11);
stan::test::expect_ad_matvar(f, m11);

Eigen::MatrixXd m22(2, 2);
m22 << 3, -5, 7, 11;
stan::test::expect_ad(tols, f, m22);
stan::test::expect_ad(f, m22);
stan::test::expect_ad_matvar(f, m22);

Eigen::MatrixXd m23(2, 3);
m23 << 3, 5, -7, -11, 13, -17;
Eigen::MatrixXd m32 = m23.transpose();
stan::test::expect_ad(tols, f, m23);
stan::test::expect_ad(tols, f, m32);
stan::test::expect_ad(f, m23);
stan::test::expect_ad(f, m32);
stan::test::expect_ad_matvar(f, m23);
stan::test::expect_ad_matvar(f, m32);

Eigen::MatrixXd a22(2, 2);
a22 << 1, 2, 3, 4;
stan::test::expect_ad(tols, f, a22);
stan::test::expect_ad(f, a22);
stan::test::expect_ad_matvar(f, a22);
}
34 changes: 34 additions & 0 deletions test/unit/math/mix/fun/svd_U_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <test/unit/math/test_ad.hpp>
#include <stdexcept>

TEST(MathMixMatFun, svd_U) {
auto f = [](const auto& x) { return stan::math::svd_U(x); };

Eigen::MatrixXd m00(0, 0);
EXPECT_THROW(f(m00), std::invalid_argument);

Eigen::MatrixXd m11(1, 1);
m11 << 1.1;
stan::test::expect_ad(f, m11);
stan::test::expect_ad_matvar(f, m11);

Eigen::MatrixXd m22(2, 2);
m22 << 3, -5, 7, 11;
stan::test::expect_ad(f, m22);
stan::test::expect_ad_matvar(f, m22);

Eigen::MatrixXd m23(2, 3);
m23 << 3, 5, -7, -11, 13, -17;
stan::test::expect_ad(f, m23);
stan::test::expect_ad_matvar(f, m23);

Eigen::MatrixXd m32(3, 2);
m32 << 1, 3, -5, 7, 9, -11;
stan::test::expect_ad(f, m32);
stan::test::expect_ad_matvar(f, m32);

Eigen::MatrixXd a22(2, 2);
a22 << 1, 2, 3, 4;
stan::test::expect_ad(f, a22);
stan::test::expect_ad_matvar(f, a22);
}
Loading