Skip to content

Commit fd52669

Browse files
committed
Merge commit '726f9914af03df2b743f4541a4d0dae243ef267b' into HEAD
2 parents fed8db5 + 726f991 commit fd52669

23 files changed

Lines changed: 784 additions & 33 deletions

Jenkinsfile

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -592,15 +592,14 @@ pipeline {
592592
sh """#!/bin/bash
593593
set -x
594594
make doxygen
595+
cd doc/api/html
596+
git init
597+
git checkout -b gh-pages
595598
git config user.email "mc.stanislaw@gmail.com"
596599
git config user.name "Stan Jenkins"
597-
git checkout --detach
598-
git branch -D gh-pages
599-
git push https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/stan-dev/math.git :gh-pages
600-
git checkout --orphan gh-pages
601-
git add -f doc
600+
git add -f .
602601
git commit --author='Stan BuildBot <mc.stanislaw@gmail.com>' -m "auto generated docs from Jenkins"
603-
git subtree push --prefix doc/api/html https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/stan-dev/math.git gh-pages
602+
git push https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/stan-dev/math.git gh-pages --force
604603
"""
605604
}
606605
}

stan/math/fwd/fun.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
#include <stan/math/fwd/fun/tcrossprod.hpp>
117117
#include <stan/math/fwd/fun/tgamma.hpp>
118118
#include <stan/math/fwd/fun/to_fvar.hpp>
119+
#include <stan/math/fwd/fun/trace_dot.hpp>
119120
#include <stan/math/fwd/fun/trace_quad_form.hpp>
120121
#include <stan/math/fwd/fun/trigamma.hpp>
121122
#include <stan/math/fwd/fun/trunc.hpp>

stan/math/fwd/fun/trace_dot.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#ifndef STAN_MATH_FWD_FUN_TRACE_DOT_HPP
2+
#define STAN_MATH_FWD_FUN_TRACE_DOT_HPP
3+
4+
#include <stan/math/fwd/core.hpp>
5+
#include <stan/math/fwd/fun/multiply.hpp>
6+
#include <stan/math/prim/err.hpp>
7+
#include <stan/math/prim/fun/trace.hpp>
8+
9+
namespace stan {
10+
namespace math {
11+
12+
/**
13+
* Compute the trace of the product of two matrices with
14+
* forward-mode autodiff support.
15+
*
16+
* @tparam EigMat1 A type either inheriting from `Eigen::DenseBase` or a
17+
* `var_value` with an inner type inheriting from `Eigen::DenseBase`
18+
* @tparam EigMat2 A type either inheriting from `Eigen::DenseBase` or a
19+
* `var_value` with an inner type inheriting from `Eigen::DenseBase`
20+
*
21+
* @param A first matrix (m x n)
22+
* @param B second matrix (n x m)
23+
* @return trace of A * B
24+
* @throw std::invalid_argument if A and B have incompatible dimensions
25+
*/
26+
template <typename EigMat1, typename EigMat2,
27+
require_all_eigen_t<EigMat1, EigMat2>* = nullptr,
28+
require_any_vt_fvar<EigMat1, EigMat2>* = nullptr>
29+
inline return_type_t<EigMat1, EigMat2> trace_dot(EigMat1&& A, EigMat2&& B) {
30+
check_size_match("trace_dot", "A.cols()", A.cols(), "B.rows()", B.rows());
31+
check_size_match("trace_dot", "A.rows()", A.rows(), "B.cols()", B.cols());
32+
return trace(multiply(std::forward<EigMat1>(A), std::forward<EigMat2>(B)));
33+
}
34+
35+
} // namespace math
36+
} // namespace stan
37+
#endif

stan/math/fwd/prob.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
#include <stan/math/fwd/fun/Eigen_NumTraits.hpp>
66

77
#include <stan/math/fwd/prob/std_normal_log_qf.hpp>
8+
#include <stan/math/fwd/prob/student_t_qf.hpp>
89

