Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing scalar prim signatures #2612

Merged
merged 35 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
103cc03
write script to generate tests
rok-cesnovar Oct 31, 2021
a4ddd66
fix 2 typos
rok-cesnovar Nov 6, 2021
3411c67
add prim scalar signatures for missing functions
rok-cesnovar Nov 6, 2021
e6d68e8
fix runtest
rok-cesnovar Nov 6, 2021
4075c09
add requires
rok-cesnovar Nov 6, 2021
af70d36
formatting
rok-cesnovar Nov 6, 2021
872803c
add to GHA
rok-cesnovar Nov 6, 2021
969d378
remove using std::fmod in tests
rok-cesnovar Nov 6, 2021
64fa890
fix test namespaces
rok-cesnovar Nov 7, 2021
02bee11
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Nov 7, 2021
d7af743
add separate implementation for pow/fmod
rok-cesnovar Nov 7, 2021
dd142c3
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Nov 7, 2021
228a05f
fix require for pow
rok-cesnovar Nov 8, 2021
43c3a99
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Nov 8, 2021
5769a7b
Revert "add to GHA"
rok-cesnovar Nov 24, 2021
50e54f7
Revert "write script to generate tests"
rok-cesnovar Nov 24, 2021
e60adb1
fix doxygen and return types
rok-cesnovar Nov 24, 2021
b3f9ff3
optional test fixes
rok-cesnovar Nov 24, 2021
dc23f52
add integer tests to pow_test
rok-cesnovar Nov 24, 2021
8f730bb
cleanup
rok-cesnovar Nov 24, 2021
2301ba1
Merge commit 'ddf8946198ae531954456996bc2f2cf62374c3e0' into HEAD
yashikno Nov 24, 2021
0f4383d
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Nov 24, 2021
465a551
fix pow prim test
rok-cesnovar Nov 26, 2021
4a10aee
add sig_test
rok-cesnovar Nov 26, 2021
00397e9
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Nov 26, 2021
79d0351
move test file
rok-cesnovar Nov 26, 2021
d9795d2
fix test issue
rok-cesnovar Nov 26, 2021
ed76fdd
Merge branch 'develop' into missing_prim_scalar_sigs
rok-cesnovar Dec 23, 2021
e13933d
add test for complex args for existing tests
rok-cesnovar Dec 23, 2021
0925494
add missing implementations for arg, conj, norm, proj
rok-cesnovar Dec 23, 2021
382be99
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Dec 23, 2021
aac0a55
fix missing header in log
rok-cesnovar Dec 23, 2021
199da11
fix pow
rok-cesnovar Dec 23, 2021
27b3748
[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.0…
stan-buildbot Dec 23, 2021
943ccd3
Merge branch 'develop' into missing_prim_scalar_sigs
rok-cesnovar Jan 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Header checks
name: Header & signature checks

on:
pull_request:
Expand Down Expand Up @@ -80,3 +80,19 @@ jobs:
run: |
echo "STAN_NO_RANGE_CHECKS=true" > make/local
./runTests.py -j2 ./test/unit/math/prim/err/
prim_scalar_sig_checks:
name: Prim scalar signature schecks
runs-on: ubuntu-latest

steps:
- uses: n1hility/cancel-previous-runs@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
workflow: c-cpp.yml
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master' && github.ref != 'refs/heads/develop'"

- uses: actions/checkout@v2

- name: Run Prim scalar signature schecks
run: |
./runTests.py test/scalar_prim_sig
16 changes: 16 additions & 0 deletions runTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,21 @@ def handleExpressionTests(tests, only_functions, n_test_files):
-1,
)

def handleScalarPrimSigTests(tests):
scalar_prim_sig_tests = False
for n, i in list(enumerate(tests))[::-1]:
if "test/scalar_prim_sig" in i or "test/scalar_prim_sig" in i:
del tests[n]
scalar_prim_sig_tests = True
if scalar_prim_sig_tests:
HERE = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(HERE, "test"))
sys.path.append(os.path.join(HERE, "test/expressions"))
import generate_prim_sig_tests

generate_prim_sig_tests.main()
tests.append("test/expressions/prim_sig_test.cpp")


def checkToolchainPathWindows():
if isWin():
Expand Down Expand Up @@ -357,6 +372,7 @@ def main():
else:
num_expr_test_files = inputs.e
handleExpressionTests(tests, inputs.only_functions, num_expr_test_files)
handleScalarPrimSigTests(tests)

