diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9cf0d5930b9..2e1cb8bfd4a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -25,4 +25,4 @@ If this is a **feature request**, show what you expect to happen if the feature #### Current Version: -v4.8.1 +v4.9.0 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 444560a4353..5f3807ec889 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Windows Rtools40 +name: Windows Rtools44 on: pull_request: @@ -30,26 +30,17 @@ jobs: python-version: '3.x' - uses: r-lib/actions/setup-r@v2 with: - r-version: 4.1.3 + r-version: 'release' + rtools-version: '44' - - name: Set path for Rtools40 + - name: Set path for Rtools44 if: runner.os == 'Windows' - run: echo "C:/rtools40/usr/bin;C:/rtools40/mingw64/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - - name: Install mingw32-make and check toolchain path - if: runner.os == 'Windows' - run: | - pacman -Syu mingw-w64-x86_64-make --noconfirm - g++ --version - Get-Command g++ | Select-Object -ExpandProperty Definition - mingw32-make --version - Get-Command mingw32-make | Select-Object -ExpandProperty Definition - shell: powershell + run: echo "C:/rtools44/usr/bin;C:/rtools44/x86_64-w64-mingw32.static.posix/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Build Math libs shell: powershell run: | - Add-Content make\local "O=1`n" - mingw32-make -f make/standalone math-libs -j2 + make -f make/standalone math-libs -j2 - name: Add TBB to PATH shell: powershell run: echo "D:/a/math/math/lib/tbb" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 @@ -80,26 +71,17 @@ jobs: python-version: '3.x' - uses: r-lib/actions/setup-r@v2 with: - r-version: 4.1.3 + r-version: 'release' + rtools-version: '44' - - name: Set path for Rtools40 + - name: Set path for Rtools44 if: runner.os == 'Windows' - run: echo "C:/rtools40/usr/bin;C:/rtools40/mingw64/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - - name: Install mingw32-make and check toolchain path - if: runner.os == 'Windows' - run: | - pacman -Syu mingw-w64-x86_64-make --noconfirm - g++ --version - Get-Command g++ | Select-Object -ExpandProperty Definition - mingw32-make --version - Get-Command mingw32-make | Select-Object -ExpandProperty Definition - shell: powershell + run: echo "C:/rtools44/usr/bin;C:/rtools44/x86_64-w64-mingw32.static.posix/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Build Math libs shell: powershell run: | - Add-Content make\local "O=1`n" - mingw32-make -f make/standalone math-libs -j2 + make -f make/standalone math-libs -j2 - name: Add TBB to PATH shell: powershell run: echo "D:/a/math/math/lib/tbb" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 @@ -134,26 +116,17 @@ jobs: python-version: '3.x' - uses: r-lib/actions/setup-r@v2 with: - r-version: 4.1.3 + r-version: 'release' + rtools-version: '44' - - name: Set path for Rtools40 + - name: Set path for Rtools44 if: runner.os == 'Windows' - run: echo "C:/rtools40/usr/bin;C:/rtools40/mingw64/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - - name: Install mingw32-make and check toolchain path - if: runner.os == 'Windows' - run: | - pacman -Syu mingw-w64-x86_64-make --noconfirm - g++ --version - Get-Command g++ | Select-Object -ExpandProperty Definition - mingw32-make --version - Get-Command mingw32-make | Select-Object -ExpandProperty Definition - shell: powershell + run: echo "C:/rtools44/usr/bin;C:/rtools44/x86_64-w64-mingw32.static.posix/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Build Math libs shell: powershell run: | - Add-Content make\local "O=1`n" - mingw32-make -f make/standalone math-libs -j2 + make -f make/standalone math-libs -j2 - name: Add TBB to PATH shell: powershell run: echo "D:/a/math/math/lib/tbb" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 @@ -171,7 +144,7 @@ jobs: with: name: gtest_outputs_xml path: '**/*_test.xml' - + mix-fun-2: name: mix/fun tests 2 runs-on: windows-latest @@ -183,26 +156,18 @@ jobs: python-version: '3.x' - uses: r-lib/actions/setup-r@v2 with: - r-version: 4.1.3 + r-version: 'release' + rtools-version: '44' - - name: Set path for Rtools40 - if: runner.os == 'Windows' - run: echo "C:/rtools40/usr/bin;C:/rtools40/mingw64/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - - name: Install mingw32-make and check toolchain path + - name: Set path for Rtools44 if: runner.os == 'Windows' - run: | - pacman -Syu mingw-w64-x86_64-make --noconfirm - g++ --version - Get-Command g++ | Select-Object -ExpandProperty Definition - mingw32-make --version - Get-Command mingw32-make | Select-Object -ExpandProperty Definition - shell: powershell + run: echo "C:/rtools44/usr/bin;C:/rtools44/x86_64-w64-mingw32.static.posix/bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: Build Math libs shell: powershell run: | Add-Content make\local "O=1`n" - mingw32-make -f make/standalone math-libs -j2 + make -f make/standalone math-libs -j2 - name: Add TBB to PATH shell: powershell run: echo "D:/a/math/math/lib/tbb" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 diff --git a/Jenkinsfile b/Jenkinsfile index 9e7df2a5603..991215acca3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,6 +4,8 @@ import org.stan.Utils def runTests(String testPath, boolean jumbo = false) { try { + sh "cat make/local" + sh "make print-compiler-flags" if (jumbo && !params.disableJumbo) { sh "python3 runTests.py -j${env.PARALLEL} ${testPath} --jumbo --debug" } else { @@ -141,11 +143,11 @@ pipeline { } post { always { - recordIssues enabledForFailure: true, tools: - [cppLint(), - groovyScript(parserId: 'mathDependencies', pattern: '**/dependencies.log')] + recordIssues( + enabledForFailure: true, + tools: [cppLint(),groovyScript(parserId: 'mathDependencies', pattern: '**/dependencies.log')] + ) deleteDir() - } } } @@ -344,6 +346,7 @@ pipeline { runTests("test/unit/multiple_translation_units_test.cpp") } } + post { always { retry(3) { deleteDir() } } } } } } @@ -393,6 +396,7 @@ pipeline { sh "python ./test/varmat_compatibility_test.py" withEnv(['PATH+TBB=./lib/tbb']) { sh "python ./test/expressions/test_expression_testing_framework.py" + sh "cat make/local" try { sh "./runTests.py -j${PARALLEL} test/expressions" } finally { junit 'test/**/*.xml' } } @@ -400,6 +404,7 @@ pipeline { sh "echo STAN_THREADS=true >> make/local" withEnv(['PATH+TBB=./lib/tbb']) { try { + sh "cat make/local" sh "./runTests.py -j${PARALLEL} test/expressions --only-functions reduce_sum map_rect" } finally { junit 'test/**/*.xml' } @@ -498,6 +503,7 @@ pipeline { sh "echo CXXFLAGS+=-DSTAN_PROB_TEST_ALL >> make/local" } } + sh "cat make/local" sh "./runTests.py -j${PARALLEL} ${names}" } deleteDir() @@ -569,7 +575,10 @@ pipeline { post { always { node("linux") { - recordIssues enabledForFailure: false, tool: clang() + recordIssues( + enabledForFailure: false, + tool: clang() + ) } } success { diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 73b6ffafdca..2614c0f9503 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,5 +1,41 @@ Stan Math Library Release Notes +====================================================================== +v4.9.0 (3 June 2024) +====================================================================== + + - Stan now detects if your compiler supports C++17 and will issue a warning if it is not available. Support for pre-C++17 compilers may be removed as early as next version. (#3063) + - Added a new distribution: `wiener_full_lpdf`. (#2822) + - Replaced `static const` objects with `static constexpr` when available. (#2830) + - Gradients for the `hypergeometric_pFq` function rewritten for increased speed and stability. (#2961) + - Added the Hypergeometric 1F0 function and gradients. (#2962) + - Removed macro usage in the definitions of the `require_` templates. (#2965) + - Added a sparse matrix implimentation for `arena_matrix`. (#2971) + - Added hand-calculated derivatives for the multivariate normal lpdf. (#2980) + - Added derivatives for the `multi_student_t_cholesky_lpdf`. (#2990) + - Added constrain function for creating matrices holding row or column simplexes. (#2992) + - Updated TBB Windows build rules for compatibility with RTools make. (#2999) + - Improved efficiency of `von_mises_lpdf` gradient calculation, credit to @venpopov. (#3010) + - Fixed a few additional issues in checking the inputs to distributions defined over cholesky matrices. (#3012) + - Functions which are wrappers around CVODES and IDAS routines from Sundials now throw `domain_error`, rather than a mix of `domain_error` and `runtime_error`. (#3013) + - Fixed the stack_allocator being able to return non-8-byte aligned pointers. (#3014) + - Upgrade bundled Boost headers to 1.84. (#3001, #3018, #3019) + - Fixed a bug where `linspaced_array` was returning its results truncated to integers. (#3023) + - Fixed the return type of `max(int, int)` being a double. (#3024) + - Added 'override' to built-in make variables. (#3028) + - Maded `sqrt(x)` have a finite gradient at `x=0`. Allows `distance(x,y)` to work for `x=y` too. (#3033) + - Improved stability of the `von_mises_lpdf` function to avoid numeric overflow. Now it's no longer necessary to use the normal_lpdf for kappa>100, allowing vectorizing of the likelihood. (#3036) + - Updated the `weibull_cdf` and `weibull_lcdf` functions for numerical stability as well as correct handling of `y == 0.0`. (#3037) + - Improved speed of `inv_Phi` and `std_normal_qf` functions. (#3046) + - Fixed spurious linker issue with `csr_matrix_times_vector`. (#3048, #3053) + - Improved error messages when variables dimensions do not match in operations that require it. (#3049) + - Added support for Windows ARM64 with RTools ARM64 toolchain. (#3051) + - Fixed `clean-all` error when using external SUNDIALS libraries. (#3054) + - Fixed several small floating-point accuracy issues with ARM64 platforms. (#3059) + - The RNGs for the multinomial and multinomial_logit distributions now accept a zero total count, resulting in a zero integer vector. (#3061) + - Fixed a data race in profiling in multiple threads. (#3066) + - Backported SUNDIALS bugfix for hashmap integer overflow. (#3072) + ====================================================================== v4.8.1 (23 January 2024) ====================================================================== diff --git a/doxygen/contributor_help_pages/developer_doc.md b/doxygen/contributor_help_pages/developer_doc.md index 0961a5ba9c3..687eb1b157d 100644 --- a/doxygen/contributor_help_pages/developer_doc.md +++ b/doxygen/contributor_help_pages/developer_doc.md @@ -184,7 +184,7 @@ These are the more common make flags that could be set: These are the rest of the variables that can be set: - C++ compiler flags - - `CXXFLAGS_LANG`: sets the language. Currently defaults to `-std=c++1y` + - `CXXFLAGS_LANG`: sets the language. Currently defaults to `-std=c++17` - `CXXFLAGS_WARNINGS`: compiler options to squash compiler warnings - `CXXFLAGS_BOOST`: Boost-specific compiler flags - `CXXFLAGS_EIGEN`: Eigen-specific compiler flags diff --git a/doxygen/contributor_help_pages/require_meta.md b/doxygen/contributor_help_pages/require_meta.md index 1ff6261b432..95864edd911 100644 --- a/doxygen/contributor_help_pages/require_meta.md +++ b/doxygen/contributor_help_pages/require_meta.md @@ -1,22 +1,337 @@ -## Using requires<> for general overloads {#require_meta_doc} +## Using requires<> template metaprograms for partial template specialization {#require_meta_doc} + +Most of the functions in the Stan Math library are implemented as +[templated +functions](https://en.cppreference.com/w/cpp/language/function_template) +which allow for arguments that can be C++ primitive types +(e.g. `double`, `int`), Stan's reverse-mode or forward-mode automatic +differentiation (autodiff) types, or containers and expressions of +either primitive or autodiff types. We use templated functions rather +than overloaded functions for a number of reasons including the sheer +number of implementations we would need to write to handle the +combinations of arguments that are allowed in the Stan language. + +Many functions in the Stan Math library have a single function +template defined in the `stan/math/prim` folder that is flexible +enough to accept both primitive and autodiff types. Some of these +functions also have template specializations (usually [partial +template +specializations](https://en.cppreference.com/w/cpp/language/partial_specialization)) +that define an implementation where at least one template parameters +is restricted to a specific type. The source code for the function +template specializations are found in either `stan/math/rev/` for +reverse-mode implementations, `stan/math/fwd/` for forward-mode +implementations, or `stan/math/mix/` for implementations that are for +nested autodiff. This pattern of a primary template function +definition with specialization is commonly used in templated C++. + +In the Stan Math library, we have also adopted another technique which +allows multiple template function definitions, while restricting the +definition to apply only to when certain criteria are met. Since this +technique is used repeatedly through the Math library and this is not a +common use of template metaprogramming, we'll describe it in the next +subsection. + +### Using SFINAE to allow multiple template function definitions + +In the Stan Math library, each function exposed through to the Stan +language must have definitions that allow for both primitive and +autodiff types. As the language has grown, we have also added +broadcasting, which allows users to mix scalars arguments with vector +or array arguments. + +The typical C++ method of defining a primary function template and a +set of function template specialization is untenable for many +functions in the Math library. For example, a single argument of the +function may take 7 distinct C++ types: `double`, `stan::math::var`, +`std::vector`, `std::vector`, +`Eigen::Matrix`, `Eigen::Matrix`, or `stan::math::var_value>`. +For a 3-argument function, we would need to define 343 (7^3) different +function template specializations to handle all the autodiff types. + + +In the Math library, we use a technique similar to C++20's `require` +keyword that allows the definition of multiple template functions +where each handles a subset of allowable types. + +When the compiler attempts to resolve which function should be called +from a set of templated function signatures there must be only one +possibly valid function signature available. This is called the [One +Definition +Rule](https://en.cppreference.com/w/cpp/language/definition). For +example, the following code would fail to compile because the compiler +is unable to differentiate between the two function signatures. + +```c++ +template +T foo(T x) { + return x; +} + +template +K foo(K x) { + return x; +} +``` + +The compiler needs a way to differentiate between the two signatures +to select one and satisfy the One Definition Rule. One trick to have a +single valid definition is to utilize [Substitution Failure Is Not An +Error (SFNIAE)](https://en.cppreference.com/w/cpp/language/sfinae) to +purposefully create conditions where only one signature is valid +because all of the other conditions fail to compile. The simplest way +to do this is to start with a type trait like the below +`enable_if`. The `enable_if` is only defined for the case where `B` is +`true` and so if `B` is ever false the compiler would throw an error +saying that `enable_if` is not well defined. + +``` +// Define enable_if. Note: `type` is not a member typedef. +template +struct enable_if {}; + +// Only define member typedef `type` when B is true +template +struct enable_if { typedef T type; }; + +template +using enable_if_t = typename enable_if::type; +``` + +Attempting to construct this `enable_if` with `B` being `false` +anywhere else in the program would cause the compiler to crash. +Using it in the template of a function signature allows SFINAE to +deduce which signature we would like to use. + +```c++ +// foo only works with floating point types +template ::value>>* = nullptr> +T foo(T x) { + return x; +} + +// foo only works with integer types +template ::value>>* = nullptr> +K foo(K x) { + return x; +} + +// Calls the first signature +double x_dbl = 1.0; +double y_dbl = foo(x_dbl); + +// Calls the second signature +int x = 1; +int y = foo(x); + +``` + +The second template argument is referred to as a [non-type template +parameter](https://en.cppreference.com/w/cpp/language +template_parameters#Non-type_template_parameter) and has a default +value of `void`. When the templated signature has the correct type +the `enable_if_t ` produces a `void` type which is then made into a +pointer and assigned a default value of `nullptr`. When the templated +signature does not have the correct type, the compiler utilizes +[Substitution Failure Is Not An Error +(SFNIAE)](https://en.cppreference.com/w/cpp/language/sfinae), to +remove the offending signature from the list of possible matches while +continuing to search for the correct signature. + + +For convenience in using this technique, the Math library has +implemented a set of [`requires` type traits](@ref require_meta). +When we pass a type that satisfies the `requires` type trait, the +trait evaluates to `void`. When a type that does not satisfy the +`requires` template parameter is passed, there is a substitution +failure. These traits are used in the template functions by adding a +call to a `requires` type trait to the parameter list. + +Below is an example to illustrate how this technique is used. After +the example, the rest of this page describes what the requires type +traits are, how to use them, and how to add new ones if necessary. + +#### Example + +Here's a function that would have two different template functions for +`stan::math::var` and `double`: + +```c++ +template * = nullptr> +T foo(const T& t) { + // handles primitives + return t; +} + +template * = nullptr> +T foo(const T& t) { + // handles stan::math::var + return t; +} +``` + +When `foo()` is called with a `stan::math::var`, the first template +function matches but not the second. This works because +`requires_var_t` evaluates to `void` whereas +`requires_not_var_t` is a subsitution error causing +the compiler to omit the second definition for `stan::math::var`. + +When `foo()` is called with `double` or `int`, the second template +function matches, but not the first. + + +### Requires<> type traits + +The Stan Math library defines boolean type traits--template +metaprograms that operate on types at compile time--in the +`stan/math{prim, rev, fwd}/meta` folders. Each of these type traits +are named `is_{condition}` and the struct contains a `value` that is +`true` or `false` at compile time. For example, `is_var::value` is +`true` if and only if the type `T` is `stan::math::var_value`. + +We provide [`requires<>` type traits](@ref require_meta) based on the boolean +`is_{condition}` type traits. When types satisfy the condition, the +`requires<>` will evaluate to `void`. When the types do not +satisfy the condition, `requires<>` is an invalid subsitution +and is not used. (See @ref requires_impl for more details.) + +Note: every possible requires<> type trait is not implemented in the +Stan Math library. If one of the missing requires<> type trait is +missing, we can implement it and include it. Please see @ref +requires_dev_guide for more information. + + +#### All requires<> type traits + +For any boolean type trait, below is the list of possible requires<> +type traits. Any `*` should be thought of as a wildcard where a type +traits name is put in its place. For example, for `is_var`, we can +substitute `var` for `*`. + +1. `require_*_t`: A template parameter `T` must satisfy the `is_*` +type trait. This means `require_var_t` is +`void`, but `require_var_t` is an invalid subsitution. + +2. `require_not_*_t`: A template parameter `T` must not satisfy the +`is_*` type trait. + + *NOTE:* The `not` version of the `requires` template parameters +should be used sparingly. Often a `requires` template parameter is +used to specify what types a function should accept. Defining a +function by the types it cannot accept can make understanding what +goes into a function more difficult and error prone. + +3. `require_all_*_t`: All types in the parameter pack of types must +satisfy the `is_*` type trait. + +4. `require_any_*_t`: Any type in the parameter pack of types must +satisfy the `is_*` type trait. + +5. `require_any_not_*_t`: Any type in the parameter pack must not +satisfy the `is_*` type trait. + +6. `require_all_not_*_t`: All types in the parameter pack must not +satisfy the `is_*` type trait. + + `std::vector` and `Eigen` types have additional `requires` + template parameters to detect if the @ref stan::value_type (the + type of the elements of either `std::vector` or the `Eigen` type) + or the @ref stan::scalar_type (the underlying scalar type after + recursively walking through the container types) satisfy a + condition to enable a class or function. + + The container `requires` template parameters have an ending at + their signature of `_vt` and `_st` to symbolize whether you want + to inspect the @ref stan::value_type or @ref stan::scalar_type. + + In the next requires traits, `is_type` is used to represent any + boolean type trait. + +7. `require_*_vt`: A template parameter `T` must satisfy +the `is_*` type trait and `is_type>::value` must +evaluate to true. + +8. `require_not_*_vt`: A template parameter `T` must not +satisfy the `is_*` type trait or `is_type>::value` must +not evaluate to true. + +9. `require_all_*_vt`: All types in the parameter pack of +types must satisfy the `is_*` type trait and all +`is_type>::value` must evaluate to true. + +10. `require_any_*_vt`: Any type in the parameter pack of +types must satisfy the `is_*` type trait and any +`is_type>::value` must evaluate to true. + +11. `require_any_not_*_vt`: At least one type in the +parameter pack must not satisfy the `is_*` type trait and one of +`is_type>::value` must evaluate to false. + +12. `require_all_not_*_vt`: None of the types in the +parameter pack must satisfy the `is_*` type trait and none of +`is_type>::value` must evaluate to true. + +13. `require_*_st`: A template parameter `T` must satisfy +the `is_*` type trait and `is_type>::value` must +evaluate to true. -The [`requires` template parameters](@ref require_meta) type traits are aliases for `std::enable_if_t` that have premade conditions for turning on and off function definitions during compilation. -These are useful for having generalized templates while still overloading a function or class. -You can think of these as "legacy concepts". -These are used in a very similar fashion to C++20's `requires` keyword. +14. `require_not_*_st`: A template parameter `T` must not +satisfy the `is_*` type trait or `is_type>::value` must +not evaluate to true. -`requires` template parameters are [`std::enable_if_t`](https://en.cppreference.com/w/cpp/types/enable_if) aliases such as the following example definition of @ref stan::require_t. +15. `require_all_*_st`: All types in the parameter pack of +types must satisfy the `is_*` type trait and all +`is_type>::value` must evaluate to true. + +16. `require_any_*_st`: Any type in the parameter pack of +types must satisfy the `is_*` type trait and any +`is_type>::value` must evaluate to true. + +17. `require_any_not_*_st`: At least one type in the +parameter pack must not satisfy the `is_*` type trait and one of +`is_type>::value` must evaluate to false. + +18. `require_all_not_*_st`: None of the types in the +parameter pack must satisfy the `is_*` type trait and none of +`is_type>::value` must evaluate to true. + + + +### Implementation details of requires<> type traits {#requires_impl} + +The [`requires` template parameters](@ref require_meta) type traits +are aliases for `std::enable_if_t` that have premade conditions for +turning on and off function definitions during compilation. These are +useful for having generalized templates while still overloading a +function or class. You can think of these as "legacy concepts." +These are used in a very similar fashion to C++20's `requires` +keyword. + +`requires` template parameters are +[`std::enable_if_t`](https://en.cppreference.com/w/cpp/types/enable_if) +aliases such as the following example definition of @ref +stan::require_t. ```cpp template using require_t = std::enable_if_t; ``` -This differes from `std::enable_if_t` in that `std::enable_if_t`'s argument must be boolean, but the alias @ref stan::require_t 's template type `T` must have a valid boolean member named `value`. -This allows us to directly call @ref stan::require_t with type traits instead of having to do the extra step of accessing the type traits boolean member struct value explicity with calls such as `a_type_trait::value`. - -The most common use case for a `requires` template parameters is to overload a function or declare specializations of a class. -For example, the function below will only work on types derived from [`Eigen::DenseBase`](https://eigen.tuxfamily.org/dox/classEigen_1_1DenseBase.html) with only 1 row or column at compile time such as `Eigen::Matrix` or `Eigen::Matrix`. +This differs from `std::enable_if_t` in that `std::enable_if_t`'s +argument must be boolean, but the alias @ref stan::require_t 's +template type `T` must have a valid boolean member named `value`. +This allows us to directly call @ref stan::require_t with type traits +instead of having to do the extra step of accessing the type traits +boolean member struct value explicity with calls such as +`a_type_trait::value`. + +The most common use case for a `requires` template parameters is to +overload a function or declare specializations of a class. For +example, the function below will only work on types derived from +[`Eigen::DenseBase`](https://eigen.tuxfamily.org/dox/classEigen_1_1DenseBase.html) +with only 1 row or column at compile time such as +`Eigen::Matrix` or `Eigen::Matrix`. ```cpp template * = nullptr> @@ -25,15 +340,10 @@ auto my_func(const EigVec& x) { } ``` -The `requires` template parameter is included in the template as a pointer [non-type template parameter](https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter) with a default value of `nullptr`. -This might look a bit odd, but it uses the fact that any non-type template parameter of type `void*` with a default of `nullptr` will be ignored by the compiler. -Under the hood, if any of the `requires` template parameters are successful they will return back a type `void`. -So when we pass a type that the `requires` template parameter accepts we get back a `void* = nullptr`, which is safely ignored by the compiler. -In the case that the type does not satisfy the `requires` template parameter then the function is removed from the set of possible functions the caller could use via SFINAE. -With this scheme we end up having a very nice pattern for writing generic templates for functions while also being able to restrict the set of types that a function can be used for. - -For overloading classes and structs with this scheme we create an initial forward definition with a `void` non-type template parameter. -Then the class overloads use the `requires` template parameter in place of the non-type template parameter. +For overloading classes and structs with this scheme we create an +initial forward definition with a `void` non-type template parameter. +Then the class overloads use the `requires` template parameter in +place of the non-type template parameter. ```cpp template @@ -49,135 +359,136 @@ class a_class> { }; ``` -In the above example, `a_class` has an overload specifically for standard vectors with a @ref stan::scalar_type of @ref stan::math::var . +In the above example, `a_class` has an overload specifically for +standard vectors with a @ref stan::scalar_type of @ref stan::math::var. -The examples below cover the general themes for all of the [`requires` template parameters](@ref require_meta) found in the Stan math library. -Any `*` should be thought of as a wildcard where a type traits name is put in its place. +There are also `requires` template parameters for generically checking +if a type's @ref stan::value_type or @ref stan::scalar_type is +correct. To differentiate them from the Eigen and standard library +vector checks the `vt` and `st` come *before* the type such as +`require_vt_var` which checks if a type `T`'s @ref stan::value_type +satisfies @ref stan::is_var. -- `requires_*_t`: A template parameter `T` must satisfy the `requires` template parameter in order for -the overload to be available. +The `requires` template parameters type traits allow Stan to have more +generic types so that the library can forward Eigen expression and +have better move semantics. For instance, the code below will accept +any arbitrary Eigen expression that, if it's an rvalue, can be +forwarded to another function. ```cpp - // Works for stan::math::var types - template * = nullptr> - auto add(T&& x, T&& y) { return x + y; } + template * = nullptr> + inline auto a_func(Mat1&& m1, Mat2&& m2) { + check_not_nan(m1); + check_not_nan(m2); + return another_func(std::forward(m1), std::forward(m2)); ``` -- `require_not_*_t` : A template parameter `T` must *not* satisfy the `requires` template parameter in order for the overload to be availabe. +## Developing new requires type traits {#requires_dev_guide} -*NOTE:* The `not` version of the `requires` template parameters should be used sparingly. -Often a `requires` template parameter is used to specify what types a function should accept. -Defining a function by the types it cannot accept can make understanding what goes into a function more difficult and error prone. +Every requires type trait is not implemented for every boolean type +trait available. This was done intentionally to allow us to identify +which requires type traits are currently in use. If you +need a requires type trait and it is not currently available, please +feel free to implement the one you need and add a pull request. -```cpp - // Works for anything that is not a std::vector - template * = nullptr> - auto add(T&& x, T&& y) { return x + y; } -``` -- `require_all_*_t` : Takes a parameter pack of types to enable if all types satisfy the check. +### Adding a new boolean type trait -```cpp - // Works if T1 and T2 are complex - template * = nullptr> - auto add(T1&& x, T2&& y) { return x + y; } -``` +If you are adding a new boolean type trait, please add the primary +function template to `stan/math/prim/meta/`, then add any autodiff +specialization to the appropriate `stan/math/{rev, fwd, mix}/meta/` +folder. -- `require_any_*_t` : Takes a parameter pack of types to enable if any of the types satisfy the check. +### Adding a new requires -```cpp - // Works if either T1 or T2 enherit from EigenBase - template * = nullptr> - auto add(T1&& x, T2&& y) { return x + y; } -``` -- `require_not_any_*_t` : Takes a parameter pack of types to enable if any one of the types are not satisfied. +The Stan Math library requires a strict API to ensure consistency for +the `requires`. The below go over all of the possible API +configurations a developer should use when writing a new `requires`. -```cpp - // Works if either neither T1 or T2 are arithmetic - template * = nullptr> - auto add(T1 x, T2 y) { return x + y; } -``` +For the API docs below, let `T` represent the type parameter we want +to check, `is_type` is a generic type trait which will be replaced by +the developer, and `InnerCheck` is a type trait used to check either +the @ref value_type or @ref scalar_type of `T`. -- `require_not_all_*_t` : Takes a parameter pack of types to enable if all of the types are not satisfied. +Each requires ends in `_t`, `_vt`, or `_st`. They differ in the +following ways -```cpp - // Works if neither T1 and T2 are arithmetic - template * = nullptr> - auto add(T1 x, T2 y) { return x + y; } -``` +* `_t` uses `Check` to test the type `T` passed in -`std::vector` and `Eigen` types have additional `requires` template parameters to detect if the @ref stan::value_type (the first underlying type) or the @ref stan::scalar_type (the containers underlying scalar type) satisfy a condition to enable a class or function. + Ex: -The container `requires` template parameters have an ending at their signature of _vt and _st to symbolize whether you want to inspect the @ref stan::value_type or @ref stan::scalar_type. -A function that accepts eigen matrices with floating point value types can be defined as + ``` + // Always decay types coming into the requires + template + require_autodiff_t = require_t>>; + ``` -```cpp - template * = nullptr> - auto add(Mat1&& A, Mat2&& B) { return A + B;} -``` +* `_vt` uses `Check` to test the type `T` passed in and uses + `InnerCheck` to test the @ref value_type of `T` + + ``` + template