Skip to content

Commit 3505018

Browse files
committed
Merge commit '490c8b5b802e27389d4ba5339dab51b09d517b6c' into HEAD
2 parents 7724011 + 490c8b5 commit 3505018

60 files changed

Lines changed: 6813 additions & 2090 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
python runTests.py -j4 ${{ matrix.config.tests }}
7272
7373
- name: Upload gtest_output xml
74-
uses: actions/upload-artifact@v6
74+
uses: actions/upload-artifact@v7
7575
if: failure()
7676
with:
7777
name: gtest_outputs_xml
@@ -134,7 +134,7 @@ jobs:
134134
python runTests.py -j2 $MixFunProbTestsArray[(${{ matrix.group }} - 1)]
135135
136136
- name: Upload gtest_output xml
137-
uses: actions/upload-artifact@v6
137+
uses: actions/upload-artifact@v7
138138
if: failure()
139139
with:
140140
name: gtest_outputs_xml

Jenkinsfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ pipeline {
223223
steps {
224224
retry(3) { checkout scm }
225225

226-
sh "echo CXXFLAGS += -fsanitize=address >> make/local"
226+
sh "echo O=3 >> make/local"
227+
sh "echo CXXFLAGS+=-march=native -mtune=native >> make/local"
227228
sh "./runTests.py -j${PARALLEL} --changed --debug"
228229

229230
}

doxygen/doxygen.cfg

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,15 +2743,16 @@ MSCGEN_TOOL =
27432743
MSCFILE_DIRS =
27442744

27452745
ALIASES += laplace_options="\
2746-
\param[in] theta_0 the initial guess for the Laplace approximation. \
2747-
\param[in] tolerance controls the convergence criterion when finding the mode in the Laplace approximation. \
2748-
\param[in] max_num_steps maximum number of steps before the Newton solver breaks and returns an error. \
2749-
\param[in] hessian_block_size Block size of Hessian of log likelihood w.r.t latent Gaussian variable theta. \
2750-
\param[in] solver Type of Newton solver. Each corresponds to a distinct choice of B matrix (i.e. application SWM formula): \
2751-
1. computes square-root of negative Hessian. \
2752-
2. computes square-root of covariance matrix. \
2753-
3. computes no square-root and uses LU decomposition. \
2754-
\param[in] max_steps_line_search Number of steps after which the algorithm gives up on doing a line search. If 0, no linesearch. \
2746+
\param[in] ops Options for controlling Laplace approximation. The following options are available: \
2747+
- theta_0 the initial guess for the Laplace approximation. \
2748+
- tolerance controls the convergence criterion when finding the mode in the Laplace approximation. \
2749+
- max_num_steps maximum number of steps before the Newton solver breaks and returns an error. \
2750+
- solver Type of Newton solver. Each corresponds to a distinct choice of B matrix (i.e. application SWM formula): \
2751+
1. computes square-root of negative Hessian. \
2752+
2. computes square-root of covariance matrix. \
2753+
3. computes no square-root and uses LU decomposition. \
2754+
- max_steps_line_search Number of steps after which the algorithm gives up on doing a line search. If 0, no linesearch. \
2755+
- allow_fallthrough If true, if solver 1 fails then solver 2 is tried, and if that fails solver 3 is tried. \
27552756
"
27562757

27572758
ALIASES += laplace_common_template_args="\
@@ -2761,6 +2762,7 @@ ALIASES += laplace_common_template_args="\
27612762
should be a type inheriting from `Eigen::EigenBase` with dynamic sized \
27622763
rows and columns. \
27632764
\tparam CovarArgs A tuple of types to passed as the first arguments of `CovarFun::operator()`\
2765+
\tparam OpsTuple A tuple of laplace_options types \
27642766
"
27652767

27662768
ALIASES += laplace_common_args="\
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#ifndef STAN_MATH_MIX_FUNCTOR_BARZILAI_BORWEIN_STEP_SIZE_HPP
2+
#define STAN_MATH_MIX_FUNCTOR_BARZILAI_BORWEIN_STEP_SIZE_HPP
3+
#include <stan/math/prim/fun/Eigen.hpp>
4+
#include <algorithm>
5+
#include <numeric>
6+
#include <cmath>
7+
8+
namespace stan::math::internal {
9+
/**
10+
* @brief Curvature-aware Barzilai–Borwein (BB) step length with robust
11+
* safeguards.
12+
*
13+
* Given successive parameter displacements \f$s = x_k - x_{k-1}\f$ and
14+
* gradients \f$g_k\f$, \f$g_{k-1}\f$, this routine forms
15+
* \f$y = g_k - g_{k-1}\f$ and computes the two classical BB candidates
16+
*
17+
* \f{align*}{
18+
* \alpha_{\text{BB1}} &= \frac{\langle s,s\rangle}{\langle s,y\rangle},\\
19+
* \alpha_{\text{BB2}} &= \frac{\langle s,y\rangle}{\langle y,y\rangle},
20+
* \f}
21+
*
22+
* then chooses between them using the **spectral cosine**
23+
* \f$r = \cos^2\!\angle(s,y) = \dfrac{\langle s,y\rangle^2}
24+
* {\langle s,s\rangle\,\langle
25+
* y,y\rangle}\in[0,1]\f$:
26+
*
27+
* - if \f$r > 0.9\f$ (well-aligned curvature) and the previous line search
28+
* did **≤ 1** backtrack, prefer the “long” step \f$\alpha_{\text{BB1}}\f$;
29+
* - if \f$0.1 \le r \le 0.9\f$, take the neutral geometric mean
30+
* \f$\sqrt{\alpha_{\text{BB1}}\alpha_{\text{BB2}}}\f$;
31+
* - otherwise default to the “short” step \f$\alpha_{\text{BB2}}\f$.
32+
*
33+
* All candidates are clamped into \f$[\text{min\_alpha},\,\text{max\_alpha}]\f$
34+
* and must be finite and positive.
35+
* If the curvature scalars are ill-posed (non-finite or too small),
36+
* \f$\langle s,y\rangle \le \varepsilon\f$, or if `last_backtracks == 99`
37+
* (explicitly disabling BB for this iteration), the function falls back to a
38+
* **safe** step:
39+
* use `prev_step` when finite and positive, otherwise \f$1.0\f$, then clamp to
40+
* \f$[\text{min\_alpha},\,\text{max\_alpha}]\f$.
41+
*
42+
* @param s Displacement between consecutive iterates
43+
* (\f$s = x_k - x_{k-1}\f$).
44+
* @param g_curr Gradient at the current iterate \f$g_k\f$.
45+
* @param g_prev Gradient at the previous iterate \f$g_{k-1}\f$.
46+
* @param prev_step Previously accepted step length (used by the fallback).
47+
* @param last_backtracks
48+
* Number of backtracking contractions performed by the most
49+
* recent line search; set to 99 to force the safe fallback.
50+
* @param min_alpha Lower bound for the returned step length.
51+
* @param max_alpha Upper bound for the returned step length.
52+
*
53+
* @return A finite, positive BB-style step length \f$\alpha \in
54+
* [\text{min\_alpha},\,\text{max\_alpha}]\f$ suitable for seeding a
55+
* line search or as a spectral preconditioner scale.
56+
*
57+
* @note Uses \f$\varepsilon=10^{-16}\f$ to guard against division by very
58+
* small curvature terms, and applies `std::abs` to BB ratios to avoid
59+
* negative steps; descent is enforced by the line search.
60+
* @warning The vectors must have identical size. Non-finite inputs yield the
61+
* safe fallback.
62+
*/
63+
inline double barzilai_borwein_step_size(const Eigen::VectorXd& s,
64+
const Eigen::VectorXd& g_curr,
65+
const Eigen::VectorXd& g_prev,
66+
double prev_step, int last_backtracks,
67+
double min_alpha, double max_alpha) {
68+
// Fallbacks
69+
auto safe_fallback = [&]() -> double {
70+
double a = std::clamp(
71+
prev_step > 0.0 && std::isfinite(prev_step) ? prev_step : 1.0,
72+
min_alpha, max_alpha);
73+
return a;
74+
};
75+
76+
const Eigen::VectorXd y = g_curr - g_prev;
77+
const double sty = s.dot(y);
78+
const double sts = s.squaredNorm();
79+
const double yty = y.squaredNorm();
80+
81+
// Basic validity checks
82+
constexpr double eps = 1e-16;
83+
if (!(std::isfinite(sty) && std::isfinite(sts) && std::isfinite(yty))
84+
|| sts <= eps || yty <= eps || sty <= eps || last_backtracks == -1) {
85+
return safe_fallback();
86+
}
87+
88+
// BB candidates
89+
double alpha_bb1 = std::clamp(std::abs(sts / sty), min_alpha, max_alpha);
90+
double alpha_bb2 = std::clamp(std::abs(sty / yty), min_alpha, max_alpha);
91+
92+
// Safeguard candidates
93+
if (!std::isfinite(alpha_bb1) || !std::isfinite(alpha_bb2) || alpha_bb1 <= 0.0
94+
|| alpha_bb2 <= 0.0) {
95+
return safe_fallback();
96+
}
97+
98+
// Spectral cosine r = cos^2(angle(s, y)) in [0,1]
99+
const double r = (sty * sty) / (sts * yty);
100+
101+
// Heuristic thresholds (robust defaults)
102+
constexpr double kLoose = 0.9; // "nice" curvature
103+
constexpr double kTight = 0.1; // "dodgy" curvature
104+
105+
double alpha0 = alpha_bb2; // default to short BB for robustness
106+
if (r > kLoose && last_backtracks <= 1) {
107+
// Spectrum looks friendly and line search was not harsh -> try long BB
108+
alpha0 = alpha_bb1;
109+
} else if (r >= kTight && r <= kLoose) {
110+
// Neither clearly friendly nor clearly dodgy -> neutral middle
111+
alpha0 = std::sqrt(alpha_bb1 * alpha_bb2);
112+
} // else keep alpha_bb2
113+
114+
// Clip to user bounds
115+
alpha0 = std::clamp(alpha0, min_alpha, max_alpha);
116+
117+
if (!std::isfinite(alpha0) || alpha0 <= 0.0) {
118+
return safe_fallback();
119+
}
120+
return alpha0;
121+
}
122+
123+
} // namespace stan::math::internal
124+
#endif
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#ifndef STAN_MATH_MIX_FUNCTOR_CONDITIONAL_COPY_AND_PROMOTE_HPP
2+
#define STAN_MATH_MIX_FUNCTOR_CONDITIONAL_COPY_AND_PROMOTE_HPP
3+
4+
#include <stan/math/mix/functor/hessian_block_diag.hpp>
5+
#include <stan/math/prim/functor.hpp>
6+
#include <stan/math/prim/fun.hpp>
7+
8+
namespace stan::math::internal {
9+
10+
/**
11+
* Decide if object should be deep or shallow copied when
12+
* using @ref conditional_copy_and_promote .
13+
*/
14+
enum class COPY_TYPE : uint8_t { SHALLOW = 0, DEEP = 1 };
15+
16+
/**
17+
* Conditional copy and promote a type's scalar type to a `PromotedType`.
18+
* @tparam Filter type trait with a static constexpr bool member `value`
19+
* that is true if the type should be promoted. Otherwise, the type is
20+
* left unchanged.
21+
* @tparam PromotedType type to promote the scalar to.
22+
* @tparam CopyType type of copy to perform.
23+
* @tparam Args variadic arguments.
24+
* @param args variadic arguments to conditionally copy and promote.
25+
* @return a tuple where each element is either a reference to the original
26+
* argument or a promoted copy of the argument.
27+
*/
28+
template <template <typename...> class Filter,
29+
typename PromotedType = stan::math::var,
30+
COPY_TYPE CopyType = COPY_TYPE::DEEP, typename... Args>
31+
inline auto conditional_copy_and_promote(Args&&... args) {
32+
return map_if<Filter>(
33+
[](auto&& arg) {
34+
if constexpr (is_tuple_v<decltype(arg)>) {
35+
return stan::math::apply(
36+
[](auto&&... inner_args) {
37+
return make_holder_tuple(
38+
conditional_copy_and_promote<Filter, PromotedType,
39+
CopyType>(
40+
std::forward<decltype(inner_args)>(inner_args))...);
41+
},
42+
std::forward<decltype(arg)>(arg));
43+
} else if constexpr (is_std_vector_v<decltype(arg)>) {
44+
std::vector<decltype(conditional_copy_and_promote<
45+
Filter, PromotedType, CopyType>(arg[0]))>
46+
ret;
47+
for (std::size_t i = 0; i < arg.size(); ++i) {
48+
ret.push_back(
49+
conditional_copy_and_promote<Filter, PromotedType, CopyType>(
50+
arg[i]));
51+
}
52+
return ret;
53+
} else {
54+
if constexpr (CopyType == COPY_TYPE::DEEP) {
55+
return stan::math::eval(promote_scalar<PromotedType>(
56+
value_of_rec(std::forward<decltype(arg)>(arg))));
57+
} else if (CopyType == COPY_TYPE::SHALLOW) {
58+
if constexpr (std::is_same_v<PromotedType,
59+
scalar_type_t<decltype(arg)>>) {
60+
return std::forward<decltype(arg)>(arg);
61+
} else {
62+
return stan::math::eval(promote_scalar<PromotedType>(
63+
std::forward<decltype(arg)>(arg)));
64+
}
65+
}
66+
}
67+
},
68+
std::forward<Args>(args)...);
69+
}
70+
71+
/**
72+
* Conditional deep copy types with a `var` scalar type to `PromotedType`.
73+
* @tparam PromotedType type to promote the scalar to.
74+
* @tparam Args variadic arguments.
75+
* @param args variadic arguments to conditionally copy and promote.
76+
* @return a tuple where each element is either a reference to the original
77+
* argument or a promoted copy of the argument.
78+
*/
79+
template <typename PromotedType, typename... Args>
80+
inline auto deep_copy_vargs(Args&&... args) {
81+
return conditional_copy_and_promote<is_any_var_scalar, PromotedType,
82+
COPY_TYPE::DEEP>(
83+
std::forward<Args>(args)...);
84+
}
85+
86+
/**
87+
* Conditional shallow copy types with a `var` scalar type to `PromotedType`.
88+
* @note This function is useful whenever you are inside of nested autodiff
89+
* and want to allow the input arguments from an outer autodiff to be used
90+
* in an inner autodiff without making a hard copy of the input arguments.
91+
* @tparam PromotedType type to promote the scalar to.
92+
* @tparam Args variadic arguments.
93+
* @param args variadic arguments to conditionally copy and promote.
94+
* @return a tuple where each element is either a reference to the original
95+
* argument or a promoted copy of the argument.
96+
*/
97+
template <typename PromotedType, typename... Args>
98+
inline auto shallow_copy_vargs(Args&&... args) {
99+
return conditional_copy_and_promote<is_any_var_scalar, PromotedType,
100+
COPY_TYPE::SHALLOW>(
101+
std::forward<Args>(args)...);
102+
}
103+
104+
} // namespace stan::math::internal
105+
106+
#endif

stan/math/mix/functor/laplace_base_rng.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ inline Eigen::VectorXd laplace_base_rng(
3838
LLFunc&& ll_fun, LLArgs&& ll_args, CovarFun&& covariance_function,
3939
CovarArgs&& covar_args, const laplace_options<InitTheta>& options, RNG& rng,
4040
std::ostream* msgs) {
41+
Eigen::MatrixXd covariance_train = stan::math::apply(
42+
[msgs, &covariance_function](auto&&... args) {
43+
return covariance_function(std::forward<decltype(args)>(args)..., msgs);
44+
},
45+
std::forward<CovarArgs>(covar_args));
4146
auto md_est = internal::laplace_marginal_density_est(
42-
ll_fun, std::forward<LLArgs>(ll_args),
43-
std::forward<CovarFun>(covariance_function),
44-
to_ref(std::forward<CovarArgs>(covar_args)), options, msgs);
45-
// Modified R&W method
46-
auto&& covariance_train = md_est.covariance;
47+
ll_fun, std::forward<LLArgs>(ll_args), covariance_train, options, msgs);
4748
Eigen::VectorXd mean_train = covariance_train * md_est.theta_grad;
4849
if (options.solver == 1 || options.solver == 2) {
4950
Eigen::MatrixXd V_dec

0 commit comments

Comments
 (0)