tests = findTests(inputs.tests, inputs.f, inputs.do_jumbo)

Expand Down
1 change: 1 addition & 0 deletions stan/math/prim/fun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <stan/math/prim/fun/asinh.hpp>
#include <stan/math/prim/fun/assign.hpp>
#include <stan/math/prim/fun/atan.hpp>
#include <stan/math/prim/fun/atan2.hpp>
#include <stan/math/prim/fun/atanh.hpp>
#include <stan/math/prim/fun/autocorrelation.hpp>
#include <stan/math/prim/fun/autocovariance.hpp>
Expand Down
2 changes: 1 addition & 1 deletion stan/math/prim/fun/abs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace math {
* Return the absolute value of the specified arithmetic argument.
* The return type is the same as the argument type.
*
* @tparam T type of arument (must be arithmetic)
* @tparam T type of argument (must be arithmetic)
* @param x argument
* @return absolute value of argument
*/
Expand Down
29 changes: 29 additions & 0 deletions stan/math/prim/fun/atan2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef STAN_MATH_PRIM_FUN_ATAN2_HPP
#define STAN_MATH_PRIM_FUN_ATAN2_HPP

#include <stan/math/prim/core.hpp>
#include <stan/math/prim/meta.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
* Returns the principal value of the arg tangent of y/x,
* expressed in radians.
*
* @tparam T1 type of first argument (must be arithmetic)
* @tparam T2 type of second argument (must be arithmetic)
* @param y first argument
* @param x second argument
* @return `atan2()` value of the arguments
*/
template <typename T1, typename T2, require_all_arithmetic_t<T1, T2>* = nullptr>
return_type_t<T1, T2> atan2(T1 y, T2 x) {
return std::atan2(y, x);
}

} // namespace math
} // namespace stan

#endif
14 changes: 14 additions & 0 deletions stan/math/prim/fun/fmod.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@
namespace stan {
namespace math {

/**
* The mod() function implementation for scalar inputs.
*
* @tparam T1 type of first input
* @tparam T2 type of second input
* @param a First input
* @param b Second input
* @return fmod function applied to the two inputs.
*/
template <typename T1, typename T2, require_all_arithmetic_t<T1, T2>* = nullptr>
inline auto fmod(const T1& a, const T2& b) {
return std::fmod(a, b);
}

/**
* Enables the vectorised application of the fmod function,
* when the first and/or second arguments are containers.
Expand Down
15 changes: 15 additions & 0 deletions stan/math/prim/fun/max.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@
namespace stan {
namespace math {

/**
* Returns the maximum coefficient of the two specified
* scalar arguments.
*
* @tparam T1 type of first argument (must be arithmetic)
* @tparam T2 type of second argument (must be arithmetic)
* @param x first argument
* @param y second argument
* @return maximum value of the two arguments
*/
template <typename T1, typename T2, require_all_arithmetic_t<T1, T2>* = nullptr>
return_type_t<T1, T2> max(T1 x, T2 y) {
return std::max(x, y);
}

/**
* Returns the maximum coefficient in the specified
* matrix, vector, row vector or std vector.
Expand Down
15 changes: 15 additions & 0 deletions stan/math/prim/fun/min.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@
namespace stan {
namespace math {

/**
* Returns the minimum coefficient of the two specified
* scalar arguments.
*
* @tparam T1 type of first argument (must be arithmetic)
* @tparam T2 type of second argument (must be arithmetic)
* @param x first argument
* @param y second argument
* @return minimum value of the two arguments
*/
template <typename T1, typename T2, require_all_arithmetic_t<T1, T2>* = nullptr>
return_type_t<T1, T2> min(T1 x, T2 y) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this return an integer if x and y are both integers? Usually, the minimum promotion target we've used is double. If not, you have to be careful with std::min because it takes a single template class parameter which won't be matched if one argument's int and the other double. You can get around that calling fmin, but that won't return int types for int arguments. Or you can just have an overload for (int, int) args, and I think it'd be more specific than this template.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this return an integer if x and y are both integers?

Yes, it will. That is tested here: https://github.com/stan-dev/math/blob/develop/test/unit/math/mix/meta/return_type_test.cpp#L17

return std::min(x, y);
}

/**
* Returns the minimum coefficient in the specified
* matrix, vector, row vector or std vector.
Expand Down
14 changes: 14 additions & 0 deletions stan/math/prim/fun/pow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ inline complex_return_t<U, V> complex_pow(const U& x, const V& y) {
}
} // namespace internal

/**
* The pow() function implementation for scalar inputs.
*
* @tparam T1 type of first input
* @tparam T2 type of second input
* @param a First input
* @param b Second input
* @return pow function applied to the two inputs.
*/
template <typename T1, typename T2, require_all_arithmetic_t<T1, T2>* = nullptr>
inline auto pow(const T1& a, const T2& b) {
return std::pow(a, b);
}

/**
* Enables the vectorised application of the pow function, when
* the first and/or second arguments are containers.
Expand Down
2 changes: 1 addition & 1 deletion stan/math/rev/core/count_vars.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ inline size_t count_vars_impl(size_t count, const var& x, Pargs&&... args) {
* Arguments without vars contribute zero to the total number of vars.
*
* Return the running total number of vars plus the number of
* vars in the remaining aruments.
* vars in the remaining arguments.
*
* @tparam Arith An object that is either arithmetic or holds arithmetic
* types
Expand Down
90 changes: 90 additions & 0 deletions test/generate_prim_sig_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/python
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation for not just writing unit tests for these? These tests are all very much boilerplate, but I don't see the need for moving to Python. One of the reasons I prefer basic tests is that they're easy to find in the code. With this kind of thing, I'll never be able to find where math(int, double) gets tested. And these should all be tested on a mix of integer and real inputs to make sure promotion and return type calculation works as expected.

And where are we testing that they produce the same values as the C++ functions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decision to make these tests automatic is mostly because I wanted to test that all scalar argument functions work correctly.

But I guess we only need to test std:: shadowing functions, which is a non-changing small list of functions. Will revert to that.


from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
import numbers
import sys

from sig_utils import handle_function_list, get_signatures
from signature_parser import SignatureParser
from code_generator import CodeGenerator

src_folder = "./test/expressions/"
build_folder = "./test/expressions/"

test_code_template = """
TEST(PrimSigsTests, {test_name}) {{
/*
{comment}
*/
{code}
{check}
}}
"""


def main():
"""
Generates tests to check primitive scalar functions.