910
#endif
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#ifndef STAN_MATH_FWD_PROB_STUDENT_T_QF_HPP
2+
#define STAN_MATH_FWD_PROB_STUDENT_T_QF_HPP
3+
4+
#include <stan/math/fwd/meta.hpp>
5+
#include <stan/math/fwd/fun/digamma.hpp>
6+
#include <stan/math/fwd/fun/exp.hpp>
7+
#include <stan/math/fwd/fun/hypergeometric_2F1.hpp>
8+
#include <stan/math/fwd/fun/hypergeometric_pFq.hpp>
9+
#include <stan/math/fwd/fun/inv_inc_beta.hpp>
10+
#include <stan/math/fwd/fun/log.hpp>
11+
#include <stan/math/fwd/fun/sqrt.hpp>
12+
#include <stan/math/fwd/fun/value_of.hpp>
13+
#include <stan/math/fwd/fun/value_of_rec.hpp>
14+
#include <stan/math/prim/meta.hpp>
15+
#include <stan/math/prim/prob/student_t_lpdf.hpp>
16+
17+
namespace stan {
18+
namespace math {
19+
20+
template <typename T_p, typename T_nu, typename T_mu, typename T_sigma,
21+
require_all_stan_scalar_t<T_p, T_mu, T_sigma, T_nu>* = nullptr,
22+
require_any_fvar_t<T_p, T_nu, T_mu, T_sigma>* = nullptr>
23+
inline auto student_t_qf(const T_p& p, const T_nu& nu, const T_mu& mu,
24+
const T_sigma& sigma) {
25+
static constexpr const char* function = "student_t_qf";
26+
using FvarT = return_type_t<T_p, T_mu, T_sigma, T_nu>;
27+
using T_partials = partials_type_t<FvarT>;
28+
29+
auto p_val = value_of(p);
30+
auto nu_val = value_of(nu);
31+
auto mu_val = value_of(mu);
32+
auto sigma_val = value_of(sigma);
33+
34+
check_nonnegative(function, "Degrees of freedom parameter", nu_val);
35+
check_positive(function, "Scale parameter", sigma_val);
36+
check_bounded(function, "Probability parameter", p_val, 0.0, 1.0);
37+
38+
if (unlikely(p_val == 0.0)) {
39+
return FvarT{NEGATIVE_INFTY, 0.0};
40+
} else if (unlikely(p_val == 1.0)) {
41+
return FvarT{INFTY, 0.0};
42+
} else if (unlikely(p_val == 0.5)) {
43+
return FvarT{mu_val, 0.0};
44+
}
45+
46+
const auto p_val_flip = p_val < 0.5 ? p_val : 1.0 - p_val;
47+
const double p_sign = value_of_rec(p_val) < 0.5 ? -1.0 : 1.0;
48+
auto sqrt_nu_val = sqrt(nu_val);
49+
auto ibeta_arg = inv_inc_beta(0.5 * nu_val, 0.5, 2.0 * p_val_flip);
50+
auto rtn_val
51+
= mu_val
52+
+ p_sign * sigma_val * sqrt_nu_val * sqrt(-1.0 + 1.0 / ibeta_arg);
53+
54+
FvarT rtn(rtn_val, 0.0);
55+
56+
if constexpr (is_autodiff_v<T_p>) {
57+
rtn.d_ += p.d_ * exp(-student_t_lpdf(rtn_val, nu_val, mu_val, sigma_val));
58+
}
59+
60+
if constexpr (is_autodiff_v<T_nu>) {
61+
const auto half_nu = nu_val / 2.0;
62+
Eigen::Matrix<T_partials, -1, 1> hyper_arg_a{{0.5, half_nu, half_nu}};
63+
Eigen::Matrix<T_partials, -1, 1> hyper_arg_b{
64+
{1.0 + half_nu, 1.0 + half_nu}};
65+
const auto hyper_arg
66+
= hypergeometric_pFq(hyper_arg_a, hyper_arg_b, ibeta_arg);
67+
const auto hyper2f1 = hypergeometric_2F1(1.0, (1.0 + nu_val) / 2.0,
68+
(2.0 + nu_val) / 2.0, ibeta_arg);
69+
const auto digamma_a1 = digamma(half_nu);
70+
const auto digamma_a2 = digamma((1.0 + nu_val) / 2.0);
71+
72+
const auto arg_1 = (4.0 * hyper_arg * sqrt(1.0 - ibeta_arg)) / nu_val;
73+
const auto arg_2 = -2.0 * hyper2f1 * (-1.0 + ibeta_arg)
74+
* (log(ibeta_arg) - digamma_a1 + digamma_a2);
75+
76+
const auto num1 = sigma_val * (-2.0 + (2.0 - arg_1 + arg_2) / ibeta_arg);
77+
const auto den1 = 4.0 * sqrt_nu_val * sqrt(-1.0 + 1.0 / ibeta_arg);
78+
rtn.d_ += nu.d_ * p_sign * num1 / den1;
79+
}
80+
81+
if constexpr (is_autodiff_v<T_mu>) {
82+
rtn.d_ += mu.d_;
83+
}
84+
85+
if constexpr (is_autodiff_v<T_sigma>) {
86+
rtn.d_ += sigma.d_ * p_sign * sqrt_nu_val * sqrt(-1.0 + 1.0 / ibeta_arg);
87+
}
88+
89+
return rtn;
90+
}
91+
} // namespace math
92+
} // namespace stan
93+
94+
#endif

