From d6ded0777cbc1d4a745424d70d0b83d4a411972a Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 26 Nov 2024 09:21:31 -0500 Subject: [PATCH 1/3] Add failing test --- test/integration/good/code-gen/cpp.expected | 409 +----------------- .../code-gen/deprecated_jacobian_usage.stan | 12 + 2 files changed, 24 insertions(+), 397 deletions(-) diff --git a/test/integration/good/code-gen/cpp.expected b/test/integration/good/code-gen/cpp.expected index ea8d15dff..181873453 100644 --- a/test/integration/good/code-gen/cpp.expected +++ b/test/integration/good/code-gen/cpp.expected @@ -3925,403 +3925,18 @@ stan::math::profile_map& get_stan_profile_data() { #endif [exit 0] $ ../../../../../install/default/bin/stanc --print-cpp deprecated_jacobian_usage.stan -// Code generated by %%NAME%% %%VERSION%% -#include -namespace deprecated_jacobian_usage_model_namespace { -using stan::model::model_base_crtp; -using namespace stan::math; -stan::math::profile_map profiles__; -static constexpr std::array locations_array__ = - {" (found before start of program)", - " (in 'deprecated_jacobian_usage.stan', line 11, column 2 to column 20)", - " (in 'deprecated_jacobian_usage.stan', line 12, column 2 to column 21)", - " (in 'deprecated_jacobian_usage.stan', line 5, column 4 to column 22)", - " (in 'deprecated_jacobian_usage.stan', line 6, column 4 to column 18)", - " (in 'deprecated_jacobian_usage.stan', line 7, column 4 to column 20)", - " (in 'deprecated_jacobian_usage.stan', line 4, column 19 to line 8, column 3)"}; -template , - std::is_floating_point>>* = nullptr> -stan::promote_args_t foo(const T0__& x, std::ostream* pstream__); -// real foo(real) -template , - std::is_floating_point>>*> -stan::promote_args_t foo(const T0__& x, std::ostream* pstream__) { - using local_scalar_t__ = stan::promote_args_t; - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - static constexpr bool propto__ = true; - // suppress unused var warning - (void) propto__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - try { - local_scalar_t__ jacobian = DUMMY_VAR__; - current_statement__ = 3; - jacobian = 0; - current_statement__ = 4; - jacobian = (jacobian + x); - current_statement__ = 5; - return jacobian; - } catch (const std::exception& e) { - stan::lang::rethrow_located(e, locations_array__[current_statement__]); - } -} -class deprecated_jacobian_usage_model final : public model_base_crtp { - private: - - public: - ~deprecated_jacobian_usage_model() {} - deprecated_jacobian_usage_model(stan::io::var_context& context__, - unsigned int random_seed__ = 0, - std::ostream* pstream__ = nullptr) - : model_base_crtp(0) { - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - using local_scalar_t__ = double; - auto base_rng__ = stan::services::util::create_rng(random_seed__, 0); - // suppress unused var warning - (void) base_rng__; - static constexpr const char* function__ = - "deprecated_jacobian_usage_model_namespace::deprecated_jacobian_usage_model"; - // suppress unused var warning - (void) function__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - num_params_r__ = 0U; - } - inline std::string model_name() const final { - return "deprecated_jacobian_usage_model"; - } - inline std::vector model_compile_info() const noexcept { - return std::vector{"stanc_version = %%NAME%%3 %%VERSION%%", - "stancflags = --print-cpp"}; - } - // Base log prob - template * = nullptr, - stan::require_vector_like_vt* = nullptr, - stan::require_not_st_var* = nullptr> - inline stan::scalar_type_t - log_prob_impl(VecR& params_r__, VecI& params_i__, std::ostream* - pstream__ = nullptr) const { - using T__ = stan::scalar_type_t; - using local_scalar_t__ = T__; - T__ lp__(0.0); - stan::math::accumulator lp_accum__; - stan::io::deserializer in__(params_r__, params_i__); - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - static constexpr const char* function__ = - "deprecated_jacobian_usage_model_namespace::log_prob"; - // suppress unused var warning - (void) function__; - try { - local_scalar_t__ jacobian = DUMMY_VAR__; - current_statement__ = 1; - jacobian = 1; - current_statement__ = 2; - jacobian = (jacobian + foo(static_cast(1), pstream__)); - } catch (const std::exception& e) { - stan::lang::rethrow_located(e, locations_array__[current_statement__]); - } - lp_accum__.add(lp__); - return lp_accum__.sum(); - } - // Reverse mode autodiff log prob - template * = nullptr, - stan::require_vector_like_vt* = nullptr, - stan::require_st_var* = nullptr> - inline stan::scalar_type_t - log_prob_impl(VecR& params_r__, VecI& params_i__, std::ostream* - pstream__ = nullptr) const { - using T__ = stan::scalar_type_t; - using local_scalar_t__ = T__; - T__ lp__(0.0); - stan::math::accumulator lp_accum__; - stan::io::deserializer in__(params_r__, params_i__); - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - static constexpr const char* function__ = - "deprecated_jacobian_usage_model_namespace::log_prob"; - // suppress unused var warning - (void) function__; - try { - local_scalar_t__ jacobian = DUMMY_VAR__; - current_statement__ = 1; - jacobian = 1; - current_statement__ = 2; - jacobian = (jacobian + foo(static_cast(1), pstream__)); - } catch (const std::exception& e) { - stan::lang::rethrow_located(e, locations_array__[current_statement__]); - } - lp_accum__.add(lp__); - return lp_accum__.sum(); - } - template * = nullptr, stan::require_vector_like_vt* = nullptr, stan::require_vector_vt* = nullptr> - inline void - write_array_impl(RNG& base_rng__, VecR& params_r__, VecI& params_i__, - VecVar& vars__, const bool - emit_transformed_parameters__ = true, const bool - emit_generated_quantities__ = true, std::ostream* - pstream__ = nullptr) const { - using local_scalar_t__ = double; - stan::io::deserializer in__(params_r__, params_i__); - stan::io::serializer out__(vars__); - static constexpr bool propto__ = true; - // suppress unused var warning - (void) propto__; - double lp__ = 0.0; - // suppress unused var warning - (void) lp__; - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - stan::math::accumulator lp_accum__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - constexpr bool jacobian__ = false; - // suppress unused var warning - (void) jacobian__; - static constexpr const char* function__ = - "deprecated_jacobian_usage_model_namespace::write_array"; - // suppress unused var warning - (void) function__; - try { - double jacobian = std::numeric_limits::quiet_NaN(); - if (stan::math::logical_negation( - (stan::math::primitive_value(emit_transformed_parameters__) || - stan::math::primitive_value(emit_generated_quantities__)))) { - return ; - } - current_statement__ = 1; - jacobian = 1; - current_statement__ = 2; - jacobian = (jacobian + foo(static_cast(1), pstream__)); - if (emit_transformed_parameters__) { - out__.write(jacobian); - } - if (stan::math::logical_negation(emit_generated_quantities__)) { - return ; - } - } catch (const std::exception& e) { - stan::lang::rethrow_located(e, locations_array__[current_statement__]); - } - } - template * = nullptr, - stan::require_vector_like_vt* = nullptr> - inline void - unconstrain_array_impl(const VecVar& params_r__, const VecI& params_i__, - VecVar& vars__, std::ostream* pstream__ = nullptr) const { - using local_scalar_t__ = double; - stan::io::deserializer in__(params_r__, params_i__); - stan::io::serializer out__(vars__); - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - } - template * = nullptr> - inline void - transform_inits_impl(const stan::io::var_context& context__, VecVar& - vars__, std::ostream* pstream__ = nullptr) const { - using local_scalar_t__ = double; - stan::io::serializer out__(vars__); - int current_statement__ = 0; - // suppress unused var warning - (void) current_statement__; - local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); - // suppress unused var warning - (void) DUMMY_VAR__; - } - inline void - get_param_names(std::vector& names__, const bool - emit_transformed_parameters__ = true, const bool - emit_generated_quantities__ = true) const { - names__ = std::vector{}; - if (emit_transformed_parameters__) { - std::vector temp{"jacobian"}; - names__.reserve(names__.size() + temp.size()); - names__.insert(names__.end(), temp.begin(), temp.end()); - } - if (emit_generated_quantities__) {} - } - inline void - get_dims(std::vector>& dimss__, const bool - emit_transformed_parameters__ = true, const bool - emit_generated_quantities__ = true) const { - dimss__ = std::vector>{}; - if (emit_transformed_parameters__) { - std::vector> temp{std::vector{}}; - dimss__.reserve(dimss__.size() + temp.size()); - dimss__.insert(dimss__.end(), temp.begin(), temp.end()); - } - if (emit_generated_quantities__) {} - } - inline void - constrained_param_names(std::vector& param_names__, bool - emit_transformed_parameters__ = true, bool - emit_generated_quantities__ = true) const final { - if (emit_transformed_parameters__) { - param_names__.emplace_back(std::string() + "jacobian"); - } - if (emit_generated_quantities__) {} - } - inline void - unconstrained_param_names(std::vector& param_names__, bool - emit_transformed_parameters__ = true, bool - emit_generated_quantities__ = true) const final { - if (emit_transformed_parameters__) { - param_names__.emplace_back(std::string() + "jacobian"); - } - if (emit_generated_quantities__) {} - } - inline std::string get_constrained_sizedtypes() const { - return std::string("[{\"name\":\"jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"transformed_parameters\"}]"); - } - inline std::string get_unconstrained_sizedtypes() const { - return std::string("[{\"name\":\"jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"transformed_parameters\"}]"); - } - // Begin method overload boilerplate - template inline void - write_array(RNG& base_rng, Eigen::Matrix& params_r, - Eigen::Matrix& vars, const bool - emit_transformed_parameters = true, const bool - emit_generated_quantities = true, std::ostream* - pstream = nullptr) const { - const size_t num_params__ = 0; - const size_t num_transformed = emit_transformed_parameters * (1); - const size_t num_gen_quantities = emit_generated_quantities * (0); - const size_t num_to_write = num_params__ + num_transformed + - num_gen_quantities; - std::vector params_i; - vars = Eigen::Matrix::Constant(num_to_write, - std::numeric_limits::quiet_NaN()); - write_array_impl(base_rng, params_r, params_i, vars, - emit_transformed_parameters, emit_generated_quantities, pstream); - } - template inline void - write_array(RNG& base_rng, std::vector& params_r, std::vector& - params_i, std::vector& vars, bool - emit_transformed_parameters = true, bool - emit_generated_quantities = true, std::ostream* - pstream = nullptr) const { - const size_t num_params__ = 0; - const size_t num_transformed = emit_transformed_parameters * (1); - const size_t num_gen_quantities = emit_generated_quantities * (0); - const size_t num_to_write = num_params__ + num_transformed + - num_gen_quantities; - vars = std::vector(num_to_write, - std::numeric_limits::quiet_NaN()); - write_array_impl(base_rng, params_r, params_i, vars, - emit_transformed_parameters, emit_generated_quantities, pstream); - } - template inline T_ - log_prob(Eigen::Matrix& params_r, std::ostream* pstream = nullptr) const { - Eigen::Matrix params_i; - return log_prob_impl(params_r, params_i, pstream); - } - template inline T_ - log_prob(std::vector& params_r, std::vector& params_i, - std::ostream* pstream = nullptr) const { - return log_prob_impl(params_r, params_i, pstream); - } - inline void - transform_inits(const stan::io::var_context& context, - Eigen::Matrix& params_r, std::ostream* - pstream = nullptr) const final { - std::vector params_r_vec(params_r.size()); - std::vector params_i; - transform_inits(context, params_i, params_r_vec, pstream); - params_r = Eigen::Map>(params_r_vec.data(), - params_r_vec.size()); - } - inline void - transform_inits(const stan::io::var_context& context, std::vector& - params_i, std::vector& vars, std::ostream* - pstream__ = nullptr) const { - vars.resize(num_params_r__); - transform_inits_impl(context, vars, pstream__); - } - inline void - unconstrain_array(const std::vector& params_constrained, - std::vector& params_unconstrained, std::ostream* - pstream = nullptr) const { - const std::vector params_i; - params_unconstrained = std::vector(num_params_r__, - std::numeric_limits::quiet_NaN()); - unconstrain_array_impl(params_constrained, params_i, - params_unconstrained, pstream); - } - inline void - unconstrain_array(const Eigen::Matrix& params_constrained, - Eigen::Matrix& params_unconstrained, - std::ostream* pstream = nullptr) const { - const std::vector params_i; - params_unconstrained = Eigen::Matrix::Constant(num_params_r__, - std::numeric_limits::quiet_NaN()); - unconstrain_array_impl(params_constrained, params_i, - params_unconstrained, pstream); - } -}; -} -using stan_model = deprecated_jacobian_usage_model_namespace::deprecated_jacobian_usage_model; -#ifndef USING_R -// Boilerplate -stan::model::model_base& -new_model(stan::io::var_context& data_context, unsigned int seed, - std::ostream* msg_stream) { - stan_model* m = new stan_model(data_context, seed, msg_stream); - return *m; -} -stan::math::profile_map& get_stan_profile_data() { - return deprecated_jacobian_usage_model_namespace::profiles__; -} -#endif -Warning in 'deprecated_jacobian_usage.stan', line 5, column 9: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 5, column 9: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 6, column 4: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 7, column 11: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 11, column 7: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 11, column 7: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -Warning in 'deprecated_jacobian_usage.stan', line 12, column 2: Variable name - 'jacobian' will be a reserved word starting in Stan 2.38. Please rename - it! -[exit 0] +Semantic error in 'deprecated_jacobian_usage.stan', line 15, column 11 to column 26: + ------------------------------------------------- + 13: + 14: real bar(real x) { + 15: return bar_jacobian(x); + ^ + 16: } + 17: } + ------------------------------------------------- + +The jacobian adjustment can only be applied in the transformed parameters block or in functions ending with _jacobian +[exit 1] $ ../../../../../install/default/bin/stanc --print-cpp double-reject.stan // Code generated by %%NAME%% %%VERSION%% #include diff --git a/test/integration/good/code-gen/deprecated_jacobian_usage.stan b/test/integration/good/code-gen/deprecated_jacobian_usage.stan index 8ff4a9698..f63990244 100644 --- a/test/integration/good/code-gen/deprecated_jacobian_usage.stan +++ b/test/integration/good/code-gen/deprecated_jacobian_usage.stan @@ -6,8 +6,20 @@ functions { jacobian += x; return jacobian; } + + real bar_jacobian(real x) { + return x; + } + + real bar(real x) { + return bar_jacobian(x); + } } transformed parameters { real jacobian = 1; jacobian += foo(1); } + +generated quantities { + real b_jacobian = bar(10); +} From ddf5c6adf30db7ec584195867680cbf173918989 Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 26 Nov 2024 11:31:01 -0500 Subject: [PATCH 2/3] Allow existing uses of _jacobian in function names, with warning --- src/frontend/Deprecation_analysis.ml | 86 ++++ src/frontend/Deprecation_analysis.mli | 5 + src/frontend/Typechecker.ml | 3 + src/middle/Fun_kind.ml | 7 +- test/integration/good/code-gen/cpp.expected | 489 +++++++++++++++++++- 5 files changed, 577 insertions(+), 13 deletions(-) diff --git a/src/frontend/Deprecation_analysis.ml b/src/frontend/Deprecation_analysis.ml index 9b75c3618..80fcae5a8 100644 --- a/src/frontend/Deprecation_analysis.ml +++ b/src/frontend/Deprecation_analysis.ml @@ -41,6 +41,76 @@ let lkj_cov_message = independent lognormal distribution on the scales, see: \ https://mc-stan.org/docs/reference-manual/deprecations.html#lkj_cov-distribution" +let functions_block_contains_jac_pe (stmts : untyped_statement list) = + (* tracking if 'jacobian' is a variable in scope *) + let jacobian_scope_id = ref 0 in + let is_jacobian_in_scope () = !jacobian_scope_id > 0 in + let current_scope_id = ref 1 in + let found_jacobian () = + if not (is_jacobian_in_scope ()) then jacobian_scope_id := !current_scope_id + in + let push_scope () = current_scope_id := !current_scope_id + 1 in + let pop_scope () = + current_scope_id := !current_scope_id - 1; + (* if the scope we just left was the one defining jacobian, reset it *) + if !jacobian_scope_id > !current_scope_id then jacobian_scope_id := 0 in + (* walk over the tree, looking for usages of jacobian+= where + there is no variable called jacobian already in scope *) + let rec f (s : untyped_statement) = + match s.stmt with + | FunDef {body; funname; _} + when String.is_suffix funname.name ~suffix:"_jacobian" -> + push_scope (); + let res = f body in + pop_scope (); + res + | Block stmts | Profile (_, stmts) -> + push_scope (); + let res = List.exists ~f stmts in + pop_scope (); + res + | For {loop_body; _} | While (_, loop_body) | ForEach (_, _, loop_body) -> + push_scope (); + let res = f loop_body in + pop_scope (); + res + | IfThenElse (_, s1, s2_opt) -> + push_scope (); + let res1 = f s1 in + pop_scope (); + push_scope (); + let res2 = match s2_opt with Some s2 -> f s2 | None -> false in + pop_scope (); + res1 || res2 + | JacobianPE _ -> true + | Assignment + { assign_lhs= LValue {lval= LVariable {name; _}; _} + ; assign_op= OperatorAssign Plus + ; _ } + when String.equal name "jacobian" -> + not (is_jacobian_in_scope ()) + | VarDecl {variables; _} -> + if + List.exists + ~f:(fun {identifier; _} -> String.equal identifier.name "jacobian") + variables + then found_jacobian (); + false + | _ -> false in + let res = List.exists ~f stmts in + (* sanity check that pushes and pops are balanced *) + if !current_scope_id <> 1 then + Common.ICE.internal_compiler_error + [%message + "functions_block_contains_jac_pe: scope tracking failed" + (!current_scope_id : int) + (!jacobian_scope_id : int) + (stmts : untyped_statement list)]; + res + +let set_jacobian_compatibility_mode stmts = + Fun_kind.jacobian_compat_mode := not (functions_block_contains_jac_pe stmts) + let rec collect_deprecated_expr (acc : (Location_span.t * string) list) ({expr; emeta} : (typed_expr_meta, fun_kind) expr_with) : (Location_span.t * string) list = @@ -89,6 +159,22 @@ let rec collect_deprecated_stmt fundefs (acc : (Location_span.t * string) list) , "Functions do not need to be declared before definition; all user \ defined function names are always in scope regardless of \ definition order." ) ] + | FunDef {funname; body; _} + when !Fun_kind.jacobian_compat_mode + && String.is_suffix funname.name ~suffix:"_jacobian" -> + let acc = + ( funname.id_loc + , "Functions that end in _jacobian will change meaning in Stan 2.39. \ + They will be used for the encapsulating usages of 'jacobian +=', \ + and therefore not available to be called in all the same places as \ + this function is now. To avoid any issues, please rename this \ + function to not end in _jacobian." ) + :: acc in + fold_statement collect_deprecated_expr + (collect_deprecated_stmt fundefs) + collect_deprecated_lval + (fun l _ -> l) + acc body.stmt | Tilde {distribution; _} when String.equal distribution.name "lkj_cov" -> let acc = (distribution.id_loc, lkj_cov_message) :: acc in fold_statement collect_deprecated_expr diff --git a/src/frontend/Deprecation_analysis.mli b/src/frontend/Deprecation_analysis.mli index 2e0ec55a7..616f0a6a0 100644 --- a/src/frontend/Deprecation_analysis.mli +++ b/src/frontend/Deprecation_analysis.mli @@ -11,3 +11,8 @@ val rename_deprecated : (string * (int * int)) String.Map.t -> string -> string val stan_lib_deprecations : (string * (int * int)) String.Map.t val collect_warnings : typed_program -> Warnings.t list val remove_unneeded_forward_decls : typed_program -> typed_program + +val set_jacobian_compatibility_mode : untyped_statement list -> unit +(** Pre-Stan 2.39, we need to know if _jacobian functions are + FnPlain or not. We use the presence of any jacobian+= statements + as our condition. If none are present, we assume this is old code. *) diff --git a/src/frontend/Typechecker.ml b/src/frontend/Typechecker.ml index dd1eddea7..559b4a219 100644 --- a/src/frontend/Typechecker.ml +++ b/src/frontend/Typechecker.ml @@ -454,6 +454,7 @@ let verify_fn_target_plus_equals cf loc id = let verify_fn_jacobian_plus_equals cf loc id = if String.is_suffix id.name ~suffix:"_jacobian" + && (not !Fun_kind.jacobian_compat_mode) && not (in_jacobian_function cf || cf.current_block = TParam) then Semantic_error.jacobian_plusequals_not_allowed loc |> error @@ -1894,6 +1895,8 @@ let add_userdefined_functions tenv stmts_opt = match stmts_opt with | None -> tenv | Some {stmts; _} -> + (* TODO(2.39): Remove this workaround *) + Deprecation_analysis.set_jacobian_compatibility_mode stmts; let f tenv (s : Ast.untyped_statement) = match s with | {stmt= FunDef {returntype; funname; arguments; body}; smeta= {loc}} -> diff --git a/src/middle/Fun_kind.ml b/src/middle/Fun_kind.ml index a81dfad07..b9394e532 100644 --- a/src/middle/Fun_kind.ml +++ b/src/middle/Fun_kind.ml @@ -21,11 +21,16 @@ type 'e t = | UserDefined of string * bool suffix [@@deriving compare, sexp, hash, map, fold] +(** If true, we assume _jacobian functions are + "plain" functions for the purposes of typechecking and warnings +*) +let jacobian_compat_mode = ref false + let suffix_from_name fname = let is_suffix suffix = Core.String.is_suffix ~suffix fname in if is_suffix "_rng" then FnRng else if is_suffix "_lp" then FnTarget - else if is_suffix "_jacobian" then FnJacobian + else if is_suffix "_jacobian" && not !jacobian_compat_mode then FnJacobian else if is_suffix "_lupdf" then FnLpdf true else if is_suffix "_lupmf" then FnLpmf true else if is_suffix "_lpdf" then FnLpdf false diff --git a/test/integration/good/code-gen/cpp.expected b/test/integration/good/code-gen/cpp.expected index 181873453..e5a27a9c7 100644 --- a/test/integration/good/code-gen/cpp.expected +++ b/test/integration/good/code-gen/cpp.expected @@ -3925,18 +3925,483 @@ stan::math::profile_map& get_stan_profile_data() { #endif [exit 0] $ ../../../../../install/default/bin/stanc --print-cpp deprecated_jacobian_usage.stan -Semantic error in 'deprecated_jacobian_usage.stan', line 15, column 11 to column 26: - ------------------------------------------------- - 13: - 14: real bar(real x) { - 15: return bar_jacobian(x); - ^ - 16: } - 17: } - ------------------------------------------------- - -The jacobian adjustment can only be applied in the transformed parameters block or in functions ending with _jacobian -[exit 1] +// Code generated by %%NAME%% %%VERSION%% +#include +namespace deprecated_jacobian_usage_model_namespace { +using stan::model::model_base_crtp; +using namespace stan::math; +stan::math::profile_map profiles__; +static constexpr std::array locations_array__ = + {" (found before start of program)", + " (in 'deprecated_jacobian_usage.stan', line 19, column 2 to column 20)", + " (in 'deprecated_jacobian_usage.stan', line 24, column 2 to column 28)", + " (in 'deprecated_jacobian_usage.stan', line 20, column 2 to column 21)", + " (in 'deprecated_jacobian_usage.stan', line 5, column 4 to column 22)", + " (in 'deprecated_jacobian_usage.stan', line 6, column 4 to column 18)", + " (in 'deprecated_jacobian_usage.stan', line 7, column 4 to column 20)", + " (in 'deprecated_jacobian_usage.stan', line 4, column 19 to line 8, column 3)", + " (in 'deprecated_jacobian_usage.stan', line 11, column 4 to column 13)", + " (in 'deprecated_jacobian_usage.stan', line 10, column 28 to line 12, column 3)", + " (in 'deprecated_jacobian_usage.stan', line 15, column 4 to column 27)", + " (in 'deprecated_jacobian_usage.stan', line 14, column 19 to line 16, column 3)"}; +template , + std::is_floating_point>>* = nullptr> +stan::promote_args_t foo(const T0__& x, std::ostream* pstream__); +template , + std::is_floating_point>>* = nullptr> +stan::promote_args_t +bar_jacobian(const T0__& x, std::ostream* pstream__); +template , + std::is_floating_point>>* = nullptr> +stan::promote_args_t bar(const T0__& x, std::ostream* pstream__); +// real foo(real) +template , + std::is_floating_point>>*> +stan::promote_args_t foo(const T0__& x, std::ostream* pstream__) { + using local_scalar_t__ = stan::promote_args_t; + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + static constexpr bool propto__ = true; + // suppress unused var warning + (void) propto__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + try { + local_scalar_t__ jacobian = DUMMY_VAR__; + current_statement__ = 4; + jacobian = 0; + current_statement__ = 5; + jacobian = (jacobian + x); + current_statement__ = 6; + return jacobian; + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } +} +// real bar_jacobian(real) +template , + std::is_floating_point>>*> +stan::promote_args_t +bar_jacobian(const T0__& x, std::ostream* pstream__) { + using local_scalar_t__ = stan::promote_args_t; + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + static constexpr bool propto__ = true; + // suppress unused var warning + (void) propto__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + try { + current_statement__ = 8; + return x; + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } +} +// real bar(real) +template , + std::is_floating_point>>*> +stan::promote_args_t bar(const T0__& x, std::ostream* pstream__) { + using local_scalar_t__ = stan::promote_args_t; + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + static constexpr bool propto__ = true; + // suppress unused var warning + (void) propto__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + try { + current_statement__ = 10; + return bar_jacobian(x, pstream__); + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } +} +class deprecated_jacobian_usage_model final : public model_base_crtp { + private: + + public: + ~deprecated_jacobian_usage_model() {} + deprecated_jacobian_usage_model(stan::io::var_context& context__, + unsigned int random_seed__ = 0, + std::ostream* pstream__ = nullptr) + : model_base_crtp(0) { + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + using local_scalar_t__ = double; + auto base_rng__ = stan::services::util::create_rng(random_seed__, 0); + // suppress unused var warning + (void) base_rng__; + static constexpr const char* function__ = + "deprecated_jacobian_usage_model_namespace::deprecated_jacobian_usage_model"; + // suppress unused var warning + (void) function__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + num_params_r__ = 0U; + } + inline std::string model_name() const final { + return "deprecated_jacobian_usage_model"; + } + inline std::vector model_compile_info() const noexcept { + return std::vector{"stanc_version = %%NAME%%3 %%VERSION%%", + "stancflags = --print-cpp"}; + } + // Base log prob + template * = nullptr, + stan::require_vector_like_vt* = nullptr, + stan::require_not_st_var* = nullptr> + inline stan::scalar_type_t + log_prob_impl(VecR& params_r__, VecI& params_i__, std::ostream* + pstream__ = nullptr) const { + using T__ = stan::scalar_type_t; + using local_scalar_t__ = T__; + T__ lp__(0.0); + stan::math::accumulator lp_accum__; + stan::io::deserializer in__(params_r__, params_i__); + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + static constexpr const char* function__ = + "deprecated_jacobian_usage_model_namespace::log_prob"; + // suppress unused var warning + (void) function__; + try { + local_scalar_t__ jacobian = DUMMY_VAR__; + current_statement__ = 1; + jacobian = 1; + current_statement__ = 3; + jacobian = (jacobian + foo(static_cast(1), pstream__)); + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } + lp_accum__.add(lp__); + return lp_accum__.sum(); + } + // Reverse mode autodiff log prob + template * = nullptr, + stan::require_vector_like_vt* = nullptr, + stan::require_st_var* = nullptr> + inline stan::scalar_type_t + log_prob_impl(VecR& params_r__, VecI& params_i__, std::ostream* + pstream__ = nullptr) const { + using T__ = stan::scalar_type_t; + using local_scalar_t__ = T__; + T__ lp__(0.0); + stan::math::accumulator lp_accum__; + stan::io::deserializer in__(params_r__, params_i__); + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + static constexpr const char* function__ = + "deprecated_jacobian_usage_model_namespace::log_prob"; + // suppress unused var warning + (void) function__; + try { + local_scalar_t__ jacobian = DUMMY_VAR__; + current_statement__ = 1; + jacobian = 1; + current_statement__ = 3; + jacobian = (jacobian + foo(static_cast(1), pstream__)); + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } + lp_accum__.add(lp__); + return lp_accum__.sum(); + } + template * = nullptr, stan::require_vector_like_vt* = nullptr, stan::require_vector_vt* = nullptr> + inline void + write_array_impl(RNG& base_rng__, VecR& params_r__, VecI& params_i__, + VecVar& vars__, const bool + emit_transformed_parameters__ = true, const bool + emit_generated_quantities__ = true, std::ostream* + pstream__ = nullptr) const { + using local_scalar_t__ = double; + stan::io::deserializer in__(params_r__, params_i__); + stan::io::serializer out__(vars__); + static constexpr bool propto__ = true; + // suppress unused var warning + (void) propto__; + double lp__ = 0.0; + // suppress unused var warning + (void) lp__; + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + stan::math::accumulator lp_accum__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + constexpr bool jacobian__ = false; + // suppress unused var warning + (void) jacobian__; + static constexpr const char* function__ = + "deprecated_jacobian_usage_model_namespace::write_array"; + // suppress unused var warning + (void) function__; + try { + double jacobian = std::numeric_limits::quiet_NaN(); + if (stan::math::logical_negation( + (stan::math::primitive_value(emit_transformed_parameters__) || + stan::math::primitive_value(emit_generated_quantities__)))) { + return ; + } + current_statement__ = 1; + jacobian = 1; + current_statement__ = 3; + jacobian = (jacobian + foo(static_cast(1), pstream__)); + if (emit_transformed_parameters__) { + out__.write(jacobian); + } + if (stan::math::logical_negation(emit_generated_quantities__)) { + return ; + } + double b_jacobian = std::numeric_limits::quiet_NaN(); + current_statement__ = 2; + b_jacobian = bar(static_cast(10), pstream__); + out__.write(b_jacobian); + } catch (const std::exception& e) { + stan::lang::rethrow_located(e, locations_array__[current_statement__]); + } + } + template * = nullptr, + stan::require_vector_like_vt* = nullptr> + inline void + unconstrain_array_impl(const VecVar& params_r__, const VecI& params_i__, + VecVar& vars__, std::ostream* pstream__ = nullptr) const { + using local_scalar_t__ = double; + stan::io::deserializer in__(params_r__, params_i__); + stan::io::serializer out__(vars__); + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + } + template * = nullptr> + inline void + transform_inits_impl(const stan::io::var_context& context__, VecVar& + vars__, std::ostream* pstream__ = nullptr) const { + using local_scalar_t__ = double; + stan::io::serializer out__(vars__); + int current_statement__ = 0; + // suppress unused var warning + (void) current_statement__; + local_scalar_t__ DUMMY_VAR__(std::numeric_limits::quiet_NaN()); + // suppress unused var warning + (void) DUMMY_VAR__; + } + inline void + get_param_names(std::vector& names__, const bool + emit_transformed_parameters__ = true, const bool + emit_generated_quantities__ = true) const { + names__ = std::vector{}; + if (emit_transformed_parameters__) { + std::vector temp{"jacobian"}; + names__.reserve(names__.size() + temp.size()); + names__.insert(names__.end(), temp.begin(), temp.end()); + } + if (emit_generated_quantities__) { + std::vector temp{"b_jacobian"}; + names__.reserve(names__.size() + temp.size()); + names__.insert(names__.end(), temp.begin(), temp.end()); + } + } + inline void + get_dims(std::vector>& dimss__, const bool + emit_transformed_parameters__ = true, const bool + emit_generated_quantities__ = true) const { + dimss__ = std::vector>{}; + if (emit_transformed_parameters__) { + std::vector> temp{std::vector{}}; + dimss__.reserve(dimss__.size() + temp.size()); + dimss__.insert(dimss__.end(), temp.begin(), temp.end()); + } + if (emit_generated_quantities__) { + std::vector> temp{std::vector{}}; + dimss__.reserve(dimss__.size() + temp.size()); + dimss__.insert(dimss__.end(), temp.begin(), temp.end()); + } + } + inline void + constrained_param_names(std::vector& param_names__, bool + emit_transformed_parameters__ = true, bool + emit_generated_quantities__ = true) const final { + if (emit_transformed_parameters__) { + param_names__.emplace_back(std::string() + "jacobian"); + } + if (emit_generated_quantities__) { + param_names__.emplace_back(std::string() + "b_jacobian"); + } + } + inline void + unconstrained_param_names(std::vector& param_names__, bool + emit_transformed_parameters__ = true, bool + emit_generated_quantities__ = true) const final { + if (emit_transformed_parameters__) { + param_names__.emplace_back(std::string() + "jacobian"); + } + if (emit_generated_quantities__) { + param_names__.emplace_back(std::string() + "b_jacobian"); + } + } + inline std::string get_constrained_sizedtypes() const { + return std::string("[{\"name\":\"jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"transformed_parameters\"},{\"name\":\"b_jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"generated_quantities\"}]"); + } + inline std::string get_unconstrained_sizedtypes() const { + return std::string("[{\"name\":\"jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"transformed_parameters\"},{\"name\":\"b_jacobian\",\"type\":{\"name\":\"real\"},\"block\":\"generated_quantities\"}]"); + } + // Begin method overload boilerplate + template inline void + write_array(RNG& base_rng, Eigen::Matrix& params_r, + Eigen::Matrix& vars, const bool + emit_transformed_parameters = true, const bool + emit_generated_quantities = true, std::ostream* + pstream = nullptr) const { + const size_t num_params__ = 0; + const size_t num_transformed = emit_transformed_parameters * (1); + const size_t num_gen_quantities = emit_generated_quantities * (1); + const size_t num_to_write = num_params__ + num_transformed + + num_gen_quantities; + std::vector params_i; + vars = Eigen::Matrix::Constant(num_to_write, + std::numeric_limits::quiet_NaN()); + write_array_impl(base_rng, params_r, params_i, vars, + emit_transformed_parameters, emit_generated_quantities, pstream); + } + template inline void + write_array(RNG& base_rng, std::vector& params_r, std::vector& + params_i, std::vector& vars, bool + emit_transformed_parameters = true, bool + emit_generated_quantities = true, std::ostream* + pstream = nullptr) const { + const size_t num_params__ = 0; + const size_t num_transformed = emit_transformed_parameters * (1); + const size_t num_gen_quantities = emit_generated_quantities * (1); + const size_t num_to_write = num_params__ + num_transformed + + num_gen_quantities; + vars = std::vector(num_to_write, + std::numeric_limits::quiet_NaN()); + write_array_impl(base_rng, params_r, params_i, vars, + emit_transformed_parameters, emit_generated_quantities, pstream); + } + template inline T_ + log_prob(Eigen::Matrix& params_r, std::ostream* pstream = nullptr) const { + Eigen::Matrix params_i; + return log_prob_impl(params_r, params_i, pstream); + } + template inline T_ + log_prob(std::vector& params_r, std::vector& params_i, + std::ostream* pstream = nullptr) const { + return log_prob_impl(params_r, params_i, pstream); + } + inline void + transform_inits(const stan::io::var_context& context, + Eigen::Matrix& params_r, std::ostream* + pstream = nullptr) const final { + std::vector params_r_vec(params_r.size()); + std::vector params_i; + transform_inits(context, params_i, params_r_vec, pstream); + params_r = Eigen::Map>(params_r_vec.data(), + params_r_vec.size()); + } + inline void + transform_inits(const stan::io::var_context& context, std::vector& + params_i, std::vector& vars, std::ostream* + pstream__ = nullptr) const { + vars.resize(num_params_r__); + transform_inits_impl(context, vars, pstream__); + } + inline void + unconstrain_array(const std::vector& params_constrained, + std::vector& params_unconstrained, std::ostream* + pstream = nullptr) const { + const std::vector params_i; + params_unconstrained = std::vector(num_params_r__, + std::numeric_limits::quiet_NaN()); + unconstrain_array_impl(params_constrained, params_i, + params_unconstrained, pstream); + } + inline void + unconstrain_array(const Eigen::Matrix& params_constrained, + Eigen::Matrix& params_unconstrained, + std::ostream* pstream = nullptr) const { + const std::vector params_i; + params_unconstrained = Eigen::Matrix::Constant(num_params_r__, + std::numeric_limits::quiet_NaN()); + unconstrain_array_impl(params_constrained, params_i, + params_unconstrained, pstream); + } +}; +} +using stan_model = deprecated_jacobian_usage_model_namespace::deprecated_jacobian_usage_model; +#ifndef USING_R +// Boilerplate +stan::model::model_base& +new_model(stan::io::var_context& data_context, unsigned int seed, + std::ostream* msg_stream) { + stan_model* m = new stan_model(data_context, seed, msg_stream); + return *m; +} +stan::math::profile_map& get_stan_profile_data() { + return deprecated_jacobian_usage_model_namespace::profiles__; +} +#endif +Warning in 'deprecated_jacobian_usage.stan', line 5, column 9: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 5, column 9: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 6, column 4: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 7, column 11: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 19, column 7: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 19, column 7: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 20, column 2: Variable name + 'jacobian' will be a reserved word starting in Stan 2.38. Please rename + it! +Warning in 'deprecated_jacobian_usage.stan', line 10, column 7: Functions + that end in _jacobian will change meaning in Stan 2.39. They will be used + for the encapsulating usages of 'jacobian +=', and therefore not + available to be called in all the same places as this function is now. To + avoid any issues, please rename this function to not end in _jacobian. +[exit 0] $ ../../../../../install/default/bin/stanc --print-cpp double-reject.stan // Code generated by %%NAME%% %%VERSION%% #include From 02c8663da9db3ee9602c28c479d8b8ec9b449cfa Mon Sep 17 00:00:00 2001 From: Brian Ward Date: Tue, 26 Nov 2024 12:19:00 -0500 Subject: [PATCH 3/3] Drive by: Unify checks for function call statements with expressions --- src/frontend/Typechecker.ml | 11 +++------ .../bad/err-jacobian-plusequals-scope4.stan | 9 +++++++ test/integration/bad/err_void_rng_check.stan | 9 +++++++ test/integration/bad/stanc.expected | 24 +++++++++++++++++++ .../cli-args/warn-pedantic/stanc.expected | 4 ++++ test/integration/good/pretty.expected | 4 ++++ 6 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 test/integration/bad/err-jacobian-plusequals-scope4.stan create mode 100644 test/integration/bad/err_void_rng_check.stan diff --git a/src/frontend/Typechecker.ml b/src/frontend/Typechecker.ml index 559b4a219..b6d8a8046 100644 --- a/src/frontend/Typechecker.ml +++ b/src/frontend/Typechecker.ml @@ -914,13 +914,6 @@ let check_expression_of_scalar_or_type cf tenv t e name = (* -- Statements ------------------------------------------------- *) (* non returning functions *) -let verify_nrfn_target loc cf id = - if - String.is_suffix id.name ~suffix:"_lp" - && not - (in_lp_function cf || cf.current_block = Model - || cf.current_block = TParam) - then Semantic_error.target_plusequals_outside_model_or_logprob loc |> error let check_nrfn loc tenv id es = match Env.find tenv id.name with @@ -961,7 +954,9 @@ let check_nrfn loc tenv id es = let check_nr_fn_app loc cf tenv id es = let tes = List.map ~f:(check_expression cf tenv) es in verify_identifier id; - verify_nrfn_target loc cf id; + verify_fn_target_plus_equals cf loc id; + verify_fn_jacobian_plus_equals cf loc id; + verify_fn_rng cf loc id; check_nrfn loc tenv id tes (* target plus-equals / jacobian plus-equals *) diff --git a/test/integration/bad/err-jacobian-plusequals-scope4.stan b/test/integration/bad/err-jacobian-plusequals-scope4.stan new file mode 100644 index 000000000..932019490 --- /dev/null +++ b/test/integration/bad/err-jacobian-plusequals-scope4.stan @@ -0,0 +1,9 @@ +functions { + // void return type to check function statement, rather than expression + void foo_jacobian() { + jacobian += 1; + } +} +transformed data { + foo_jacobian(); +} diff --git a/test/integration/bad/err_void_rng_check.stan b/test/integration/bad/err_void_rng_check.stan new file mode 100644 index 000000000..b685e95b4 --- /dev/null +++ b/test/integration/bad/err_void_rng_check.stan @@ -0,0 +1,9 @@ +functions { + void foo_rng(real x){ + print(normal_rng(0,x)); + } +} + +model { + foo_rng(1.0); +} diff --git a/test/integration/bad/stanc.expected b/test/integration/bad/stanc.expected index 2094b7e6f..27552c422 100644 --- a/test/integration/bad/stanc.expected +++ b/test/integration/bad/stanc.expected @@ -1083,6 +1083,18 @@ Semantic error in 'err-jacobian-plusequals-scope3.stan', line 14, column 11 to c 15: } ------------------------------------------------- +The jacobian adjustment can only be applied in the transformed parameters block or in functions ending with _jacobian +[exit 1] + $ ../../../../install/default/bin/stanc err-jacobian-plusequals-scope4.stan +Semantic error in 'err-jacobian-plusequals-scope4.stan', line 8, column 2 to column 17: + ------------------------------------------------- + 6: } + 7: transformed data { + 8: foo_jacobian(); + ^ + 9: } + ------------------------------------------------- + The jacobian adjustment can only be applied in the transformed parameters block or in functions ending with _jacobian [exit 1] $ ../../../../install/default/bin/stanc err-minus-types.stan @@ -1245,6 +1257,18 @@ Syntax error in 'err-transformed-params.stan', line 4, column 0 to column 11, pa ------------------------------------------------- "transformed parameters {", "model {" or "generated quantities {" expected after end of parameters block. +[exit 1] + $ ../../../../install/default/bin/stanc err_void_rng_check.stan +Semantic error in 'err_void_rng_check.stan', line 8, column 4 to column 17: + ------------------------------------------------- + 6: + 7: model { + 8: foo_rng(1.0); + ^ + 9: } + ------------------------------------------------- + +Random number generators are only allowed in transformed data block, generated quantities block or user-defined functions with names ending in _rng. [exit 1] $ ../../../../install/default/bin/stanc expect_statement_seq_close_brace.stan Syntax error in 'expect_statement_seq_close_brace.stan', line 6, column 0 to column 0, parsing error: diff --git a/test/integration/cli-args/warn-pedantic/stanc.expected b/test/integration/cli-args/warn-pedantic/stanc.expected index 55ed0c5f9..f7788d3db 100644 --- a/test/integration/cli-args/warn-pedantic/stanc.expected +++ b/test/integration/cli-args/warn-pedantic/stanc.expected @@ -546,6 +546,10 @@ Warning in 'jacobian_warning_user.stan', line 5, column 2: Left-hand side of using jacobian += in the transformed parameters block. [exit 0] $ ../../../../../install/default/bin/stanc --warn-pedantic lp_fun.stan +Warning in 'lp_fun.stan', line 10, column 2: Using _lp functions in + transformed parameters is deprecated and will be disallowed in Stan 2.39. + Use an _jacobian function instead, as this allows change of variable + adjustments which are conditionally enabled by the algorithms. Warning: The parameter y has 2 priors. [exit 0] $ ../../../../../install/default/bin/stanc --warn-pedantic missing-prior-false-alarm.stan diff --git a/test/integration/good/pretty.expected b/test/integration/good/pretty.expected index 2a7bbcc03..19cd4f95c 100644 --- a/test/integration/good/pretty.expected +++ b/test/integration/good/pretty.expected @@ -3991,6 +3991,10 @@ Warning in 'lp_transformed_param.stan', line 14, column 15: Using _lp functions in transformed parameters is deprecated and will be disallowed in Stan 2.39. Use an _jacobian function instead, as this allows change of variable adjustments which are conditionally enabled by the algorithms. +Warning in 'lp_transformed_param.stan', line 15, column 2: Using _lp + functions in transformed parameters is deprecated and will be disallowed + in Stan 2.39. Use an _jacobian function instead, as this allows change of + variable adjustments which are conditionally enabled by the algorithms. [exit 0] $ ../../../../install/default/bin/stanc --auto-format map_rect.stan functions {