The tests check whether the primitive scalar functions are defined in the
stan::math namespace and that no stan::math::var objects are created when
they are used. If a var object is created, some of the input arguments
were cast into a var and the reverse mode function was called.

"""

tests = []

default_checks = True
signatures = set(get_signatures())

for n, signature in enumerate(signatures):
sp = SignatureParser(signature)

# skip ignored signatures if running the default checks
if default_checks and sp.is_ignored():
continue

# skip signatures without inputs that can be eigen types
if (
not sp.has_scalar_only_args()
or sp.returns_complex()
or sp.is_nullary()
or sp.is_rng()
):
continue

cg = CodeGenerator()

arg_list_no_expression = cg.build_arguments(
sp, sp.number_arguments() * ["Prim"], size=1
)

# Check results with expressions and without are the same
cpp_function_name = "stan::math::" + sp.function_name
result_1 = cg.function_call_assign(cpp_function_name, *arg_list_no_expression)
result_2 = cg.function_call_assign(cpp_function_name, *arg_list_no_expression)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result_1 and result_2 have the same definition. Should one of these have a different arg list?

cg.expect_eq(result_1, result_2)

var_check = (
"EXPECT_EQ(stan::math::ChainableStack::instance_->var_stack_.size(), 0);\n"
+ "EXPECT_EQ(stan::math::ChainableStack::instance_->var_nochain_stack_.size(), 0);\n"
+ "EXPECT_EQ(stan::math::ChainableStack::instance_->var_alloc_stack_.size(), 0);\n"
)

tests.append(
test_code_template.format(
overload="Prim",
comment=signature.strip(),
test_name=sp.function_name + repr(n),
code=cg.cpp(),
check=var_check,
)
)

with open(src_folder + "prim_sig_test.cpp", "w") as out:
out.write(
"#include <stan/math.hpp>\n\n#include <test/expressions/expression_test_helpers.hpp>\n\n"
)
for test in tests:
out.write(test)
2 changes: 1 addition & 1 deletion test/prob/cauchy/cauchy_cdf_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ class AgradCdfCauchy : public AgradCdfTest {
const T_scale& sigma,
const T3&, const T4&,
const T5&) {
using stan::math::atan2;
using stan::math::pi;
using std::atan2;
return atan2(y - mu, sigma) / pi() + 0.5;
}
};
6 changes: 3 additions & 3 deletions test/prob/frechet/frechet_ccdf_log_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ class AgradCcdfLogFrechet : public AgradCcdfLogTest {
stan::return_type_t<T_y, T_shape, T_scale> ccdf_log_function(
const T_y& y, const T_shape& alpha, const T_scale& sigma, const T3&,
const T4&, const T5&) {
using std::exp;
using std::log;
using std::pow;
using stan::math::exp;
using stan::math::log;
using stan::math::pow;
return log(1.0 - exp(-pow(sigma / y, alpha)));
}
};
4 changes: 2 additions & 2 deletions test/prob/frechet/frechet_cdf_log_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ class AgradCdfLogFrechet : public AgradCdfLogTest {
stan::return_type_t<T_y, T_shape, T_scale> cdf_log_function(
const T_y& y, const T_shape& alpha, const T_scale& sigma, const T3&,
const T4&, const T5&) {
using std::log;
using std::pow;
using stan::math::log;
using stan::math::pow;
return -pow(sigma / y, alpha);
}
};
4 changes: 2 additions & 2 deletions test/prob/frechet/frechet_cdf_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class AgradCdfFrechet : public AgradCdfTest {
const T_scale& sigma,
const T3&, const T4&,
const T5&) {
using std::log;
using std::pow;
using stan::math::log;
using stan::math::pow;
return exp(-pow(sigma / y, alpha));
}
};
4 changes: 2 additions & 2 deletions test/prob/frechet/frechet_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ class AgradDistributionsFrechet : public AgradDistributionTest {
const T_y& y, const T_shape& alpha, const T_scale& sigma, const T3&,
const T4&, const T5&) {
using stan::math::include_summand;
using stan::math::log;
using stan::math::multiply_log;
using stan::math::pow;
using stan::math::value_of;
using std::log;
using std::pow;

return log(alpha) + multiply_log(alpha, sigma) - multiply_log(alpha + 1, y)
- pow(sigma / y, alpha);
Expand Down
2 changes: 1 addition & 1 deletion test/prob/loglogistic/loglogistic_cdf_test.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Arguments: Doubles, Doubles, Doubles
#include <stan/math/prim.hpp>

using stan::math::pow;
using stan::math::var;
using std::numeric_limits;
using std::pow;
using std::vector;

class AgradCdfLogistic : public AgradCdfTest {
Expand Down
8 changes: 4 additions & 4 deletions test/prob/normal/normal_cdf_log_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ class AgradCdfLogNormal : public AgradCdfLogTest {
stan::return_type_t<T_y, T_loc, T_scale> cdf_log_function(
const T_y& y, const T_loc& mu, const T_scale& sigma, const T3&, const T4&,
const T5&) {
using stan::math::exp;
using stan::math::log;
using stan::math::log1p;
using stan::math::LOG_HALF;
using stan::math::pow;
using stan::math::SQRT_PI;
using stan::math::SQRT_TWO;
using std::exp;
using std::log;
using std::log1p;
using std::pow;

stan::return_type_t<T_y, T_loc, T_scale> cdf_log(0.0);

Expand Down
4 changes: 1 addition & 3 deletions test/prob/pareto_type_2/pareto_type_2_ccdf_log_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ class AgradCcdfLogParetoType2 : public AgradCcdfLogTest {
stan::return_type_t<T_y, T_loc, T_scale, T_shape> ccdf_log_function(
const T_y& y, const T_loc& mu, const T_scale& lambda,
const T_shape& alpha, const T4&, const T5&) {
using std::log;
using std::pow;
return log(pow(1.0 + (y - mu) / lambda, -alpha));
return stan::math::log(stan::math::pow(1.0 + (y - mu) / lambda, -alpha));
}
};
4 changes: 1 addition & 3 deletions test/prob/pareto_type_2/pareto_type_2_cdf_log_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class AgradCdfLogParetoType2 : public AgradCdfLogTest {
stan::return_type_t<T_y, T_loc, T_scale, T_shape> cdf_log_function(
const T_y& y, const T_loc& mu, const T_scale& lambda,
const T_shape& alpha, const T4&, const T5&) {
using stan::math::log1m;
using std::pow;
return log1m(pow(1.0 + (y - mu) / lambda, -alpha));
return stan::math::log1m(stan::math::pow(1.0 + (y - mu) / lambda, -alpha));
}
};
Loading