stan/math/prim/fun.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@
328328
#include <stan/math/prim/fun/to_row_vector.hpp>
329329
#include <stan/math/prim/fun/to_vector.hpp>
330330
#include <stan/math/prim/fun/trace.hpp>
331+
#include <stan/math/prim/fun/trace_dot.hpp>
331332
#include <stan/math/prim/fun/trace_gen_inv_quad_form_ldlt.hpp>
332333
#include <stan/math/prim/fun/trace_gen_quad_form.hpp>
333334
#include <stan/math/prim/fun/trace_inv_quad_form_ldlt.hpp>

stan/math/prim/fun/hypergeometric_pFq.hpp

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stan/math/prim/meta.hpp>
55
#include <stan/math/prim/err/check_not_nan.hpp>
66
#include <stan/math/prim/err/check_finite.hpp>
7+
#include <stan/math/prim/fun/to_ref.hpp>
78
#include <stan/math/prim/fun/to_row_vector.hpp>
89
#include <boost/math/special_functions/hypergeometric_pFq.hpp>
910

@@ -25,34 +26,53 @@ namespace math {
2526
template <typename Ta, typename Tb, typename Tz,
2627
require_all_vector_st<std::is_arithmetic, Ta, Tb>* = nullptr,
2728
require_arithmetic_t<Tz>* = nullptr>
28-
inline return_type_t<Ta, Tb, Tz> hypergeometric_pFq(const Ta& a, const Tb& b,
29-
const Tz& z) {
30-
plain_type_t<Ta> a_ref = a;
31-
plain_type_t<Tb> b_ref = b;
29+
inline return_type_t<Ta, Tb, Tz> hypergeometric_pFq(Ta&& a, Tb&& b, Tz&& z) {
30+
decltype(auto) a_ref = to_ref(std::forward<Ta>(a));
31+
decltype(auto) b_ref = to_ref(std::forward<Tb>(b));
3232
check_finite("hypergeometric_pFq", "a", a_ref);
3333
check_finite("hypergeometric_pFq", "b", b_ref);
3434
check_finite("hypergeometric_pFq", "z", z);
35-
3635
check_not_nan("hypergeometric_pFq", "a", a_ref);
3736
check_not_nan("hypergeometric_pFq", "b", b_ref);
3837
check_not_nan("hypergeometric_pFq", "z", z);
3938

40-
bool condition_1 = (a_ref.size() > (b_ref.size() + 1)) && (z != 0);
41-
bool condition_2 = (a_ref.size() == (b_ref.size() + 1)) && (std::fabs(z) > 1);
39+
const bool condition_1 = (a_ref.size() > (b_ref.size() + 1)) && (z != 0);
40+
const bool condition_2
41+
= (a_ref.size() == (b_ref.size() + 1)) && (std::fabs(z) > 1);
4242

4343
if (condition_1 || condition_2) {
44-
std::stringstream msg;
45-
msg << "hypergeometric function pFq does not meet convergence "
46-
<< "conditions with given arguments. "
47-
<< "a: " << to_row_vector(a_ref) << ", "
48-
<< "b: " << to_row_vector(b_ref) << ", "
49-
<< "z: " << z;
50-
throw std::domain_error(msg.str());
44+
[&]() STAN_COLD_PATH {
45+
std::stringstream msg;
46+
msg << "hypergeometric function pFq does not meet convergence "
47+
"conditions with given arguments. "
48+
"a: "
49+
<< to_row_vector(a_ref) << ", "
50+
<< "b: " << to_row_vector(b_ref) << ", "
51+
<< "z: " << z;
52+
throw std::domain_error(msg.str());
53+
}();
54+
}
55+
// For plain vectors, we can use Eigen's Map to avoid unnecessary copies
56+
using a_ref_t = decltype(a_ref);
57+
using b_ref_t = decltype(b_ref);
58+
constexpr bool is_a_plain_vec
59+
= std::is_same_v<std::decay_t<a_ref_t>, plain_type_t<a_ref_t>>;
60+
constexpr bool is_b_plain_vec
61+
= std::is_same_v<std::decay_t<b_ref_t>, plain_type_t<b_ref_t>>;
62+
if constexpr (is_a_plain_vec && is_b_plain_vec) {
63+
// We use type erasure not do a hard copy here
64+
using map_t = Eigen::Map<Eigen::VectorXd>;
65+
auto map_a = map_t(const_cast<double*>(a_ref.data()), a_ref.size());
66+
auto map_b = map_t(const_cast<double*>(b_ref.data()), b_ref.size());
67+
return boost::math::hypergeometric_pFq(map_a, map_b, z);
68+
} else {
69+
// We need pointers to `a` and `b`'s data here so we hard evaluate.
70+
decltype(auto) a_eval = eval(a_ref);
71+
decltype(auto) b_eval = eval(b_ref);
72+
return boost::math::hypergeometric_pFq(
73+
std::vector<double>(a_eval.data(), a_eval.data() + a_eval.size()),
74+
std::vector<double>(b_eval.data(), b_eval.data() + b_eval.size()), z);
5175
}
52-
53-
return boost::math::hypergeometric_pFq(
54-
std::vector<double>(a_ref.data(), a_ref.data() + a_ref.size()),
55-
std::vector<double>(b_ref.data(), b_ref.data() + b_ref.size()), z);
5676
}
5777
} // namespace math
5878
} // namespace stan

