diff --git a/src/stan/io/serializer.hpp b/src/stan/io/serializer.hpp index 7e7240407bf..95128030f7e 100644 --- a/src/stan/io/serializer.hpp +++ b/src/stan/io/serializer.hpp @@ -24,7 +24,7 @@ class serializer { /** * Check there is room for at least m more reals to store * - * @param m Number of reals to read + * @param m Number of reals to Write * @throws std::runtime_error if there isn't room for m reals */ void check_r_capacity(size_t m) const { @@ -178,8 +178,6 @@ class serializer { * Write a Eigen matrix of size `(rows, cols)` with complex inner type to * storage * @tparam Mat The type to write - * @param rows The size of the rows of the matrix. - * @param cols The size of the cols of the matrix. */ template * = nullptr, require_vt_complex* = nullptr> @@ -229,8 +227,261 @@ class serializer { */ template * = nullptr> inline void write(StdVec&& x) { - for (size_t i = 0; i < x.size(); ++i) { - this->write(x[i]); + for (const auto& x_i : x) { + this->write(x_i); + } + } + + /** + * Write a serialized lower bounded variable and unconstrain it + * + * @tparam S A scalar, Eigen type, or `std::vector` + * @tparam L Type of lower bound + * @param lb Lower bound + * @param x An object of the types listed above. + */ + template + inline void write_free_lb(const L& lb, const S& x) { + this->write(stan::math::lb_free(x, lb)); + } + + /** + * Write a serialized lower bounded variable and unconstrain it + * + * @tparam S A scalar, Eigen type, or `std::vector` + * @tparam U Type of upper bound + * @param ub Upper bound + * @param x An object of the types listed above. + */ + template + inline void write_free_ub(const U& ub, const S& x) { + this->write(stan::math::ub_free(x, ub)); + } + + /** + * Write a serialized bounded variable and unconstrain it + * + * @tparam S A scalar, Eigen type, or `std::vector` + * @tparam L Type of lower bound + * @tparam U Type of upper bound + * @param lb Lower bound + * @param ub Upper bound + * @param x An object of the types listed above. + */ + template + inline void write_free_lub(const L& lb, const U& ub, const S& x) { + this->write(stan::math::lub_free(x, lb, ub)); + } + + /** + * Write a serialized offset-multiplied variable and unconstrain it + * + * @tparam S A scalar, Eigen type, or `std::vector` + * @tparam O Type of offset + * @tparam M Type of multiplier + * @param offset Offset + * @param multiplier Multiplier + * @param x An object of the types listed above. + */ + template + inline void write_free_offset_multiplier(const O& offset, const M& multiplier, + const S& x) { + this->write(stan::math::offset_multiplier_free(x, offset, multiplier)); + } + + /** + * Write a serialized unit vector and unconstrain it + * + * @tparam Vec An Eigen type with either fixed rows or columns at compile + * time. + * @param x The vector to read from. + */ + template * = nullptr> + inline void write_free_unit_vector(const Vec& x) { + this->write(stan::math::unit_vector_free(x)); + } + + /** + * Write serialized unit vectors and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_unit_vector(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_unit_vector(ret_i); + } + } + + /** + * Write a serialized simplex and unconstrain it + * + * @tparam Vec An Eigen type with either fixed rows or columns at compile + * time. + * @param x The vector to read from. + */ + template * = nullptr> + inline void write_free_simplex(const Vec& x) { + this->write(stan::math::simplex_free(x)); + } + + /** + * Write serialized simplices and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_simplex(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_simplex(ret_i); + } + } + + /** + * Write a serialized ordered and unconstrain it + * + * @tparam Vec An Eigen type with either fixed rows or columns at compile + * time. + * @param x The vector to read from. + */ + template * = nullptr> + inline void write_free_ordered(const Vec& x) { + this->write(stan::math::ordered_free(x)); + } + + /** + * Write serialized ordered vectors and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_ordered(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_ordered(ret_i); + } + } + + /** + * Write a serialized positive ordered vector and unconstrain it + * + * @tparam Vec An Eigen type with either fixed rows or columns at compile + * time. + * @param x The vector to read from. + */ + template * = nullptr> + inline void write_free_positive_ordered(const Vec& x) { + this->write(stan::math::positive_ordered_free(x)); + } + + /** + * Write serialized positive ordered vectors and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_positive_ordered(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_positive_ordered(ret_i); + } + } + + /** + * Write a serialized covariance matrix cholesky factor and unconstrain it + * + * @tparam Mat An Eigen type with dynamic rows and columns at compile time. + * @param x An Eigen matrix to write to the serialized vector. + */ + template * = nullptr> + inline void write_free_cholesky_factor_cov(const Mat& x) { + this->write(stan::math::cholesky_factor_free(x)); + } + + /** + * Write serialized covariance matrix cholesky factors and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_cholesky_factor_cov(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_cholesky_factor_cov(ret_i); + } + } + + /** + * Write a serialized covariance matrix cholesky factor and unconstrain it + * + * @tparam Mat Type of input + * @param x An Eigen matrix to write to the serialized vector. + */ + template * = nullptr> + inline void write_free_cholesky_factor_corr(const Mat& x) { + this->write(stan::math::cholesky_corr_free(x)); + } + + /** + * Write serialized correlation matrix cholesky factors and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_cholesky_factor_corr(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_cholesky_factor_corr(ret_i); + } + } + + /** + * Write a serialized covariance matrix cholesky factor and unconstrain it + * + * @tparam Mat An Eigen type with dynamic rows and columns at compile time + * @param x An Eigen matrix to write to the serialized vector. + */ + template * = nullptr> + inline void write_free_cov_matrix(const Mat& x) { + this->write(stan::math::cov_matrix_free(x)); + } + + /** + * Write serialized covariance matrices and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x a standard vector. + */ + template * = nullptr> + inline void write_free_cov_matrix(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_cov_matrix(ret_i); + } + } + + /** + * Write a serialized covariance matrix cholesky factor and unconstrain it + * + * @tparam Mat An Eigen type with dynamic rows and columns at compile time. + * @param x An Eigen matrix to write to the serialized vector. + */ + template * = nullptr> + inline void write_free_corr_matrix(const Mat& x) { + this->write(stan::math::corr_matrix_free(x)); + } + + /** + * Write serialized correlation matrices and unconstrain them + * + * @tparam StdVec A `std:vector` + * @param x An std vector. + */ + template * = nullptr> + inline void write_free_corr_matrix(const StdVec& x) { + for (const auto& ret_i : x) { + this->write_free_corr_matrix(ret_i); } } }; diff --git a/src/test/unit/io/serializer_test.cpp b/src/test/unit/io/serializer_test.cpp index 3e128bf3e3c..f6ab9fc742d 100644 --- a/src/test/unit/io/serializer_test.cpp +++ b/src/test/unit/io/serializer_test.cpp @@ -1,3 +1,4 @@ +#include #include // expect_near_rel comes from lib/stan_math #include @@ -289,3 +290,446 @@ TEST(serializer, eos_exception) { std::runtime_error); } } + +template +void write_free_lb_test(Sizes... sizes) { + double lb = 0.5; + constexpr size_t theta_size = 100; + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(theta_size); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_lb(lb, lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta_size); + stan::io::serializer serializer(theta2); + serializer.write_free_lb(lb, vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_lb) { + write_free_lb_test(); + write_free_lb_test(4); + write_free_lb_test>(2, 4); + write_free_lb_test>>(3, 2, 4); +} + +template +void write_free_ub_test(Sizes... sizes) { + double ub = 0.5; + constexpr size_t theta_size = 100; + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(theta_size); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_ub(ub, lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta_size); + stan::io::serializer serializer(theta2); + serializer.write_free_ub(ub, vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_ub) { + write_free_ub_test(); + write_free_ub_test(4); + write_free_ub_test>(2, 4); + write_free_ub_test>>(3, 2, 4); +} + +template +void write_free_lub_test(Sizes... sizes) { + double ub = 0.5; + double lb = 0.1; + constexpr size_t theta_size = 100; + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(theta_size); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref + = deserializer.read_constrain_lub(lb, ub, lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta_size); + stan::io::serializer serializer(theta2); + serializer.write_free_lub(lb, ub, vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_lub) { + write_free_lub_test(); + write_free_lub_test(4); + write_free_lub_test>(2, 4); + write_free_lub_test>>(3, 2, 4); +} + +template +void write_free_offset_multiplier_test(Sizes... sizes) { + double offset = 0.5; + double multiplier = 0.35; + constexpr size_t theta_size = 100; + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(theta_size); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_offset_multiplier( + offset, multiplier, lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta_size); + stan::io::serializer serializer(theta2); + serializer.write_free_offset_multiplier(offset, multiplier, vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_offset_multiplier) { + write_free_offset_multiplier_test(); + write_free_offset_multiplier_test(4); + write_free_offset_multiplier_test>(2, 4); + write_free_offset_multiplier_test>>( + 3, 2, 4); +} +template +void write_free_unit_vector_test(Sizes... sizes) { + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref + = deserializer.read_constrain_unit_vector(lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + stan::io::serializer serializer(theta2); + serializer.write_free_unit_vector(vec_ref); + + // For unit vector, it's not actually doing a change of variables so we check + // theta2 equals theta2 (freeing doesn't actually get the unconstrained + // variable back). + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta1.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta2.segment(0, used1), + theta2.segment(0, used2)); +} + +TEST(serializer_vectorized, write_free_unit_vector) { + write_free_unit_vector_test(4); + write_free_unit_vector_test>(2, 4); + write_free_unit_vector_test>>(3, 2, + 4); +} + +template +void write_free_simplex_test(Sizes... sizes) { + constexpr size_t theta_size = 100; + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(theta_size); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_simplex(lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta_size); + stan::io::serializer serializer(theta2); + serializer.write_free_simplex(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_simplex) { + write_free_simplex_test(4); + write_free_simplex_test>(2, 4); + write_free_simplex_test>>(3, 2, 4); +} + +// ordered + +template +void write_free_ordered_test(Sizes... sizes) { + // Read an constrained variable + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + std::vector theta_i; + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_ordered(lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + stan::io::serializer serializer(theta2); + serializer.write_free_ordered(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_ordered) { + write_free_ordered_test(4); + write_free_ordered_test>(2, 4); + write_free_ordered_test>>(3, 2, 4); +} + +// positive_ordered + +template +void write_free_positive_ordered_test(Sizes... sizes) { + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref + = deserializer.read_constrain_positive_ordered(lp, sizes...); + + // Serialize a constrained variable + stan::io::serializer serializer(theta2); + serializer.write_free_positive_ordered(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_positive_ordered) { + write_free_positive_ordered_test(4); + write_free_positive_ordered_test>(2, 4); + write_free_positive_ordered_test>>( + 3, 2, 4); +} + +// cholesky_factor_cov + +template +void write_free_cholesky_factor_cov_test(Sizes... sizes) { + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_cholesky_factor_cov( + lp, sizes...); + + // Serialize a constrained variable + stan::io::serializer serializer(theta2); + serializer.write_free_cholesky_factor_cov(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_cholesky_factor_cov) { + write_free_cholesky_factor_cov_test(4, 3); + write_free_cholesky_factor_cov_test>(2, 4, 3); + write_free_cholesky_factor_cov_test< + std::vector>>(3, 2, 4, 3); + + write_free_cholesky_factor_cov_test(2, 2); + write_free_cholesky_factor_cov_test>(2, 2, 2); + write_free_cholesky_factor_cov_test< + std::vector>>(3, 2, 2, 2); +} + +// cholesky_factor_corr + +template +void write_free_cholesky_factor_corr_test(Sizes... sizes) { + // Read an constrained variable + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + std::vector theta_i; + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref = deserializer.read_constrain_cholesky_factor_corr( + lp, sizes...); + + // Serialize a constrained variable + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + stan::io::serializer serializer(theta2); + serializer.write_free_cholesky_factor_corr(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_cholesky_factor_corr) { + write_free_cholesky_factor_corr_test(2); + write_free_cholesky_factor_corr_test>(2, 2); + write_free_cholesky_factor_corr_test< + std::vector>>(3, 2, 2); +} + +// cov_matrix + +template +void write_free_cov_matrix_test(Sizes... sizes) { + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref + = deserializer.read_constrain_cov_matrix(lp, sizes...); + + // Serialize a constrained variable + stan::io::serializer serializer(theta2); + serializer.write_free_cov_matrix(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_cov_matrix) { + write_free_cov_matrix_test(2); + write_free_cov_matrix_test>(2, 2); + write_free_cov_matrix_test>>(3, 2, + 2); +} + +// corr_matrix + +template +void write_free_corr_matrix_test(Sizes... sizes) { + Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); + Eigen::VectorXd theta2 = Eigen::VectorXd::Random(theta1.size()); + std::vector theta_i; + + // Read an constrained variable + stan::io::deserializer deserializer(theta1, theta_i); + double lp = 0.0; + Ret vec_ref + = deserializer.read_constrain_corr_matrix(lp, sizes...); + + // Serialize a constrained variable + stan::io::serializer serializer(theta2); + serializer.write_free_corr_matrix(vec_ref); + + size_t used1 = theta1.size() - deserializer.available(); + size_t used2 = theta2.size() - serializer.available(); + + // Number of variables read should equal number of variables written + EXPECT_EQ(used1, used2); + + // Make sure the variables written back are the same + stan::test::expect_near_rel("deserializer read free", + theta1.segment(0, used1), + theta2.segment(0, used1)); +} + +TEST(serializer_vectorized, write_free_corr_matrix) { + write_free_corr_matrix_test(2); + write_free_corr_matrix_test>(2, 2); + write_free_corr_matrix_test>>(3, 2, + 2); +}