stator
A math, geometry, and utility library
binary_ops.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2017 Marcus N Campbell Bannerman <[email protected]>
3 
4  This file is part of stator.
5 
6  stator is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  stator is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with stator. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #pragma once
21 
22 namespace sym {
23  namespace detail {
24  struct NoIdentity {
25  template<class T>
26  constexpr bool operator==(const T&) const { return false; }
27  };
28  };
29 
32  template<class LHS, typename Op, class RHS>
34  const LHS _l;
35  const RHS _r;
36  BinaryOp(const LHS& l, const RHS& r): _l(l), _r(r) {}
37  };
38 
39  template<class LHS, class RHS, class Op, class Var, class Arg>
41  -> STATOR_AUTORETURN_BYVALUE(Op::apply(sub(f._l, x), sub(f._r, x)));
42 
43  template<class LHS, class RHS,
44  typename = typename std::enable_if<(std::is_arithmetic<LHS>::value && std::is_arithmetic<RHS>::value)>::type>
45  auto pow(const LHS& l, const RHS& r) -> STATOR_AUTORETURN(std::pow(l, r));
46 
47  template<class LHS, std::intmax_t num, std::intmax_t den,
48  typename = typename std::enable_if<std::is_arithmetic<LHS>::value>::type>
49  auto pow(const LHS& l, const C<num, den>& r) -> STATOR_AUTORETURN(std::pow(l, double(r)));
50 
51  template<class LHS,
52  typename = typename std::enable_if<std::is_arithmetic<LHS>::value>::type>
53  auto pow(const LHS& l, const C<1,3>& r) -> STATOR_AUTORETURN(std::cbrt(l));
54 
55  template<class LHS,
56  typename = typename std::enable_if<std::is_arithmetic<LHS>::value>::type>
57  auto pow(const LHS& l, const C<1,2>& r) -> STATOR_AUTORETURN(std::sqrt(l));
58 
59  template<class LHS,
60  typename = typename std::enable_if<std::is_base_of<Eigen::EigenBase<LHS>, LHS>::value>::type>
61  auto pow(const LHS& l, const C<2,1>& r) -> STATOR_AUTORETURN_BYVALUE(l.squaredNorm());
62 
63  template<class LHS>
64  auto pow(const LHS& l, const C<1,1>& r) -> STATOR_AUTORETURN(l);
65 
66  namespace {
74  template<size_t Power>
75  struct PowerOpSub {
76  template<class Arg_t>
77  static auto eval(Arg_t x)
78  -> STATOR_AUTORETURN(PowerOpSub<Power-1>::eval(x) * x)
79  };
80 
81  template<>
82  struct PowerOpSub<1> {
83  template<class Arg_t> static Arg_t eval(Arg_t x) { return x; }
84  };
85 
86  template<>
87  struct PowerOpSub<0> {
88  template<class Arg_t> static Unity eval(Arg_t x) { return Unity(); }
89  };
90  }
91 
92  template<std::intmax_t num1, std::intmax_t den1, std::intmax_t num2>
93  auto pow(const C<num1, den1>& l, const C<num2,1>& r)
94  -> STATOR_AUTORETURN(PowerOpSub<num2>::eval(sub(l, num2)));
95 
96  namespace detail {
97  enum class Associativity { LEFT, RIGHT, NONE };
98 
99  template<class Op>
100  constexpr int RBP() {
101  return Op::leftBindingPower + (Op::associativity == Associativity::LEFT) + (Op::associativity == Associativity::NONE);
102  }
103 
104  template<class Op>
105  constexpr int NBP() {
106  return Op::leftBindingPower - (Op::associativity == Associativity::RIGHT) - (Op::associativity == Associativity::NONE);
107  }
108 
109  struct Add {
110  static constexpr int leftBindingPower = 20;
111  static constexpr auto associativity = Associativity::LEFT;
112  static constexpr bool commutative = true;
113  static constexpr bool associative = true;
118  static inline std::string repr() { return "+"; }
119  //Apply has to accept by const ref, as returned objs may reference/alias the arguments, so everything needs at least the parent scope
120  template<class L, class R> static auto apply(const L& l, const R& r) -> STATOR_AUTORETURN(l + r);
121  };
122 
123  struct Subtract {
124  static constexpr int leftBindingPower = 20;
125  static constexpr auto associativity = Associativity::LEFT;
126  static constexpr bool commutative = false;
127  static constexpr bool associative = false;
132  static inline std::string repr() { return "-"; }
133  template<class L, class R> static auto apply(const L& l, const R& r) -> STATOR_AUTORETURN(l - r);
134  };
135 
136  struct Multiply {
137  static constexpr int leftBindingPower = 30;
138  static constexpr auto associativity = Associativity::LEFT;
139  static constexpr bool commutative = true;
140  static constexpr bool associative = true;
143  typedef Null left_zero;
144  typedef Null right_zero;
145  static inline std::string repr() { return "*"; }
146  template<class L, class R> static auto apply(const L& l, const R& r) -> STATOR_AUTORETURN(l * r);
147  };
148 
149  struct Divide {
150  static constexpr int leftBindingPower = 30;
151  static constexpr auto associativity = Associativity::LEFT;
152  static constexpr bool commutative = false;
153  static constexpr bool associative = false;
156  typedef Null left_zero;
158  static inline std::string repr() { return "/"; }
159  template<class L, class R> static auto apply(const L& l, const R& r) -> STATOR_AUTORETURN(l / r);
160  };
161 
162  struct Power {
163  static constexpr int leftBindingPower = 40;
164  static constexpr auto associativity = Associativity::RIGHT;
165  static constexpr bool commutative = false;
166  static constexpr bool associative = false;
167  static inline std::string repr() { return "^"; }
171  typedef Unity left_zero;
172  //We have to prevent silly powers (i.e. matrix powers) otherwise
173  //the MSVSC compiler gets confused
174  template<class L, class R,
175  typename = typename std::enable_if<!std::is_base_of<Eigen::EigenBase<R>, R>::value>::type>
176  static auto apply(const L& l, const R& r) -> STATOR_AUTORETURN(pow(l, r));
177  };
178  }
179 
180  template<class LHS, class RHS> using AddOp = BinaryOp<LHS, detail::Add, RHS>;
181  template<class LHS, class RHS> using SubtractOp = BinaryOp<LHS, detail::Subtract, RHS>;
182  template<class LHS, class RHS> using MultiplyOp = BinaryOp<LHS, detail::Multiply, RHS>;
183  template<class LHS, class RHS> using DivideOp = BinaryOp<LHS, detail::Divide, RHS>;
184  template<class LHS, class RHS> using PowerOp = BinaryOp<LHS, detail::Power, RHS>;
185 
186  template <class Op, class OverOp>
187  struct left_distributive : std::false_type {};
188 
189  template <class Op, class OverOp>
190  struct right_distributive { static constexpr bool value = Op::commutative && left_distributive<Op,OverOp>::value; };
191 
192  template <class Op, class OverOp>
194 
195  template<> struct left_distributive<detail::Multiply, detail::Add> : std::true_type {};
196 
197  template<> struct right_distributive<detail::Divide, detail::Add> : std::true_type {};
198 
199  template<> struct right_distributive<detail::Power, detail::Multiply> : std::true_type {};
200 
207  template<class LHS, class RHS>
210  };
211 
213  template<class Arg,
214  typename = typename std::enable_if<IsSymbolic<SymbolicOperator>::value>::type>
215  Arg operator+(const Arg& l) { return l; }
216 
218  template<class Arg,
219  typename = typename std::enable_if<IsSymbolic<SymbolicOperator>::value>::type>
220  auto operator-(const Arg& l) -> STATOR_AUTORETURN(C<-1>() * l)
221 
222 
223  template<class LHS, class RHS,
224  typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
225  auto operator+(const LHS& l, const RHS& r)
226  -> STATOR_AUTORETURN((AddOp<decltype(store(l)), decltype(store(r))>(l, r)))
227 
228 
229  template<class LHS, class RHS,
230  typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
231  auto operator*(const LHS& l, const RHS& r)
232  -> STATOR_AUTORETURN((MultiplyOp<decltype(store(l)), decltype(store(r))>(l, r)))
233 
234 
235  template<class LHS, class RHS,
236  typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
237  auto operator-(const LHS& l, const RHS& r)
238  -> STATOR_AUTORETURN((SubtractOp<decltype(store(l)), decltype(store(r))>(l, r)))
239 
240 
241  template<class LHS, class RHS,
242  typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
243  auto operator/(const LHS& l, const RHS& r)
244  -> STATOR_AUTORETURN((DivideOp<decltype(store(l)), decltype(store(r))>(l, r)))
245 
246 
247  template<class LHS, class RHS,
248  typename = typename std::enable_if<ApplySymbolicOps<LHS, RHS>::value>::type>
249  auto pow(const LHS& l, const RHS& r)
250  -> STATOR_AUTORETURN((PowerOp<decltype(store(l)), decltype(store(r))>(l, r)));
251 
254  template<class Var, class LHS, class RHS>
255  auto derivative(const AddOp<LHS, RHS>& f, Var v)
256  -> STATOR_AUTORETURN(derivative(f._l, v) + derivative(f._r, v))
257 
258 
260  template<class Var, class LHS, class RHS>
261  auto derivative(const SubtractOp<LHS, RHS>& f, Var v)
262  -> STATOR_AUTORETURN(derivative(f._l, v) - derivative(f._r, v))
263 
264 
266  template<class Var, class LHS, class RHS>
267  auto derivative(const MultiplyOp<LHS, RHS>& f, Var v)
268  -> STATOR_AUTORETURN(derivative(f._l, v) * f._r + f._l * derivative(f._r, v))
269 
270 
274  template<class Var, class LHS, class RHS>
275  auto derivative(const DivideOp<LHS, RHS>& f, Var v)
276  -> STATOR_AUTORETURN((derivative(f._l, v) * f._r - f._l * derivative(f._r, v)) / pow(f._r, C<2>()))
277 
278 
280  template<class Var, class Arg, std::intmax_t num, std::intmax_t den>
281  auto derivative(const PowerOp<Arg, C<num, den> >& f, Var v)
282  -> STATOR_AUTORETURN((C<num, den>() * derivative(f._l, v) * pow(f._l, C<num, den>()-C<1>())));
283 
286  template<class Var, class Arg, class Power>
287  auto derivative(const PowerOp<Arg, Power>& f, Var v)
288  -> STATOR_AUTORETURN(f._r * derivative(f._l, v) * pow(f._l, f._r - C<1>()) + derivative(f._r, v) * log(f._l) * f);
290 }
291 
292 
Simple combination rule to enable Symbolic operations, but avoid redundantly specifying where two Sym...
Definition: binary_ops.hpp:208
#define STATOR_AUTORETURN_BYVALUE(EXPR)
A convenience Macro for defining auto by-value return type functions.
Definition: config.hpp:107
auto pow(const C< num1, den1 > &l, const C< num2, 1 > &r) -> STATOR_AUTORETURN(PowerOpSub< num2 >::eval(sub(l, num2)))
Symbolic representation of a variable.
Definition: symbolic.hpp:94
constexpr bool operator==(const T &) const
Definition: binary_ops.hpp:26
constexpr int RBP()
Definition: binary_ops.hpp:100
auto operator-(const Arg &l) -> STATOR_AUTORETURN(C<-1 >() *l) template< class LHS, class RHS, typename=typename std::enable_if< ApplySymbolicOps< LHS, RHS >::value >::type > auto operator+(const LHS &l, const RHS &r) -> STATOR_AUTORETURN((AddOp< decltype(store(l)), decltype(store(r))>(l, r))) template< class LHS, class RHS, typename=typename std::enable_if< ApplySymbolicOps< LHS, RHS >::value >::type > auto operator*(const LHS &l, const RHS &r) -> STATOR_AUTORETURN((MultiplyOp< decltype(store(l)), decltype(store(r))>(l, r))) template< class LHS, class RHS, typename=typename std::enable_if< ApplySymbolicOps< LHS, RHS >::value >::type > auto operator-(const LHS &l, const RHS &r) -> STATOR_AUTORETURN((SubtractOp< decltype(store(l)), decltype(store(r))>(l, r))) template< class LHS, class RHS, typename=typename std::enable_if< ApplySymbolicOps< LHS, RHS >::value >::type > auto operator/(const LHS &l, const RHS &r) -> STATOR_AUTORETURN((DivideOp< decltype(store(l)), decltype(store(r))>(l, r))) template< class LHS, class RHS, typename=typename std::enable_if< ApplySymbolicOps< LHS, RHS >::value >::type > auto pow(const LHS &l, const RHS &r) -> STATOR_AUTORETURN((PowerOp< decltype(store(l)), decltype(store(r))>(l, r)))
Symbolic unary negation operator.
NoIdentity right_zero
Definition: binary_ops.hpp:157
static std::string repr()
Definition: binary_ops.hpp:118
A class representing a compile-time rational constant (i.e., std::ratio).
Definition: constants.hpp:31
BinaryOp(const LHS &l, const RHS &r)
Definition: binary_ops.hpp:36
float log(float a)
Definition: unary_ops.hpp:50
Symbolic representation of a binary symbolic operation.
Definition: binary_ops.hpp:33
constexpr auto operator/(C< n1, d1 >, C< n2, d2 >) -> STATOR_AUTORETURN((typename detail::C_wrap< std::ratio_divide< std::ratio< n1, d1 >, std::ratio< n2, d2 > > >::type()))
Arg operator+(const Arg &l)
Symbolic unary positive operator.
Definition: binary_ops.hpp:215
static std::string repr()
Definition: binary_ops.hpp:145
C< 1 > Unity
A symbolic representation of one.
Definition: constants.hpp:53
constexpr auto operator*(C< n1, d1 >, C< n2, d2 >) -> STATOR_AUTORETURN((typename detail::C_wrap< std::ratio_multiply< std::ratio< n1, d1 >, std::ratio< n2, d2 > > >::type()))
static std::string repr()
Definition: binary_ops.hpp:158
Symbolic representation of a variable substitution.
Definition: symbolic.hpp:76
NoIdentity left_identity
Definition: binary_ops.hpp:128
#define STATOR_AUTORETURN(EXPR)
A convenience Macro for defining auto return type functions.
Definition: config.hpp:51
The stator symbolic math library.
NoIdentity right_zero
Definition: binary_ops.hpp:117
const RHS _r
Definition: binary_ops.hpp:35
static std::string repr()
Definition: binary_ops.hpp:167
auto sub(BinaryOp< LHS, Op, RHS > f, Relation< Var, Arg > x) -> STATOR_AUTORETURN_BYVALUE(Op::apply(sub(f._l, x), sub(f._r, x)))
const LHS _l
Definition: binary_ops.hpp:34
NoIdentity left_identity
Definition: binary_ops.hpp:168
constexpr int NBP()
Definition: binary_ops.hpp:105
auto derivative(const Expression &)
Performs a symbolic derivative on the expression.
auto pow(const LHS &l, const RHS &r) -> STATOR_AUTORETURN(std::pow(l, r))
NoIdentity left_zero
Definition: binary_ops.hpp:116
A type trait to denote symbolic terms (i.e., one that is not yet immediately evaluable to a "normal" ...
Definition: symbolic.hpp:50
static std::string repr()
Definition: binary_ops.hpp:132
NoIdentity right_zero
Definition: binary_ops.hpp:170
NoIdentity left_identity
Definition: binary_ops.hpp:154
auto store(const T &val) -> decltype(store_impl(val, select_overload
Definition: config.hpp:94