diff --git a/src/stan/io/serializer.hpp b/src/stan/io/serializer.hpp index 08817b68b4..cfe0d8bdfd 100644 --- a/src/stan/io/serializer.hpp +++ b/src/stan/io/serializer.hpp @@ -492,6 +492,54 @@ class serializer { this->write_free_corr_matrix(ret_i); } } + + /** + * Read a serialized column simplex matrix and unconstrain it + * + * @tparam Mat An Eigen matrix + * @param x A column stochastic eigen matrix + */ + template * = nullptr> + inline void write_free_stochastic_column(Mat&& x) { + this->write(stan::math::stochastic_column_free(x)); + } + + /** + * Read serialized column simplex matrices and unconstrain them + * + * @tparam StdVec A standard vector of Eigen matrices + * @param x A vector of column stochastic Eigen matrices + */ + template * = nullptr> + inline void write_free_stochastic_column(StdVec&& x) { + for (auto&& x_i : x) { + this->write_free_stochastic_column(x_i); + } + } + + /** + * Read a serialized row simplex matrix and unconstrain it + * + * @tparam Mat An Eigen matrix + * @param x A row stochastic eigen matrix + */ + template * = nullptr> + inline void write_free_stochastic_row(Mat&& x) { + this->write(stan::math::stochastic_row_free(x)); + } + + /** + * Read serialized row simplex matrices and unconstrain them + * + * @tparam StdVec A standard vector of Eigen matrices + * @param x A vector of row stochastic Eigen matrices + */ + template * = nullptr> + inline void write_free_stochastic_row(StdVec&& x) { + for (auto&& x_i : x) { + this->write_free_stochastic_row(x_i); + } + } }; } // namespace io diff --git a/src/test/unit/io/serializer_test.cpp b/src/test/unit/io/serializer_test.cpp index f6ab9fc742..bcb5f09051 100644 --- a/src/test/unit/io/serializer_test.cpp +++ b/src/test/unit/io/serializer_test.cpp @@ -291,96 +291,29 @@ TEST(serializer, eos_exception) { } } -template -void write_free_lb_test(Sizes... sizes) { +namespace stan { +namespace test { +template +void serializer_test_impl(DeserializeRead&& deserialize_read, + SerializeFree&& serialize_free, + const std::tuple& sizes, Args&&... args) { 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...); + = stan::math::apply(deserialize_read, sizes, deserializer, args..., lp); // 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); + // serializer.write_free_lb(lb, vec_ref); + serialize_free(serializer, args..., vec_ref); size_t used1 = theta1.size() - deserializer.available(); size_t used2 = theta2.size() - serializer.available(); @@ -394,51 +327,116 @@ void write_free_lub_test(Sizes... sizes) { 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 class Serializer, typename... Args, + typename... Sizes> +void serializer_test(const std::tuple& sizes, Args&&... args) { + serializer_test_impl(Serializer::read(), Serializer::free(), + sizes, args...); } -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; +} // namespace test +} // namespace stan - // 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); +template +struct LbConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_lb(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_lb(args...); + }; + } +}; - size_t used1 = theta1.size() - deserializer.available(); - size_t used2 = theta2.size() - serializer.available(); +TEST(serializer_vectorized, write_free_lb) { + using stan::test::serializer_test; + serializer_test(std::make_tuple(), 0.5); + serializer_test(std::make_tuple(4), 0.5); + serializer_test, LbConstrain>( + std::make_tuple(2, 4), 0.5); + serializer_test>, LbConstrain>( + std::make_tuple(3, 2, 4), 0.5); +} + +// ub +template +struct UbConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_ub(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_ub(args...); + }; + } +}; - // Number of variables read should equal number of variables written - EXPECT_EQ(used1, used2); +TEST(serializer_vectorized, write_free_ub) { + using stan::test::serializer_test; + serializer_test(std::make_tuple(), 0.5); + serializer_test(std::make_tuple(4), 0.5); + serializer_test, UbConstrain>( + std::make_tuple(2, 4), 0.5); + serializer_test>, UbConstrain>( + std::make_tuple(3, 2, 4), 0.5); +} + +// lub +template +struct LubConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_lub(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_lub(args...); + }; + } +}; - // 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) { + using stan::test::serializer_test; + serializer_test(std::make_tuple(), 0.2, 0.5); + serializer_test(std::make_tuple(4), 0.2, 0.5); + serializer_test, LubConstrain>( + std::make_tuple(2, 4), 0.2, 0.5); + serializer_test>, LubConstrain>( + std::make_tuple(3, 2, 4), 0.2, 0.5); +} + +// offset multiplier +template +struct OffsetMultConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_offset_multiplier(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_offset_multiplier(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test(std::make_tuple(), 0.2, 0.5); + serializer_test(std::make_tuple(4), 0.2, + 0.5); + serializer_test, OffsetMultConstrain>( + std::make_tuple(2, 4), 0.2, 0.5); + serializer_test>, + OffsetMultConstrain>(std::make_tuple(3, 2, 4), 0.2, 0.5); } + template void write_free_unit_vector_test(Sizes... sizes) { Eigen::VectorXd theta1 = Eigen::VectorXd::Random(100); @@ -477,259 +475,229 @@ TEST(serializer_vectorized, write_free_unit_vector) { 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)); -} +// simplex +template +struct SimplexConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_simplex(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_simplex(args...); + }; + } +}; TEST(serializer_vectorized, write_free_simplex) { - write_free_simplex_test(4); - write_free_simplex_test>(2, 4); - write_free_simplex_test>>(3, 2, 4); + using stan::test::serializer_test; + serializer_test(std::make_tuple(4)); + serializer_test, SimplexConstrain>( + std::make_tuple(2, 4)); + serializer_test>, SimplexConstrain>( + std::make_tuple(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)); -} +template +struct OrderedConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_ordered(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_ordered(args...); + }; + } +}; TEST(serializer_vectorized, write_free_ordered) { - write_free_ordered_test(4); - write_free_ordered_test>(2, 4); - write_free_ordered_test>>(3, 2, 4); + using stan::test::serializer_test; + serializer_test(std::make_tuple(4)); + serializer_test, OrderedConstrain>( + std::make_tuple(2, 4)); + serializer_test>, OrderedConstrain>( + std::make_tuple(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)); -} +template +struct PositiveOrderedConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_positive_ordered(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_positive_ordered(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test( + std::make_tuple(4)); + serializer_test, PositiveOrderedConstrain>( + std::make_tuple(2, 4)); + serializer_test>, + PositiveOrderedConstrain>(std::make_tuple(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)); -} +template +struct CholFacCovConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_cholesky_factor_cov( + args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_cholesky_factor_cov(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test(std::make_tuple(4, 3)); + serializer_test, CholFacCovConstrain>( + std::make_tuple(2, 4, 3)); + serializer_test>, + CholFacCovConstrain>(std::make_tuple(3, 2, 4, 3)); + + serializer_test(std::make_tuple(2, 2)); + serializer_test, CholFacCovConstrain>( + std::make_tuple(2, 2, 2)); + serializer_test>, + CholFacCovConstrain>(std::make_tuple(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)); -} +template +struct CholFacCorrConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_cholesky_factor_corr( + args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_cholesky_factor_corr(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test(std::make_tuple(2)); + serializer_test, CholFacCorrConstrain>( + std::make_tuple(2, 2)); + serializer_test>, + CholFacCorrConstrain>(std::make_tuple(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)); -} +template +struct CovMatConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_cov_matrix(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serializer, auto&&... args) { + return serializer.write_free_cov_matrix(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test(std::make_tuple(2)); + serializer_test, CovMatConstrain>( + std::make_tuple(2, 2)); + serializer_test>, CovMatConstrain>( + std::make_tuple(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)); -} +template +struct CorrMatConstrain { + static auto read() { + return [](stan::io::deserializer& deserializer, auto&&... args) { + return deserializer.read_constrain_corr_matrix(args...); + }; + } + static auto free() { + return [](stan::io::serializer& serial, auto&&... args) { + return serial.write_free_corr_matrix(args...); + }; + } +}; 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); + using stan::test::serializer_test; + serializer_test(std::make_tuple(2)); + serializer_test, CorrMatConstrain>( + std::make_tuple(2, 2)); + serializer_test>, CorrMatConstrain>( + std::make_tuple(3, 2, 2)); +} + +// stochastic_column +template +struct StochasticCol { + static auto read() { + return [](stan::io::deserializer& deserializer, auto& lp, + auto... sizes) { + return deserializer.read_constrain_stochastic_column( + lp, sizes...); + }; + } + static auto free() { + return [](stan::io::serializer& serializer, auto&&... args) { + return serializer.write_free_stochastic_column(args...); + }; + } +}; +TEST(deserializer_vector, read_stochastic_column_matrix) { + using stan::test::serializer_test; + serializer_test(std::make_tuple(3, 3)); + serializer_test, StochasticCol>( + std::make_tuple(2, 3, 3)); + serializer_test>, StochasticCol>( + std::make_tuple(3, 2, 3, 3)); +} + +template +struct StochasticRow { + static auto read() { + return [](stan::io::deserializer& deserializer, auto& lp, + auto... sizes) { + return deserializer.read_constrain_stochastic_row(lp, + sizes...); + }; + } + static auto free() { + return [](stan::io::serializer& serializer, auto&&... args) { + return serializer.write_free_stochastic_row(args...); + }; + } +}; +TEST(deserializer_vector, read_stochastic_row_matrix) { + using stan::test::serializer_test; + serializer_test(std::make_tuple(3, 3)); + serializer_test, StochasticRow>( + std::make_tuple(2, 3, 3)); + serializer_test>, StochasticRow>( + std::make_tuple(3, 2, 3, 3)); }