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

Generic var templates for operators and std::iterator_trait var/fvar specialization #1525

Merged
merged 39 commits into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8309363
Adds std iterator_traits specialization for fvar and var. The var ope…
SteveBronder Dec 18, 2019
bd9b20c
cleanup the template names
SteveBronder Dec 18, 2019
f8f2e3a
[Jenkins] auto-formatting by clang-format version 5.0.2-svn328729-1~e…
stan-buildbot Dec 18, 2019
8806331
cleanup the unary var templates
SteveBronder Dec 18, 2019
a6b4d81
cleanup the unary var templates
SteveBronder Dec 18, 2019
3868acf
[Jenkins] auto-formatting by clang-format version 5.0.2-svn328729-1~e…
stan-buildbot Dec 18, 2019
5e07ef9
replace var_or_fvar_t with autodiff_t in require_generics tests
SteveBronder Dec 18, 2019
119a7e7
[Jenkins] auto-formatting by clang-format version 6.0.0 (tags/google/…
stan-buildbot Dec 18, 2019
1348dda
remove template from ostream for var
SteveBronder Dec 18, 2019
858e1f2
Merge branch 'cleanup/generic-templates-var' of github.com:stan-dev/m…
SteveBronder Dec 18, 2019
ec6345a
fix order of members in kinsoldata
SteveBronder Dec 18, 2019
4300497
revert beta-binomial changes
SteveBronder Dec 20, 2019
fd35443
Fixup template order for core var impls
SteveBronder Dec 24, 2019
73181b4
Merge commit 'cb961050fd2bb013cc658eb8e3eafaa6bdf1cf38' into HEAD
yashikno Dec 24, 2019
36777ab
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Dec 24, 2019
ad62f4f
Moves cmath var and fvar stuff over to prim and uses initlializer bra…
SteveBronder Dec 30, 2019
fd2a4a8
Merge commit '65aec14f5caea8b9e38a7475afcd4a6681648497' into HEAD
yashikno Dec 30, 2019
930329d
[Jenkins] auto-formatting by clang-format version 5.0.2-svn328729-1~e…
stan-buildbot Dec 30, 2019
6185370
put back the namespace call for exp in gp_periodic_cov and remove sta…
SteveBronder Dec 30, 2019
12ec611
Merge branch 'cleanup/generic-templates-var' of github.com:stan-dev/m…
SteveBronder Dec 30, 2019
88c86e8
merge to develop
SteveBronder Dec 30, 2019
5564039
merge to develop
SteveBronder Dec 31, 2019
a694d1a
Merge branch 'develop' into cleanup/generic-templates-var
SteveBronder Jan 3, 2020
5740cd4
Merge remote-tracking branch 'origin/develop' into cleanup/generic-te…
SteveBronder Jan 5, 2020
4cba532
Merge remote-tracking branch 'origin/develop' into cleanup/generic-te…
SteveBronder Jan 5, 2020
30ee8c9
Remove Var&& arguments for const var& arguments in rev/core
SteveBronder Jan 6, 2020
1a565b6
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Jan 6, 2020
75eaf43
merge to develop
SteveBronder Jan 13, 2020
1342d33
Merge branch 'develop' of https://github.com/stan-dev/math into clean…
Jan 21, 2020
3604151
split cmath.hpp into files w. unit tests
Jan 22, 2020
9eebaa7
fix isnan tests to call right fun
Jan 22, 2020
993ceaa
testing pass by value for var ops instead of by reference
SteveBronder Jan 25, 2020
cd60021
Merge branch 'cleanup/pass-var-by-value', remote-tracking branch 'ori…
SteveBronder Jan 25, 2020
ced2df7
Merge branch 'cleanup/generic-templates-var' of github.com:stan-dev/m…
SteveBronder Jan 25, 2020
8d460ac
Merge commit 'c0d2265f842a1b2df04855fac49d87e9962aa878' into HEAD
yashikno Jan 25, 2020
2795719
[Jenkins] auto-formatting by clang-format version 6.0.0 (tags/google/…
stan-buildbot Jan 25, 2020
99a916b
Merge branch 'develop' of https://github.com/stan-dev/math into clean…
Jan 29, 2020
84f5b26
Merge branch 'develop' of https://github.com/stan-dev/math into clean…
Jan 31, 2020
06ad089
templated var overloads for pow
Jan 31, 2020
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
1 change: 1 addition & 0 deletions stan/math/fwd/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
#include <stan/math/fwd/core/operator_unary_not.hpp>
#include <stan/math/fwd/core/operator_unary_plus.hpp>
#include <stan/math/fwd/core/std_numeric_limits.hpp>
#include <stan/math/fwd/core/std_iterator_traits.hpp>

#endif
42 changes: 42 additions & 0 deletions stan/math/fwd/core/std_iterator_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef STAN_MATH_FWD_CORE_STD_ITERATOR_TRAITS_HPP
#define STAN_MATH_FWD_CORE_STD_ITERATOR_TRAITS_HPP

#include <stan/math/fwd/core/fvar.hpp>
#include <stan/math/prim/meta.hpp>
#include <iterator>

namespace std {
/**
* Specialization of iterator traits for Stan math. These all take
* the form of typedefs.
*/
template <typename T>
struct iterator_traits<stan::math::fvar<T>> {
/**
* Iterator category for traits.
*/
typedef random_access_iterator_tag iterator_category;

/**
* Type for difference between pointers.
*/
typedef ptrdiff_t difference_type;

/**
* Type for value of pointer to values.
*/
typedef stan::math::fvar<T> value_type;

/**
* Type of pointer to variables.
*/
typedef stan::math::fvar<T>* pointer;

/**
* Type of reference to variables.
*/
typedef stan::math::fvar<T>& reference;
};
} // namespace std

#endif
16 changes: 7 additions & 9 deletions stan/math/prim/meta/require_generics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct is_double_or_int
* @tparam The type to check
*/
template <typename T>
struct is_var_or_fvar
struct is_autodiff
SteveBronder marked this conversation as resolved.
Show resolved Hide resolved
: bool_constant<math::disjunction<is_var<std::decay_t<T>>,
is_fvar<std::decay_t<T>>>::value> {};

Expand Down Expand Up @@ -473,24 +473,22 @@ using require_any_not_fvar_t
= require_any_not_t<is_fvar<std::decay_t<Types>>...>;

template <typename T>
using require_var_or_fvar_t = require_t<is_var_or_fvar<T>>;
using require_autodiff_t = require_t<is_autodiff<T>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

[comment]
These are all super useful. Thanks for adding.


template <typename T>
using require_not_var_or_fvar_t = require_not_t<is_var_or_fvar<T>>;
using require_not_autodiff_t = require_not_t<is_autodiff<T>>;
Copy link
Contributor

@bob-carpenter bob-carpenter Dec 24, 2019

Choose a reason for hiding this comment

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

I'd strongly prefer

  • removing the require_ prefix because that's implying a use, not a behavior

  • replace _t with _v because it denotes a boolean value, not a type; this follows the behavior of is_arithmetic_v; enable_if_t ends in _t because it denotes a type (void by default), not a value.

For example, that'd be not_autodiff_v<T> for true if T is not an autodiff type, and usage would be, for example enable_if_t<not_autodiff_v<T>, U>.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removing the require_ prefix because that's implying a use, not a behavior

Can you clarify? I'm not sure i understand how the require part of the name is implying a use over a behaviour. The intent with the require_ in the name is because all those require_*s are trying to emulate a legacy version of C++20 concepts. It's for if you want a universal reference in the function signature for all the weird value types and const combinations. In terms of use/behavior I think my answer is that I think about C++ concepts like compile time contracts (which feels like a behaviour, though idk what that word means in context here)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

replace _t with _v because it denotes a boolean value, not a type; this follows the behavior of is_arithmetic_v; enable_if_t ends in _t because it denotes a type (void by default), not a value.
For example, that'd be not_autodiff_v for true if T is not an autodiff type, and usage would be, for example enable_if_t<not_autodiff_v, U>.

The require_*_t metaprogramming stuff is essentially a convoluted / compact way to write std::enable_if_t<...> i.e. they also return void.

You can think of require_autodiff_t as an alias for enable_if_t<not_autodiff_v<T>, U>.

Also idt we can use C++ compile time constexpr objects with c++1y (anything _v like how std does) since the windows compiler we require compatibility with for Rtools has a bug for those

Copy link
Contributor

Choose a reason for hiding this comment

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

I misunderstood and thought this was a boolean. Now that I see what it's doing, I still think it needs to be renamed. How about enable_if_autodiff_t to enable if it's an autodiff type? That follows the standard library naming conventions more closely than require_autodiff_t.

We can then use disable_if_autodiff_t instead of enable_if_not_autodiff_t.

We can build similar ones for primitives, enable_if_arithmetic_t and disable_if_arithmetic_t.


template <typename... Types>
using require_all_var_or_fvar_t = require_all_t<is_var_or_fvar<Types>...>;
using require_all_autodiff_t = require_all_t<is_autodiff<Types>...>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should these all have doc or are they so close to the ones without _v that they're obvious? cppreference actually doesn't doc these and they doc everything else meticulously.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've been throwing this back and forth with myself. I've leaned towards not doc since aliases are inlined in the docs you can see the full definition

http://mc-stan.org/math/d0/dfb/group__require__base__types.html#gac55eae5c1fc2f38a96336b5f0bc94449


template <typename... Types>
using require_any_var_or_fvar_t = require_any_t<is_var_or_fvar<Types>...>;
using require_any_autodiff_t = require_any_t<is_autodiff<Types>...>;

template <typename... Types>
using require_all_not_var_or_fvar_t
= require_all_not_t<is_var_or_fvar<Types>...>;
using require_all_not_autodiff_t = require_all_not_t<is_autodiff<Types>...>;

template <typename... Types>
using require_any_not_var_or_fvar_t
= require_any_not_t<is_var_or_fvar<Types>...>;
using require_any_not_autodiff_t = require_any_not_t<is_autodiff<Types>...>;

template <typename T>
using require_stan_scalar_t = require_t<is_stan_scalar<T>>;
Expand Down
2 changes: 2 additions & 0 deletions stan/math/rev/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include <stan/math/rev/core/build_vari_array.hpp>
#include <stan/math/rev/core/chainable_alloc.hpp>
#include <stan/math/rev/core/chainablestack.hpp>
#include <stan/math/rev/core/cmath.hpp>
#include <stan/math/rev/core/init_chainablestack.hpp>
#include <stan/math/rev/core/std_iterator_traits.hpp>
#include <stan/math/rev/core/ddv_vari.hpp>
#include <stan/math/rev/core/dv_vari.hpp>
#include <stan/math/rev/core/dvd_vari.hpp>
Expand Down
119 changes: 119 additions & 0 deletions stan/math/rev/core/cmath.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#ifndef STAN_MATH_REV_CORE_CMATH_HPP
Copy link
Contributor

Choose a reason for hiding this comment

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

This is only a few things from <cmath>---the rest of the functions defined in that header follow the Stan convention of being broken into their own files. I don't think we want to stray from that convention here. Sorry for all the extra work---it's one of the reasons I was dreading breaking this all out into tests and definitions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

looking at cmath it looks like all these are in the cmath header? For clarification you want signbit, isinf, isfinite, isnan, copysign, and isnormal to be in their own header files?

#define STAN_MATH_REV_CORE_CMATH_HPP

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

namespace stan {
namespace math {

/**
* Return `true` if the specified argument is negative and `false`
* otherwise.
*
* Overloads `std::signbit` from `<cmath>` for argument-dependent
* lookup.
*
* @tparam T type of argument
* @param[in] v argument
* @return `true` if the argument is negative
*/
template <typename T, require_autodiff_t<T>...>
inline bool signbit(T&& v) {
using std::signbit;
return signbit(v.val());
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we write this with value_of_rec(v) and make it generic across our autodiff types? Or havit recurse with v.val() if both forward-mode and reverse-mode support .val(). Either way, it can go in prim as it's not mentioning any autodiff types itself.

Copy link
Collaborator Author

@SteveBronder SteveBronder Dec 30, 2019

Choose a reason for hiding this comment

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

Yeah since fvar and var have .val() we can move this to prim. Should we also have each of these for arithmetic types or just rely on the cmath impl for those (in case someone calls stan::math::signbit(a) where a is double)?

If we wanted to be v smart we could break these up to have signatures for

  1. Arithmetic types
  2. AD types with an arithmetic return from .val()
  3. AD types with heavier types in .val() (i.e. complex<>/fvar<var>/fvar<fvar<var>>)

Then 1 and 2 would pass by copy while 3 would be able to forward along / pass a reference to .val()

}

/**
* Return true if specified argument is infinite (positive or
* negative).
*
* Overloads `std::isinf` from `<cmath>` for argument-dependent
* lookup.
*
* @tparam T type of argument
* @param[in] v argument
* @return true if argument is infinite
*/
template <typename T, require_autodiff_t<T>...>
inline bool isinf(T&& v) {
SteveBronder marked this conversation as resolved.
Show resolved Hide resolved
using std::isinf;
return isinf(v.val());
}

/**
* Return true if specified argument is finite (not infinite and not
* not-a-number).
*
* Overloads `std::isfinite` from `<cmath>` for argument-dependent
* lookup.
*
* @tparam T type of argument
* @param[in] v argument
* @return true if argument is finite
*/
template <typename T, require_autodiff_t<T>...>
inline bool isfinite(T&& v) {
SteveBronder marked this conversation as resolved.
Show resolved Hide resolved
using std::isfinite;
return isfinite(v.val());
}

/**
* Return true if specified argument is not-a-number.
*
* Overloads `std::isnan` from `<cmath>` for argument-dependent
* lookup.
*
* @tparam T type of argument
* @param[in] v argument
* @return true if argument is not-a-number
*/
template <typename T, require_autodiff_t<T>...>
inline bool isnan(T&& v) {
using std::isnan;
return isnan(v.val());
}

/**
* Return true if specified argument is normal. A number is normal if
* it is finite, non-zero and not subnormal.
*
* Overloads `std::isnormal` from `<cmath>` for argument-dependent
* lookup.
*
* @tparam T type of argument
* @param[in] v argument
* @return true if argument is normal
*/
template <typename T, require_autodiff_t<T>...>
inline bool isnormal(T&& v) {
using std::isnormal;
return isnormal(v.val());
}

/**
* Return the negation of the first argument if the first and second
* argument have different signs, otherwise return a copy of the first
* argument. For the sake of this function, zero is considered
* positive. This function uses negation rather than literally copying
* signs to preserve derivatives.
*
* Overload of `std::copysign` from `cmath` for argument-dependent
* lookup.
*
* @tparam T type of first argument
* @tparam U type of second argument
* @param[in] x first complex argument
* @param[in] y second complex argument
* @return copy of second argument, negated if necessary to match sign
* of first argument
*/
template <typename T, typename U>
inline T copysign(const T& x, const U& y) {
// 0 is considered positive
Copy link
Contributor

Choose a reason for hiding this comment

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

This code comment is redundant given the API doc (and apoloties for putting it there originally).

return (x < 0 && y >= 0) || (x > 0 && y < 0) ? -x : x;
}
} // namespace math
} // namespace stan

#endif
18 changes: 15 additions & 3 deletions stan/math/rev/core/operator_addition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <stan/math/rev/core/vv_vari.hpp>
#include <stan/math/rev/core/vd_vari.hpp>
#include <stan/math/prim/scal/fun/is_any_nan.hpp>
#include <stan/math/prim/meta.hpp>
#include <limits>

namespace stan {
Expand Down Expand Up @@ -73,11 +74,14 @@ class add_vd_vari : public op_vd_vari {
\end{cases}
\f]
*
* @tparam Var1 value type of a var
* @tparam Var2 value type of a var
SteveBronder marked this conversation as resolved.
Show resolved Hide resolved
* @param a First variable operand.
* @param b Second variable operand.
* @return Variable result of adding two variables.
*/
inline var operator+(const var& a, const var& b) {
template <typename Var1, typename Var2, require_all_var_t<Var1, Var2>...>
Copy link
Contributor

Choose a reason for hiding this comment

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

If require_all_var_t requires Var1 == var and Var2 == var, why not just use the const var&& arguments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The direct answer to this is that a function signature with const var&& would only accept constant rvalue types. The Var1&& and Var2&& in the function signature are universal references. Combined with require_all_var_t the operators here accept var&, const var&, and var&& (they could accept const var&& though idt const rvalues are a thing). It allows the caller (callee?) to do something like auto the_sign = copysign(std::forward<FVar>(an_fvar)); to move a fvar, for example, when the call site can tell a_fvar is movable.

The longer indirect answer is that if we want "universal references" in function signatures for perfect forwarding then you can't have any declaration specifiers alongside the type specifier for each function parameter.

Would we ever allow a var to be moved?

inline auto operator+(Var1&& a, Var2&& b) {
return var(new internal::add_vv_vari(a.vi_, b.vi_));
}

Expand All @@ -88,11 +92,15 @@ inline var operator+(const var& a, const var& b) {
*
* \f$\frac{d}{dx} (x + c) = 1\f$.
*
* @tparam Var value type of a var
* @tparam Arith An arithmetic type
* @param a First variable operand.
* @param b Second scalar operand.
* @return Result of adding variable and scalar.
*/
inline var operator+(const var& a, double b) {
template <typename Var, typename Arith, require_var_t<Var>...,
require_arithmetic_t<Arith>...>
inline auto operator+(Var&& a, Arith b) {
if (b == 0.0) {
return a;
}
Expand All @@ -106,11 +114,15 @@ inline var operator+(const var& a, double b) {
*
* \f$\frac{d}{dy} (c + y) = 1\f$.
*
* @tparam Arith An arithmetic type
* @tparam Var value type of a var
* @param a First scalar operand.
* @param b Second variable operand.
* @return Result of adding variable and scalar.
*/
inline var operator+(double a, const var& b) {
template <typename Arith, typename Var, require_arithmetic_t<Arith>...,
require_var_t<Var>...>
inline auto operator+(Arith a, Var&& b) {
if (a == 0.0) {
return b;
}
Expand Down
8 changes: 5 additions & 3 deletions stan/math/rev/core/operator_divide_equal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@

#include <stan/math/rev/core/var.hpp>
#include <stan/math/rev/core/operator_division.hpp>
#include <stan/math/prim/meta.hpp>

namespace stan {
namespace math {

inline var& var::operator/=(const var& b) {
template <typename Var, require_var_t<Var>...>
inline var& var::operator/=(Var&& b) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is different between require_var_t<Var> and just removing the template parameter and using var instead of Var? I'd think given that it's an overload, we'd also prefer the more specific type to match.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What is different between require_var_t and just removing the template parameter and using var instead of Var? I'd think given that it's an overload, we'd also prefer the more specific type to match.

As with the comment above about universal references the Var&& and require_var_t combo here will match against any var type with whatever deceleration specifiers it has on it (const etc.). So it gives us the most specific type as long as the paremeters decayed type is a var.

Does that make sense? ftr I find the universal reference thing in C++the most weird/legalize part of the language

vi_ = new internal::divide_vv_vari(vi_, b.vi_);
return *this;
}

inline var& var::operator/=(double b) {
template <typename Arith, require_arithmetic_t<Arith>...>
inline var& var::operator/=(Arith b) {
if (b == 1.0) {
return *this;
}
Expand Down
18 changes: 15 additions & 3 deletions stan/math/rev/core/operator_division.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stan/math/rev/core/vd_vari.hpp>
#include <stan/math/rev/core/dv_vari.hpp>
#include <stan/math/prim/scal/fun/is_any_nan.hpp>
#include <stan/math/prim/meta.hpp>
#include <limits>

namespace stan {
Expand Down Expand Up @@ -80,12 +81,15 @@ class divide_dv_vari : public op_dv_vari {
\end{cases}
\f]
*
* @tparam Var1 value type of a var
* @tparam Var2 value type of a var
* @param a First variable operand.
* @param b Second variable operand.
* @return Variable result of dividing the first variable by the
* second.
*/
inline var operator/(const var& a, const var& b) {
template <typename Var1, typename Var2, require_all_var_t<Var1, Var2>...>
inline auto operator/(Var1&& a, Var2&& b) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar comment/request for var as before. I'm going to stop pointing these out as the pattern seems to be used everywhere.

return var(new internal::divide_vv_vari(a.vi_, b.vi_));
}

Expand All @@ -96,11 +100,15 @@ inline var operator/(const var& a, const var& b) {
*
* \f$\frac{\partial}{\partial x} (x/c) = 1/c\f$.
*
* @tparam Var value type of a var
* @tparam Arith An arithmetic type
* @param a Variable operand.
* @param b Scalar operand.
* @return Variable result of dividing the variable by the scalar.
*/
inline var operator/(const var& a, double b) {
template <typename Var, typename Arith, require_var_t<Var>...,
require_arithmetic_t<Arith>...>
inline var operator/(Var&& a, Arith b) {
Copy link
Contributor

Choose a reason for hiding this comment

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

[optional]
The right doc for a is dividend, for b is divisor, and for the result is quotient.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Word!

if (b == 1.0) {
return a;
}
Expand All @@ -114,11 +122,15 @@ inline var operator/(const var& a, double b) {
*
* \f$\frac{d}{d y} (c/y) = -c / y^2\f$.
*
* @tparam Arith An arithmetic type
* @tparam Var value type of a var
* @param a Scalar operand.
* @param b Variable operand.
* @return Variable result of dividing the scalar by the variable.
*/
inline var operator/(double a, const var& b) {
template <typename Arith, typename Var, require_arithmetic_t<Arith>...,
require_var_t<Var>...>
inline auto operator/(Arith a, Var&& b) {
return var(new internal::divide_dv_vari(a, b.vi_));
}

Expand Down
Loading