stan/math/prim/fun/trace.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ namespace math {
1919
*/
2020
template <typename T, require_eigen_t<T>* = nullptr,
2121
require_not_st_var<T>* = nullptr>
22-
inline value_type_t<T> trace(const T& m) {
23-
return m.trace();
22+
inline auto trace(T&& m) {
23+
return make_holder([](auto&& m_) { return m_.trace(); }, std::forward<T>(m));
2424
}
2525

2626
} // namespace math

stan/math/prim/fun/trace_dot.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef STAN_MATH_PRIM_FUN_TRACE_DOT_HPP
2+
#define STAN_MATH_PRIM_FUN_TRACE_DOT_HPP
3+
4+
#include <stan/math/prim/meta.hpp>
5+
#include <stan/math/prim/err.hpp>
6+
#include <stan/math/prim/fun/Eigen.hpp>
7+
8+
namespace stan {
9+
namespace math {
10+
11+
/**
12+
* Compute the trace of the product of two matrices,
13+
* \f$ \text{tr}(A \cdot B) = \sum_{i,j} A_{ij} B_{ji} \f$.
14+
*
15+
* This is more efficient than computing the full product and
16+
* taking the trace, as it avoids forming the intermediate matrix.
17+
*
18+
* @tparam EigMat1 A type either inheriting from `Eigen::DenseBase` or a
19+
* `var_value` with an inner type inheriting from `Eigen::DenseBase`
20+
* @tparam EigMat2 A type either inheriting from `Eigen::DenseBase` or a
21+
* `var_value` with an inner type inheriting from `Eigen::DenseBase`
22+
*
23+
* @param A first matrix (m x n)
24+
* @param B second matrix (n x m)
25+
* @return trace of A * B
26+
* @throw std::invalid_argument if A and B have incompatible dimensions
27+
*/
28+
template <typename EigMat1, typename EigMat2,
29+
require_all_eigen_vt<std::is_arithmetic, EigMat1, EigMat2>* = nullptr>
30+
inline auto trace_dot(EigMat1&& A, EigMat2&& B) {
31+
check_size_match("trace_dot", "A.cols()", A.cols(), "B.rows()", B.rows());
32+
check_size_match("trace_dot", "A.rows()", A.rows(), "B.cols()", B.cols());
33+
return make_holder(
34+
[](auto&& A_, auto&& B_) {
35+
return A_.cwiseProduct(B_.transpose()).sum();
36+
},
37+
std::forward<EigMat1>(A), std::forward<EigMat2>(B));
38+
}
39+
40+
} // namespace math
41+
} // namespace stan
42+
43+
#endif

stan/math/prim/meta.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include <stan/math/prim/meta/ad_promotable.hpp>
7272
#include <stan/math/prim/meta/append_return_type.hpp>
7373
#include <stan/math/prim/meta/base_type.hpp>
74+
#include <stan/math/prim/meta/common_container_type.hpp>
7475
#include <stan/math/prim/meta/contains_std_vector.hpp>
7576
#include <stan/math/prim/meta/contains_tuple.hpp>
7677
#include <stan/math/prim/meta/error_index.hpp>

0 commit comments

Comments
 (0)