piranha  0.10
real_trigonometric_kronecker_monomial.hpp
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_REAL_TRIGONOMETRIC_KRONECKER_MONOMIAL_HPP
30 #define PIRANHA_REAL_TRIGONOMETRIC_KRONECKER_MONOMIAL_HPP
31 
32 #include <algorithm>
33 #include <array>
34 #include <cinttypes>
35 #include <cmath>
36 #include <cstddef>
37 #include <cstdint>
38 #include <cstdlib>
39 #include <functional>
40 #include <initializer_list>
41 #include <iostream>
42 #include <iterator>
43 #include <limits>
44 #include <stdexcept>
45 #include <string>
46 #include <type_traits>
47 #include <unordered_map>
48 #include <utility>
49 #include <vector>
50 
51 #include <piranha/binomial.hpp>
52 #include <piranha/config.hpp>
53 #include <piranha/detail/cf_mult_impl.hpp>
54 #include <piranha/detail/km_commons.hpp>
55 #include <piranha/detail/prepare_for_print.hpp>
56 #include <piranha/detail/safe_integral_adder.hpp>
57 #include <piranha/exceptions.hpp>
58 #include <piranha/is_cf.hpp>
59 #include <piranha/is_key.hpp>
60 #include <piranha/kronecker_array.hpp>
61 #include <piranha/math.hpp>
62 #include <piranha/mp_integer.hpp>
63 #include <piranha/s11n.hpp>
64 #include <piranha/safe_cast.hpp>
65 #include <piranha/static_vector.hpp>
66 #include <piranha/symbol_utils.hpp>
67 #include <piranha/term.hpp>
68 #include <piranha/type_traits.hpp>
69 
70 namespace piranha
71 {
72 
74 
109 // NOTES:
110 // - it might make sense, for canonicalisation and is_compatible(), to provide a method in kronecker_array to get only
111 // the first element of the array. This should be quite fast, and it will provide enough information for the
112 // canon/compatibility.
113 // - related to the above: we can embed the flavour as the first element of the kronecker array - at that point checking
114 // the flavour is just determining if the int value is even or odd.
115 // - need to do some reasoning on the impact of the codification in a specialised fast poisson series multiplier: how do
116 // we deal with the canonical form without going through code/decode? if we require the last multiplier to be always
117 // positive (instead of the first), can we just check/flip the sign of the coded value?
118 template <typename T = std::make_signed<std::size_t>::type>
120 {
121 public:
123  typedef T value_type;
124 
125 private:
127 
128 public:
130  static const std::size_t multiply_arity = 2u;
132 
136  typedef typename ka::size_type size_type;
138  static const size_type max_size = 255u;
141 
142 private:
143  static_assert(max_size <= std::numeric_limits<static_vector<int, 1u>::size_type>::max(), "Invalid max size.");
144 
145 public:
147 
150  real_trigonometric_kronecker_monomial() : m_value(0), m_flavour(true) {}
155 
156 private:
157  // Enabler for ctor from init list.
158  template <typename U>
159  using init_list_enabler = enable_if_t<has_safe_cast<value_type, U>::value, int>;
160 
161 public:
163 
180  template <typename U, init_list_enabler<U> = 0>
181  explicit real_trigonometric_kronecker_monomial(std::initializer_list<U> list, bool flavour = true)
182  : real_trigonometric_kronecker_monomial(std::begin(list), std::end(list), flavour)
183  {
184  }
185 
186 private:
187  // Enabler for ctor from iterator.
188  template <typename Iterator>
189  using it_ctor_enabler
190  = enable_if_t<conjunction<is_input_iterator<Iterator>,
191  has_safe_cast<value_type, decltype(*std::declval<const Iterator &>())>>::value,
192  int>;
193 
194 public:
196 
214  template <typename Iterator, it_ctor_enabler<Iterator> = 0>
215  explicit real_trigonometric_kronecker_monomial(const Iterator &start, const Iterator &end, bool flavour = true)
216  : m_value(0), m_flavour(flavour)
217  {
218  v_type tmp;
219  std::transform(
220  start, end, std::back_inserter(tmp),
221  [](const typename std::iterator_traits<Iterator>::value_type &v) { return safe_cast<value_type>(v); });
222  m_value = ka::encode(tmp);
223  }
225 
230 
237  explicit real_trigonometric_kronecker_monomial(const value_type &n, bool f) : m_value(n), m_flavour(f) {}
239 
246  const symbol_fset &)
248  {
249  }
252  {
259  }
261 
268 
275 
278  void set_int(const value_type &n)
279  {
280  m_value = n;
281  }
283 
287  {
288  return m_value;
289  }
291 
294  bool get_flavour() const
295  {
296  return m_flavour;
297  }
299 
302  void set_flavour(bool f)
303  {
304  m_flavour = f;
305  }
306 
307 private:
308  // Implementation of canonicalisation.
309  static bool canonicalise_impl(v_type &unpacked)
310  {
311  const auto size = unpacked.size();
312  bool sign_change = false;
313  for (decltype(unpacked.size()) i = 0u; i < size; ++i) {
314  if (sign_change || unpacked[i] < value_type(0)) {
315  unpacked[i] = static_cast<value_type>(-unpacked[i]);
316  sign_change = true;
317  } else if (unpacked[i] > value_type(0)) {
318  break;
319  }
320  }
321  return sign_change;
322  }
323 
324 public:
326 
340  bool canonicalise(const symbol_fset &args)
341  {
342  auto unpacked = unpack(args);
343  const bool retval = canonicalise_impl(unpacked);
344  if (retval) {
345  m_value = ka::encode(unpacked);
346  }
347  return retval;
348  }
350 
365  bool is_compatible(const symbol_fset &args) const noexcept
366  {
367  const auto s = args.size();
368  // No args means the value must also be zero.
369  if (s == 0u) {
370  return !m_value;
371  }
372  const auto &limits = ka::get_limits();
373  // If we overflow the maximum size available, we cannot use this object as key in series.
374  if (s >= limits.size()) {
375  return false;
376  }
377  const auto &l = limits[s];
378  // Value is compatible if it is within the bounds for the given size.
379  if (m_value < std::get<1u>(l) || m_value > std::get<2u>(l)) {
380  return false;
381  }
382  // Now check for the first multiplier.
383  // NOTE: here we have already checked all the conditions that could lead to unpack() throwing, so
384  // we do not need to put @throw specifications in the doc.
385  const auto unpacked = unpack(args);
386  // We know that s != 0.
387  piranha_assert(unpacked.size() > 0u);
388  for (typename v_type::size_type i = 0u; i < s; ++i) {
389  if (unpacked[i] < value_type(0)) {
390  return false;
391  } else if (unpacked[i] > value_type(0)) {
392  break;
393  }
394  }
395  return true;
396  }
398 
403  bool is_zero(const symbol_fset &) const noexcept
404  {
405  return m_value == value_type(0) && !m_flavour;
406  }
408 
435  const symbol_fset &args) const
436  {
437  return real_trigonometric_kronecker_monomial(detail::km_merge_symbols<v_type, ka>(ins_map, args, m_value),
438  m_flavour);
439  }
441 
444  bool is_unitary(const symbol_fset &) const
445  {
446  return (!m_value && m_flavour);
447  }
448 
449 private:
450  // Degree utils.
451  using degree_type = add_t<T, T>;
452 
453 public:
455 
466  degree_type t_degree(const symbol_fset &args) const
467  {
468  const auto tmp = unpack(args);
469  // NOTE: this should be guaranteed by the unpack function.
470  piranha_assert(tmp.size() == args.size());
471  degree_type retval(0);
472  for (const auto &x : tmp) {
473  detail::safe_integral_adder(retval, static_cast<degree_type>(x));
474  }
475  return retval;
476  }
478 
485  degree_type t_ldegree(const symbol_fset &args) const
486  {
487  return t_degree(args);
488  }
490 
505  degree_type t_degree(const symbol_idx_fset &p, const symbol_fset &args) const
506  {
507  const auto tmp = unpack(args);
508  piranha_assert(tmp.size() == args.size());
509  if (unlikely(p.size() && *p.rbegin() >= tmp.size())) {
510  piranha_throw(std::invalid_argument,
511  "the largest value in the positions set for the computation of the "
512  "partial trigonometric degree of a real trigonometric Kronecker monomial is "
513  + std::to_string(*p.rbegin()) + ", but the monomial has a size of only "
514  + std::to_string(tmp.size()));
515  }
516  degree_type retval(0);
517  for (auto idx : p) {
518  detail::safe_integral_adder(retval, static_cast<degree_type>(tmp[static_cast<decltype(tmp.size())>(idx)]));
519  }
520  return retval;
521  }
523 
531  degree_type t_ldegree(const symbol_idx_fset &p, const symbol_fset &args) const
532  {
533  return t_degree(p, args);
534  }
535 
536 private:
537  // Order utils.
538  using order_type = decltype(math::abs(std::declval<const T &>()) + math::abs(std::declval<const T &>()));
539 
540 public:
542 
553  order_type t_order(const symbol_fset &args) const
554  {
555  const auto tmp = unpack(args);
556  piranha_assert(tmp.size() == args.size());
557  order_type retval(0);
558  for (const auto &x : tmp) {
559  // NOTE: here the k codification is symmetric, we can always take the negative
560  // safely.
561  detail::safe_integral_adder(retval, static_cast<order_type>(math::abs(x)));
562  }
563  return retval;
564  }
566 
573  order_type t_lorder(const symbol_fset &args) const
574  {
575  return t_order(args);
576  }
578 
593  order_type t_order(const symbol_idx_fset &p, const symbol_fset &args) const
594  {
595  const auto tmp = unpack(args);
596  piranha_assert(tmp.size() == args.size());
597  if (unlikely(p.size() && *p.rbegin() >= tmp.size())) {
598  piranha_throw(std::invalid_argument,
599  "the largest value in the positions set for the computation of the "
600  "partial trigonometric order of a real trigonometric Kronecker monomial is "
601  + std::to_string(*p.rbegin()) + ", but the monomial has a size of only "
602  + std::to_string(tmp.size()));
603  }
604  order_type retval(0);
605  for (auto idx : p) {
606  detail::safe_integral_adder(
607  retval, static_cast<degree_type>(math::abs(tmp[static_cast<decltype(tmp.size())>(idx)])));
608  }
609  return retval;
610  }
612 
620  order_type t_lorder(const symbol_idx_fset &p, const symbol_fset &args) const
621  {
622  return t_order(p, args);
623  }
624 
625 private:
626  // Enabler for multiplication.
627  template <typename Cf>
628  using multiply_enabler = enable_if_t<
629  conjunction<is_divisible_in_place<Cf, int>, has_negate<Cf>, std::is_copy_assignable<Cf>, has_mul3<Cf>>::value,
630  int>;
631 
632 public:
634 
660  template <typename Cf, multiply_enabler<Cf> = 0>
664  {
665  // Coefficients first.
666  cf_mult_impl(res[0u].m_cf, t1.m_cf, t2.m_cf);
667  res[1u].m_cf = res[0u].m_cf;
668  const bool f1 = t1.m_key.get_flavour(), f2 = t2.m_key.get_flavour();
669  if (f1 && f2) {
670  // cos, cos: no change.
671  } else if (!f1 && !f2) {
672  // sin, sin: negate the plus.
673  math::negate(res[0u].m_cf);
674  } else if (!f1 && f2) {
675  // sin, cos: no change.
676  } else {
677  // cos, sin: negate the minus.
678  math::negate(res[1u].m_cf);
679  }
680  // Now the keys.
681  auto &retval_plus = res[0u].m_key;
682  auto &retval_minus = res[1u].m_key;
683  // Flags to signal if a sign change in the multipliers was needed as part of the canonicalization.
684  bool sign_plus = false, sign_minus = false;
685  const auto size = args.size();
686  const auto tmp1 = t1.m_key.unpack(args), tmp2 = t2.m_key.unpack(args);
687  v_type result_plus, result_minus;
688  for (typename v_type::size_type i = 0u; i < size; ++i) {
689  result_plus.push_back(tmp1[i]);
690  detail::safe_integral_adder(result_plus[i], tmp2[i]);
691  // NOTE: it is safe here to take the negative because in kronecker_array we are guaranteed
692  // that the range of each element is symmetric, so if tmp2[i] is representable also -tmp2[i] is.
693  // NOTE: the static cast here is because if value_type is narrower than int, the unary minus will promote
694  // to int and safe_adder won't work as it expects identical types.
695  result_minus.push_back(tmp1[i]);
696  detail::safe_integral_adder(result_minus[i], static_cast<value_type>(-tmp2[i]));
697  }
698  // Handle sign changes.
699  sign_plus = canonicalise_impl(result_plus);
700  sign_minus = canonicalise_impl(result_minus);
701  // Compute them before assigning, so in case of exceptions we do not touch the return values.
702  const auto re_plus = ka::encode(result_plus), re_minus = ka::encode(result_minus);
703  retval_plus.m_value = re_plus;
704  retval_minus.m_value = re_minus;
705  const bool f = (t1.m_key.get_flavour() == t2.m_key.get_flavour());
706  retval_plus.m_flavour = f;
707  retval_minus.m_flavour = f;
708  // If multiplier sign was changed and the result is a sine, negate the coefficient.
709  if (sign_plus && !res[0u].m_key.get_flavour()) {
710  math::negate(res[0u].m_cf);
711  }
712  if (sign_minus && !res[1u].m_key.get_flavour()) {
713  math::negate(res[1u].m_cf);
714  }
715  }
717 
720  std::size_t hash() const
721  {
722  return static_cast<std::size_t>(m_value);
723  }
725 
732  {
733  return (m_value == other.m_value && m_flavour == other.m_flavour);
734  }
736 
742  {
743  return (m_value != other.m_value || m_flavour != other.m_flavour);
744  }
746 
758  v_type unpack(const symbol_fset &args) const
759  {
760  return detail::km_unpack<v_type, ka>(args, m_value);
761  }
763 
771  void print(std::ostream &os, const symbol_fset &args) const
772  {
773  // Don't print anything in case all multipliers are zero.
774  if (m_value == value_type(0)) {
775  return;
776  }
777  if (m_flavour) {
778  os << "cos(";
779  } else {
780  os << "sin(";
781  }
782  const auto tmp = unpack(args);
783  piranha_assert(tmp.size() == args.size());
784  const value_type zero(0), one(1), m_one(-1);
785  bool empty_output = true;
786  auto it_args = args.begin();
787  for (decltype(tmp.size()) i = 0u; i < tmp.size(); ++i, ++it_args) {
788  if (tmp[i] != zero) {
789  // A positive multiplier, in case previous output exists, must be preceded
790  // by a "+" sign.
791  if (tmp[i] > zero && !empty_output) {
792  os << '+';
793  }
794  // Print the multiplier, unless it's "-1": in that case, just print the minus sign.
795  if (tmp[i] == m_one) {
796  os << '-';
797  } else if (tmp[i] != one) {
798  os << detail::prepare_for_print(tmp[i]) << '*';
799  }
800  // Finally, print name of variable.
801  os << *it_args;
802  empty_output = false;
803  }
804  }
805  os << ")";
806  }
808 
816  void print_tex(std::ostream &os, const symbol_fset &args) const
817  {
818  // Don't print anything in case all multipliers are zero.
819  if (m_value == value_type(0)) {
820  return;
821  }
822  if (m_flavour) {
823  os << "\\cos{\\left(";
824  } else {
825  os << "\\sin{\\left(";
826  }
827  const auto tmp = unpack(args);
828  piranha_assert(tmp.size() == args.size());
829  const value_type zero(0), one(1), m_one(-1);
830  bool empty_output = true;
831  auto it_args = args.begin();
832  for (decltype(tmp.size()) i = 0u; i < tmp.size(); ++i, ++it_args) {
833  if (tmp[i] != zero) {
834  // A positive multiplier, in case previous output exists, must be preceded
835  // by a "+" sign.
836  if (tmp[i] > zero && !empty_output) {
837  os << '+';
838  }
839  // Print the multiplier, unless it's "-1": in that case, just print the minus sign.
840  if (tmp[i] == m_one) {
841  os << '-';
842  } else if (tmp[i] != one) {
843  os << static_cast<long long>(tmp[i]);
844  }
845  // Finally, print name of variable.
846  os << '{' << *it_args << '}';
847  empty_output = false;
848  }
849  }
850  os << "\\right)}";
851  }
853 
868  std::pair<T, real_trigonometric_kronecker_monomial> partial(const symbol_idx &p, const symbol_fset &args) const
869  {
870  auto v = unpack(args);
871  if (p >= args.size() || v[static_cast<decltype(v.size())>(p)] == T(0)) {
872  // Derivative wrt a variable not in the monomial: the position is outside the bounds, or it refers to a
873  // variable with zero multiplier.
874  return std::make_pair(T(0), real_trigonometric_kronecker_monomial{args});
875  }
876  const auto v_b = v.begin();
877  if (get_flavour()) {
878  // cos(nx + b) -> -n*sin(nx + b)
879  return std::make_pair(static_cast<T>(-v_b[p]), real_trigonometric_kronecker_monomial(m_value, false));
880  }
881  // sin(nx + b) -> n*cos(nx + b)
882  return std::make_pair(v_b[p], real_trigonometric_kronecker_monomial(m_value, true));
883  }
885 
900  std::pair<T, real_trigonometric_kronecker_monomial> integrate(const std::string &s, const symbol_fset &args) const
901  {
902  auto v = unpack(args);
903  auto it_args = args.begin();
904  for (min_int<decltype(args.size()), typename v_type::size_type> i = 0u; i < args.size(); ++i, ++it_args) {
905  if (*it_args == s && v[i] != value_type(0)) {
906  if (get_flavour()) {
907  // cos(nx + b) -> sin(nx + b)
908  return std::make_pair(v[i], real_trigonometric_kronecker_monomial(m_value, false));
909  }
910  // sin(nx + b) -> -cos(nx + b)
911  return std::make_pair(static_cast<value_type>(-v[i]),
913  }
914  if (*it_args > s) {
915  // The current symbol in args is lexicographically larger than s,
916  // we won't find s any more in args.
917  break;
918  }
919  }
920  return std::make_pair(value_type(0), real_trigonometric_kronecker_monomial{args});
921  }
922 
923 private:
924  // The candidate type, resulting from math::cos()/math::sin() on T * U.
925  template <typename U>
926  using eval_t_cos = decltype(math::cos(std::declval<const mul_t<T, U> &>()));
927  template <typename U>
928  using eval_t_sin = decltype(math::sin(std::declval<const mul_t<T, U> &>()));
929  // Definition of the evaluation type.
930  template <typename U>
931  using eval_type
932  = enable_if_t<conjunction<
933  // sin/cos eval types must be the same.
934  std::is_same<eval_t_cos<U>, eval_t_sin<U>>,
935  // Eval type must be ctible form int.
936  std::is_constructible<eval_t_cos<U>, const int &>,
937  // Eval type must be returnable.
939  // T * U must be addable in-place.
941  // T * U must be ctible from int.
942  std::is_constructible<mul_t<T, U>, const int &>,
943  // T * U must be move ctible and dtible
944  std::is_move_constructible<mul_t<T, U>>, std::is_destructible<mul_t<T, U>>>::value,
945  eval_t_cos<U>>;
946 
947 public:
949 
965  template <typename U>
966  eval_type<U> evaluate(const std::vector<U> &values, const symbol_fset &args) const
967  {
968  // NOTE: here we can check the values size only against args.
969  if (unlikely(values.size() != args.size())) {
970  piranha_throw(std::invalid_argument, "invalid vector of values for real trigonometric Kronecker monomial "
971  "evaluation: the size of the vector of values ("
972  + std::to_string(values.size())
973  + ") differs from the size of the reference set of symbols ("
974  + std::to_string(args.size()) + ")");
975  }
976  // Run the unpack before the checks below in order to check the suitability of args.
977  const auto v = unpack(args);
978  // Special casing if the monomial is empty, just return 0 or 1.
979  if (!args.size()) {
980  if (get_flavour()) {
981  return eval_type<U>(1);
982  }
983  return eval_type<U>(0);
984  }
985  // Init the accumulator with the first element of the linear
986  // combination.
987  auto tmp(v[0] * values[0]);
988  // Accumulate the sin/cos argument.
989  for (decltype(values.size()) i = 1; i < values.size(); ++i) {
990  // NOTE: this could be optimised with an FMA eventually.
991  tmp += v[static_cast<decltype(v.size())>(i)] * values[i];
992  }
993  if (get_flavour()) {
994  // NOTE: move it, in the future math::cos() may exploit this.
995  return math::cos(std::move(tmp));
996  }
997  return math::sin(std::move(tmp));
998  }
999 
1000 private:
1001  // Substitution utils.
1002  // NOTE: the only additional requirement here is to be able to negate.
1003  template <typename U>
1004  using subs_type = enable_if_t<has_negate<eval_type<U>>::value, eval_type<U>>;
1005 
1006 public:
1008 
1049  template <typename U>
1050  std::vector<std::pair<subs_type<U>, real_trigonometric_kronecker_monomial>> subs(const symbol_idx_fmap<U> &smap,
1051  const symbol_fset &args) const
1052  {
1053  if (unlikely(smap.size() && smap.rbegin()->first >= args.size())) {
1054  // The last element of the substitution map must be a valid index into args.
1055  piranha_throw(std::invalid_argument, "invalid argument(s) for substitution in a real trigonometric "
1056  "Kronecker monomial: the last index of the substitution map ("
1057  + std::to_string(smap.rbegin()->first)
1058  + ") must be smaller than the monomial's size ("
1059  + std::to_string(args.size()) + ")");
1060  }
1061  std::vector<std::pair<subs_type<U>, real_trigonometric_kronecker_monomial>> retval;
1062  retval.reserve(2u);
1063  const auto f = get_flavour();
1064  if (smap.size()) {
1065  // The substitution map contains something, proceed to the substitution.
1066  auto v = unpack(args);
1067  // Init a tmp value from the linear combination of the first value
1068  // of the map with the first multiplier.
1069  auto it = smap.begin();
1070  auto tmp(v[static_cast<decltype(v.size())>(it->first)] * it->second);
1071  // Zero out the corresponding multiplier.
1072  v[static_cast<decltype(v.size())>(it->first)] = T(0);
1073  // Finish computing the linear combination of the values with
1074  // the corresponding multipliers.
1075  // NOTE: move to the next element in the init statement of the for loop.
1076  for (++it; it != smap.end(); ++it) {
1077  // NOTE: FMA in the future, maybe.
1078  tmp += v[static_cast<decltype(v.size())>(it->first)] * it->second;
1079  v[static_cast<decltype(v.size())>(it->first)] = T(0);
1080  }
1081  // Check if we need to canonicalise the vector, after zeroing out
1082  // one or more multipliers.
1083  const bool sign_changed = canonicalise_impl(v);
1084  // Encode the modified vector of multipliers.
1085  const auto new_value = ka::encode(v);
1086  // Pre-compute the sin/cos of tmp.
1087  auto s_tmp(math::sin(tmp)), c_tmp(math::cos(tmp));
1088  if (f) {
1089  // cos(tmp+x) -> cos(tmp)*cos(x) - sin(tmp)*sin(x)
1090  retval.emplace_back(std::move(c_tmp), real_trigonometric_kronecker_monomial(new_value, true));
1091  if (!sign_changed) {
1092  // NOTE: we need to negate because of trig formulas, but if
1093  // we switched the sign of x we have a double negation.
1094  math::negate(s_tmp);
1095  }
1096  retval.emplace_back(std::move(s_tmp), real_trigonometric_kronecker_monomial(new_value, false));
1097  } else {
1098  // sin(tmp+x) -> sin(tmp)*cos(x) + cos(tmp)*sin(x)
1099  retval.emplace_back(std::move(s_tmp), real_trigonometric_kronecker_monomial(new_value, true));
1100  if (sign_changed) {
1101  // NOTE: opposite of the above, if we switched the signs in x we need to negate
1102  // cos(tmp) to restore the signs.
1103  math::negate(c_tmp);
1104  }
1105  retval.emplace_back(std::move(c_tmp), real_trigonometric_kronecker_monomial(new_value, false));
1106  }
1107  } else {
1108  // The subs map is empty, return the original values.
1109  // NOTE: can we just return a 1-element vector here?
1110  if (f) {
1111  // cos(a) -> 1*cos(a) + 0*sin(a)
1112  retval.emplace_back(subs_type<U>(1), *this);
1113  retval.emplace_back(subs_type<U>(0), real_trigonometric_kronecker_monomial(m_value, false));
1114  } else {
1115  // sin(a) -> 0*cos(a) + 1*sin(a)
1116  retval.emplace_back(subs_type<U>(0), real_trigonometric_kronecker_monomial(m_value, true));
1117  retval.emplace_back(subs_type<U>(1), *this);
1118  }
1119  }
1120  return retval;
1121  }
1122 
1123 private:
1124  // Trig subs bits.
1125  template <typename U>
1126  using t_subs_t = decltype(std::declval<const integer &>() * std::declval<const mul_t<U, U> &>());
1127  template <typename U>
1128  using t_subs_type = enable_if_t<
1129  conjunction<std::is_default_constructible<U>, std::is_constructible<U, const int &>, std::is_move_assignable<U>,
1130  std::is_destructible<U>, std::is_assignable<U &, mul_t<U, U> &&>, std::is_destructible<mul_t<U, U>>,
1131  std::is_move_constructible<t_subs_t<U>>, std::is_destructible<t_subs_t<U>>,
1132  is_addable_in_place<t_subs_t<U>>, has_negate<t_subs_t<U>>, std::is_copy_constructible<t_subs_t<U>>,
1133  std::is_move_constructible<mul_t<U, U>>>::value,
1134  t_subs_t<U>>;
1135  // Couple of helper functions for Vieta's formulae.
1136  static value_type cos_phase(const value_type &n)
1137  {
1138  piranha_assert(n >= value_type(0));
1139  constexpr value_type v[4] = {1, 0, -1, 0};
1140  return v[n % value_type(4)];
1141  }
1142  static value_type sin_phase(const value_type &n)
1143  {
1144  piranha_assert(n >= value_type(0));
1145  constexpr value_type v[4] = {0, 1, 0, -1};
1146  return v[n % value_type(4)];
1147  }
1148 
1149 public:
1151 
1173  template <typename U>
1174  std::vector<std::pair<t_subs_type<U>, real_trigonometric_kronecker_monomial>>
1175  t_subs(const symbol_idx &idx, const U &c, const U &s, const symbol_fset &args) const
1176  {
1177  auto v = unpack(args);
1178  T n(0);
1179  if (idx < args.size()) {
1180  // NOTE: this will extract the affected multiplier
1181  // into n and zero out the multiplier in v.
1182  std::swap(n, v[static_cast<decltype(v.size())>(idx)]);
1183  }
1184  const auto abs_n = static_cast<T>(std::abs(n));
1185  // Prepare the powers of c and s to be used in the multiple angles formulae.
1186  // NOTE: probably a vector would be better here.
1187  std::unordered_map<T, U> c_map, s_map;
1188  // TREQ: U def ctible, ctible from int, move-assignable and dtible.
1189  c_map[0] = U(1);
1190  s_map[0] = U(1);
1191  for (T k = 0; k < abs_n; ++k) {
1192  // TREQ: U assignable from mult_t<U,U> &&, mult_t<U,U> dtible.
1193  c_map[static_cast<T>(k + 1)] = c_map[k] * c;
1194  s_map[static_cast<T>(k + 1)] = s_map[k] * s;
1195  }
1196  // Init with the first element in the summation.
1197  // NOTE: we promote abs_n to integer in these formulae in order to force
1198  // the use of the binomial() overload for integer. In the future we might want
1199  // to disable the binomial() overload for integral C++ types.
1200  // TREQ: t_subs_type<U> move-ctible, dtible.
1201  t_subs_type<U> cos_nx(cos_phase(abs_n) * math::binomial(integer(abs_n), T(0)) * (c_map[T(0)] * s_map[abs_n])),
1202  sin_nx(sin_phase(abs_n) * math::binomial(integer(abs_n), T(0)) * (c_map[T(0)] * s_map[abs_n]));
1203  // Run the main iteration.
1204  for (T k = 0; k < abs_n; ++k) {
1205  const auto p = static_cast<T>(abs_n - (k + 1));
1206  piranha_assert(p >= T(0));
1207  // TREQ: mult_t<U,U> move ctible.
1208  const auto tmp = c_map[static_cast<T>(k + 1)] * s_map[p];
1209  const auto tmp_bin = math::binomial(integer(abs_n), k + T(1));
1210  // TREQ: t_subs_type<U> addable in-place.
1211  cos_nx += cos_phase(p) * tmp_bin * tmp;
1212  sin_nx += sin_phase(p) * tmp_bin * tmp;
1213  }
1214  // Change sign as necessary.
1215  if (abs_n != n) {
1216  // TREQ: t_subs_type<U> has_negate.
1217  math::negate(sin_nx);
1218  }
1219  // Buld the new keys and canonicalise as needed.
1220  const bool sign_changed = canonicalise_impl(v);
1221  // Encode the modified vector.
1222  const auto new_value = ka::encode(v);
1223  // Init the retval.
1224  std::vector<std::pair<t_subs_type<U>, real_trigonometric_kronecker_monomial>> retval;
1225  retval.reserve(2);
1226  if (get_flavour()) {
1227  retval.emplace_back(std::move(cos_nx), real_trigonometric_kronecker_monomial(new_value, true));
1228  retval.emplace_back(std::move(sin_nx), real_trigonometric_kronecker_monomial(new_value, false));
1229  // Need to flip the sign on the sin * sin product if sign was not changed.
1230  if (!sign_changed) {
1231  math::negate(retval[1u].first);
1232  }
1233  } else {
1234  retval.emplace_back(std::move(sin_nx), real_trigonometric_kronecker_monomial(new_value, true));
1235  retval.emplace_back(std::move(cos_nx), real_trigonometric_kronecker_monomial(new_value, false));
1236  // Need to flip the sign on the cos * sin product if sign was changed.
1237  if (sign_changed) {
1238  math::negate(retval[1u].first);
1239  }
1240  }
1241  return retval;
1242  }
1244 
1263  void trim_identify(std::vector<char> &trim_mask, const symbol_fset &args) const
1264  {
1265  detail::km_trim_identify<v_type, ka>(trim_mask, args, m_value);
1266  }
1268 
1287  real_trigonometric_kronecker_monomial trim(const std::vector<char> &trim_mask, const symbol_fset &args) const
1288  {
1289  return real_trigonometric_kronecker_monomial(detail::km_trim<v_type, ka>(trim_mask, args, m_value), m_flavour);
1290  }
1292 
1301  {
1302  if (m_value == other.m_value) {
1303  return m_flavour < other.m_flavour;
1304  }
1305  return m_value < other.m_value;
1306  }
1307 
1308 #if defined(PIRANHA_WITH_MSGPACK)
1309 private:
1310  template <typename Stream>
1311  using msgpack_pack_enabler
1312  = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, T>,
1314  int>;
1315  template <typename U>
1316  using msgpack_convert_enabler
1317  = enable_if_t<conjunction<has_msgpack_convert<typename U::value_type>, has_msgpack_convert<typename U::v_type>,
1318  has_msgpack_convert<bool>>::value,
1319  int>;
1320 
1321 public:
1323 
1338  template <typename Stream, msgpack_pack_enabler<Stream> = 0>
1339  void msgpack_pack(msgpack::packer<Stream> &packer, msgpack_format f, const symbol_fset &s) const
1340  {
1341  packer.pack_array(2);
1342  if (f == msgpack_format::binary) {
1343  piranha::msgpack_pack(packer, m_value, f);
1344  piranha::msgpack_pack(packer, m_flavour, f);
1345  } else {
1346  auto tmp = unpack(s);
1347  piranha::msgpack_pack(packer, tmp, f);
1348  piranha::msgpack_pack(packer, m_flavour, f);
1349  }
1350  }
1352 
1371  template <typename U = real_trigonometric_kronecker_monomial, msgpack_convert_enabler<U> = 0>
1372  void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &s)
1373  {
1374  std::array<msgpack::object, 2> tmp;
1375  o.convert(tmp);
1376  if (f == msgpack_format::binary) {
1377  piranha::msgpack_convert(m_value, tmp[0], f);
1378  piranha::msgpack_convert(m_flavour, tmp[1], f);
1379  } else {
1380  v_type tmp_v;
1381  piranha::msgpack_convert(tmp_v, tmp[0], f);
1382  if (unlikely(tmp_v.size() != s.size())) {
1383  piranha_throw(std::invalid_argument,
1384  "incompatible symbol set in trigonometric monomial serialization: the reference "
1385  "symbol set has a size of "
1386  + std::to_string(s.size())
1387  + ", while the trigonometric monomial being deserialized has a size of "
1388  + std::to_string(tmp_v.size()));
1389  }
1390  *this = real_trigonometric_kronecker_monomial(tmp_v.begin(), tmp_v.end());
1391  piranha::msgpack_convert(m_flavour, tmp[1], f);
1392  }
1393  }
1394 #endif
1395 
1396 private:
1397  value_type m_value;
1398  bool m_flavour;
1399 };
1400 
1401 template <typename T>
1403 
1406 }
1407 
1408 // Implementation of the Boost s11n api.
1409 namespace boost
1410 {
1411 namespace serialization
1412 {
1413 
1414 template <typename Archive, typename T>
1415 inline void save(Archive &ar,
1417 {
1418  if (std::is_same<Archive, boost::archive::binary_oarchive>::value) {
1419  piranha::boost_save(ar, k.key().get_int());
1420  } else {
1421  auto tmp = k.key().unpack(k.ss());
1422  piranha::boost_save(ar, tmp);
1423  }
1424  piranha::boost_save(ar, k.key().get_flavour());
1425 }
1426 
1427 template <typename Archive, typename T>
1429  unsigned)
1430 {
1431  if (std::is_same<Archive, boost::archive::binary_iarchive>::value) {
1432  T value;
1433  piranha::boost_load(ar, value);
1434  k.key().set_int(value);
1435  } else {
1437  piranha::boost_load(ar, tmp);
1438  if (unlikely(tmp.size() != k.ss().size())) {
1439  piranha_throw(std::invalid_argument, "invalid size detected in the deserialization of a real Kronercker "
1440  "trigonometric monomial: the deserialized size is "
1441  + std::to_string(tmp.size())
1442  + " but the reference symbol set has a size of "
1443  + std::to_string(k.ss().size()));
1444  }
1445  // NOTE: here the exception safety is basic, as the last boost_load() could fail in principle.
1446  // It does not really matter much, as there's no real dependency between the multipliers and the flavour,
1447  // any combination is valid.
1449  }
1450  // The flavour loading is common.
1451  bool f;
1452  piranha::boost_load(ar, f);
1453  k.key().set_flavour(f);
1454 }
1455 
1456 template <typename Archive, typename T>
1457 inline void serialize(Archive &ar,
1459  unsigned version)
1460 {
1461  split_free(ar, k, version);
1462 }
1463 }
1464 }
1465 
1466 namespace piranha
1467 {
1468 
1469 inline namespace impl
1470 {
1471 
1472 template <typename Archive, typename T>
1473 using rtk_monomial_boost_save_enabler = enable_if_t<
1474  conjunction<has_boost_save<Archive, T>, has_boost_save<Archive, bool>,
1475  has_boost_save<Archive, typename real_trigonometric_kronecker_monomial<T>::v_type>>::value>;
1476 
1477 template <typename Archive, typename T>
1478 using rtk_monomial_boost_load_enabler = enable_if_t<
1479  conjunction<has_boost_load<Archive, T>, has_boost_load<Archive, bool>,
1480  has_boost_load<Archive, typename real_trigonometric_kronecker_monomial<T>::v_type>>::value>;
1481 }
1482 
1484 
1495 template <typename Archive, typename T>
1497  rtk_monomial_boost_save_enabler<Archive, T>>
1498  : boost_save_via_boost_api<Archive, boost_s11n_key_wrapper<real_trigonometric_kronecker_monomial<T>>> {
1499 };
1500 
1502 
1514 template <typename Archive, typename T>
1516  rtk_monomial_boost_load_enabler<Archive, T>>
1517  : boost_load_via_boost_api<Archive, boost_s11n_key_wrapper<real_trigonometric_kronecker_monomial<T>>> {
1518 };
1519 }
1520 
1521 namespace std
1522 {
1523 
1525 template <typename T>
1526 struct hash<piranha::real_trigonometric_kronecker_monomial<T>> {
1528  typedef size_t result_type;
1532 
1538  {
1539  return a.hash();
1540  }
1541 };
1542 }
1543 
1544 #endif
bool operator==(const real_trigonometric_kronecker_monomial &other) const
Equality operator.
Cf m_cf
Coefficient member.
Definition: term.hpp:189
void print(std::ostream &os, const symbol_fset &args) const
Print.
static const limits_type & get_limits()
Get the limits of the Kronecker codification.
typename detail::static_vector_size_type< MaxSize >::type size_type
Size type.
real_trigonometric_kronecker_monomial & operator=(const real_trigonometric_kronecker_monomial &other)=default
Copy assignment operator.
iterator begin()
Begin iterator.
void boost_load(Archive &ar, T &x)
Load from Boost archive.
Definition: s11n.hpp:469
Default implementation of piranha::boost_load().
Definition: s11n.hpp:391
mp_integer< 1 > integer
Alias for piranha::mp_integer with 1 limb of static storage.
Definition: mp_integer.hpp:63
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
Definition: s11n.hpp:957
bool is_zero(const symbol_fset &) const noexcept
Zero check.
Key type concept check.
Definition: is_key.hpp:65
eval_type< U > evaluate(const std::vector< U > &values, const symbol_fset &args) const
Evaluation.
degree_type t_ldegree(const symbol_fset &args) const
Low trigonometric degree (equivalent to the trigonometric degree).
real_trigonometric_kronecker_monomial(const Iterator &start, const Iterator &end, bool flavour=true)
Constructor from range.
Implementation of piranha::boost_load() via the Boost API.
Definition: s11n.hpp:363
degree_type t_degree(const symbol_fset &args) const
Trigonometric degree.
order_type t_lorder(const symbol_idx_fset &p, const symbol_fset &args) const
Partial low trigonometric order (equivalent to the partial trigonometric order).
Type trait to detect if a key type has a trigonometric degree property.
Definition: math.hpp:2105
In-place addable type trait.
detail::math_cos_type< T > cos(const T &x)
Cosine.
Definition: math.hpp:530
Exceptions.
Default implementation of piranha::boost_save().
Definition: s11n.hpp:245
void negate(T &x)
In-place negation.
Definition: math.hpp:329
Type trait to detect the presence of the piranha::math::negate function.
Definition: math.hpp:1070
STL namespace.
Key m_key
Key member.
Definition: term.hpp:191
static const std::size_t multiply_arity
Arity of the multiply() method.
Detect the presence of piranha::msgpack_convert().
Definition: s11n.hpp:610
static void multiply(std::array< term< Cf, real_trigonometric_kronecker_monomial >, multiply_arity > &res, const term< Cf, real_trigonometric_kronecker_monomial > &t1, const term< Cf, real_trigonometric_kronecker_monomial > &t2, const symbol_fset &args)
Multiply terms with a trigonometric monomial.
void trim_identify(std::vector< char > &trim_mask, const symbol_fset &args) const
Identify symbols that can be trimmed.
std::size_t size_type
Size type.
void msgpack_pack(msgpack::packer< Stream > &packer, msgpack_format f, const symbol_fset &s) const
Serialize in msgpack format.
v_type unpack(const symbol_fset &args) const
Unpack internal integer instance.
typename detail::min_int_impl< T, Args... >::type min_int
Detect narrowest integer type.
Term class.
Definition: term.hpp:66
std::vector< std::pair< t_subs_type< U >, real_trigonometric_kronecker_monomial > > t_subs(const symbol_idx &idx, const U &c, const U &s, const symbol_fset &args) const
Trigonometric substitution.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
iterator end()
End iterator.
degree_type t_degree(const symbol_idx_fset &p, const symbol_fset &args) const
Partial trigonometric degree.
void set_int(const value_type &n)
Set the internal integer instance.
Wrapper for the serialization of keys via Boost.
Definition: s11n.hpp:514
msgpack_format
Serialization format for msgpack.
Definition: s11n.hpp:673
void print_tex(std::ostream &os, const symbol_fset &args) const
Print in TeX mode.
Detect if type can be returned from a function.
auto abs(const T &x) -> decltype(abs_impl< T >()(x))
Absolute value.
Definition: math.hpp:1018
static int_type encode(const Vector &v)
Encode vector.
Type trait to detect if a key type has a trigonometric order property.
Definition: math.hpp:2173
real_trigonometric_kronecker_monomial trim(const std::vector< char > &trim_mask, const symbol_fset &args) const
Trim.
symbol_fset::size_type symbol_idx
Symbol index.
bool operator!=(const real_trigonometric_kronecker_monomial &other) const
Inequality operator.
Static vector class.
Type trait to detect if a key type has a trigonometric low order property.
Definition: math.hpp:2207
boost::container::flat_map< symbol_idx, T > symbol_idx_fmap
Flat map of symbol indices.
Implementation of piranha::boost_save() via the Boost API.
Definition: s11n.hpp:217
math_binomial_type< T, U > binomial(const T &x, const U &y)
Generalised binomial coefficient.
Definition: binomial.hpp:215
real_trigonometric_kronecker_monomial(std::initializer_list< U > list, bool flavour=true)
Constructor from initalizer list.
Root piranha namespace.
Definition: array_key.hpp:52
order_type t_order(const symbol_fset &args) const
Trigonometric order.
void msgpack_pack(msgpack::packer< Stream > &packer, const T &x, msgpack_format f)
Pack generic object in a msgpack stream.
Definition: s11n.hpp:856
std::vector< std::pair< subs_type< U >, real_trigonometric_kronecker_monomial > > subs(const symbol_idx_fmap< U > &smap, const symbol_fset &args) const
Substitution.
std::pair< T, real_trigonometric_kronecker_monomial > partial(const symbol_idx &p, const symbol_fset &args) const
Partial derivative.
void push_back(const value_type &x)
Copy-add element at the end of the vector.
Detect the presence of piranha::msgpack_pack().
Definition: s11n.hpp:607
real_trigonometric_kronecker_monomial(const value_type &n, bool f)
Constructor from value_type and flavour.
Type traits.
order_type t_order(const symbol_idx_fset &p, const symbol_fset &args) const
Partial trigonometric order.
real_trigonometric_kronecker_monomial(const real_trigonometric_kronecker_monomial &other, const symbol_fset &)
Converting constructor.
bool is_unitary(const symbol_fset &) const
Check if monomial is unitary.
static_vector< value_type, max_size > v_type
Vector type used for temporary packing/unpacking.
real_trigonometric_kronecker_monomial merge_symbols(const symbol_idx_fmap< symbol_fset > &ins_map, const symbol_fset &args) const
Merge symbols.
void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &s)
Deserialize from msgpack object.
Type trait to detect differentiable keys.
Definition: math.hpp:1807
bool is_compatible(const symbol_fset &args) const noexcept
Compatibility check.
boost::container::flat_set< symbol_idx > symbol_idx_fset
Flat set of symbol indices.
real_trigonometric_kronecker_monomial(const symbol_fset &)
Constructor from set of symbols.
Type trait to detect piranha::safe_cast().
Definition: safe_cast.hpp:237
order_type t_lorder(const symbol_fset &args) const
Low trigonometric order (equivalent to the trigonometric order).
void boost_save(Archive &ar, const T &x)
Save to Boost archive.
Definition: s11n.hpp:328
Detect piranha::math::mul3().
Definition: math.hpp:2999
Type trait to detect if a key type has a trigonometric low degree property.
Definition: math.hpp:2139
piranha::real_trigonometric_kronecker_monomial< T > argument_type
Argument type.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
degree_type t_ldegree(const symbol_idx_fset &p, const symbol_fset &args) const
Partial low trigonometric degree (equivalent to the partial trigonometric degree).
std::pair< T, real_trigonometric_kronecker_monomial > integrate(const std::string &s, const symbol_fset &args) const
Integration.
bool operator<(const real_trigonometric_kronecker_monomial &other) const
Comparison operator.
detail::math_sin_type< T > sin(const T &x)
Sine.
Definition: math.hpp:622
size_type size() const
Size.
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219