piranha  0.10
poisson_series.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_POISSON_SERIES_HPP
30 #define PIRANHA_POISSON_SERIES_HPP
31 
32 #include <algorithm>
33 #include <atomic>
34 #include <boost/container/container_fwd.hpp>
35 #include <boost/iterator/transform_iterator.hpp>
36 #include <iterator>
37 #include <stdexcept>
38 #include <string>
39 #include <type_traits>
40 #include <utility>
41 #include <vector>
42 
43 #include <piranha/base_series_multiplier.hpp>
44 #include <piranha/config.hpp>
45 #include <piranha/detail/divisor_series_fwd.hpp>
46 #include <piranha/detail/poisson_series_fwd.hpp>
47 #include <piranha/detail/polynomial_fwd.hpp>
48 #include <piranha/detail/sfinae_types.hpp>
49 #include <piranha/exceptions.hpp>
50 #include <piranha/forwarding.hpp>
51 #include <piranha/ipow_substitutable_series.hpp>
52 #include <piranha/is_cf.hpp>
53 #include <piranha/key_is_multipliable.hpp>
54 #include <piranha/math.hpp>
55 #include <piranha/mp_integer.hpp>
56 #include <piranha/power_series.hpp>
57 #include <piranha/real_trigonometric_kronecker_monomial.hpp>
58 #include <piranha/safe_cast.hpp>
59 #include <piranha/series.hpp>
60 #include <piranha/series_multiplier.hpp>
61 #include <piranha/substitutable_series.hpp>
62 #include <piranha/symbol_utils.hpp>
63 #include <piranha/t_substitutable_series.hpp>
64 #include <piranha/term.hpp>
65 #include <piranha/thread_pool.hpp>
66 #include <piranha/trigonometric_series.hpp>
67 #include <piranha/type_traits.hpp>
68 
69 namespace piranha
70 {
71 
72 namespace detail
73 {
74 
75 struct poisson_series_tag {
76 };
77 }
78 
80 
99 // TODO:
100 // - make this more general, make the key type selectable;
101 // - once the above is done, remeber to fix the rebind alias.
102 // - once we have a selectable key type, we must take care that in a few places we assume that the value type
103 // of the key is a C++ integral, but this might not be the case any more (e.g., in the sin/cos implementation
104 // we will need a safe cast) -> also in integrate(), there are a few occurrences of this (e.g., == 0 should
105 // become math::is_zero() etc.). Will also need the is_integrable check on the key type.
106 template <typename Cf>
107 class poisson_series
108  : public power_series<
109  ipow_substitutable_series<
110  substitutable_series<
111  t_substitutable_series<trigonometric_series<series<Cf, rtk_monomial, poisson_series<Cf>>>,
112  poisson_series<Cf>>,
113  poisson_series<Cf>>,
114  poisson_series<Cf>>,
115  poisson_series<Cf>>,
116  detail::poisson_series_tag
117 {
118 #if !defined(PIRANHA_DOXYGEN_INVOKED)
119  using base
122  trigonometric_series<series<Cf, rtk_monomial, poisson_series<Cf>>>,
123  poisson_series<Cf>>,
124  poisson_series<Cf>>,
125  poisson_series<Cf>>,
126  poisson_series<Cf>>;
127  // Sin/cos utils.
128  // Types coming out of sin()/cos() for the base type. These will also be the final types.
129  template <typename T>
130  using sin_type = decltype(math::sin(std::declval<const typename T::base &>()));
131  template <typename T>
132  using cos_type = decltype(math::cos(std::declval<const typename T::base &>()));
133  // Case 0: Poisson series is not suitable for special sin() implementation. Just forward to the base one, via
134  // casting.
135  template <typename T = poisson_series, typename std::enable_if<!detail::poly_in_cf<T>::value, int>::type = 0>
136  sin_type<T> sin_impl() const
137  {
138  return math::sin(*static_cast<const base *>(this));
139  }
140  // Case 1: Poisson series is suitable for special sin() implementation via poly. This can fail at runtime depending
141  // on what is contained in the coefficients. The return type is the same as the base one, as in this routine we only
142  // need operations which are supported by all coefficient types, no need for rebinding or anything like that.
143  template <typename T = poisson_series, typename std::enable_if<detail::poly_in_cf<T>::value, int>::type = 0>
144  sin_type<T> sin_impl() const
145  {
146  return special_sin_cos<false, sin_type<T>>(*this);
147  }
148  // Same as above, for cos().
149  template <typename T = poisson_series, typename std::enable_if<!detail::poly_in_cf<T>::value, int>::type = 0>
150  cos_type<T> cos_impl() const
151  {
152  return math::cos(*static_cast<const base *>(this));
153  }
154  template <typename T = poisson_series, typename std::enable_if<detail::poly_in_cf<T>::value, int>::type = 0>
155  cos_type<T> cos_impl() const
156  {
157  return special_sin_cos<true, cos_type<T>>(*this);
158  }
159  // Functions to handle the case in which special sin/cos implementation fails. They will just call the base sin/cos,
160  // we need them with tag dispatching as the implementation below is generic and needs to cope with potentially
161  // different sin/cos return types.
162  template <typename T>
163  static cos_type<T> special_sin_cos_failure(const T &s, const std::true_type &)
164  {
165  return math::cos(static_cast<const typename T::base &>(s));
166  }
167  template <typename T>
168  static sin_type<T> special_sin_cos_failure(const T &s, const std::false_type &)
169  {
170  return math::sin(static_cast<const typename T::base &>(s));
171  }
172  // Convert an input polynomial to a Poisson series of type RetT. The conversion
173  // will be successful if the polynomial can be reduced to an integral linear combination of
174  // symbols.
175  template <bool IsCos, typename RetT, typename Poly>
176  static RetT poly_to_ps(const Poly &poly)
177  {
178  // Shortcuts.
179  typedef typename RetT::term_type term_type;
180  typedef typename term_type::cf_type cf_type;
181  typedef typename term_type::key_type key_type;
182  typedef typename key_type::value_type value_type;
183  // Try to get the integral combination from the poly coefficient.
184  auto lc = poly.integral_combination();
185  // Change sign if needed.
186  bool sign_change = false;
187  if (!lc.empty() && lc.begin()->second.sgn() < 0) {
188  std::for_each(lc.begin(), lc.end(), [](std::pair<const std::string, integer> &p) { p.second.neg(); });
189  sign_change = true;
190  }
191  // Return value.
192  RetT retval;
193  // Build vector of integral multipliers and the symbol set.
194  // NOTE: integral_combination returns a string map, which is guaranteed to be ordered.
195  struct t_iter {
196  const std::string &operator()(const typename decltype(lc)::value_type &p) const
197  {
198  return p.first;
199  }
200  };
201  retval.set_symbol_set(symbol_fset(boost::container::ordered_unique_range_t{},
202  boost::make_transform_iterator(lc.begin(), t_iter{}),
203  boost::make_transform_iterator(lc.end(), t_iter{})));
204  piranha_assert(retval.get_symbol_set().size() == lc.size());
205  std::vector<value_type> v;
206  std::transform(lc.begin(), lc.end(), std::back_inserter(v), [](const typename decltype(lc)::value_type &p) {
207  // NOTE: this should probably be a safe_cast.
208  // The value type here could be anything, and not guaranteed to be castable,
209  // even if in the current implementation this is guaranteed to be a signed
210  // int of some kind.
211  return static_cast<value_type>(p.second);
212  });
213  // Build term, fix signs and flavour and move-insert it.
214  term_type term(cf_type(1), key_type(v.begin(), v.end()));
215  if (!IsCos) {
216  term.m_key.set_flavour(false);
217  if (sign_change) {
218  // NOTE: negate is supported by any coefficient type.
219  math::negate(term.m_cf);
220  }
221  }
222  retval.insert(std::move(term));
223  return retval;
224  }
225  // Special sin/cos implementation when we have reached the first polynomial coefficient in the hierarchy.
226  template <bool IsCos, typename RetT, typename T,
227  typename std::enable_if<std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value,
228  int>::type
229  = 0>
230  RetT special_sin_cos(const T &s) const
231  {
232  // Do something only if the series is equivalent to a polynomial.
233  if (s.is_single_coefficient() && !s.empty()) {
234  try {
235  return poly_to_ps<IsCos, RetT>(s._container().begin()->m_cf);
236  } catch (const std::invalid_argument &) {
237  // Interpret invalid_argument as a failure in extracting integral combination,
238  // and move on.
239  }
240  }
241  return special_sin_cos_failure(*this, std::integral_constant<bool, IsCos>{});
242  }
243  // The coefficient is not a polynomial: recurse to the inner coefficient type, if the current coefficient type
244  // is suitable.
245  template <bool IsCos, typename RetT, typename T,
246  typename std::enable_if<!std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value,
247  int>::type
248  = 0>
249  RetT special_sin_cos(const T &s) const
250  {
251  if (s.is_single_coefficient() && !s.empty()) {
252  return special_sin_cos<IsCos, RetT>(s._container().begin()->m_cf);
253  }
254  return special_sin_cos_failure(*this, std::integral_constant<bool, IsCos>{});
255  }
256  // Integration utils.
257  // The type resulting from the integration of the key of series T.
258  template <typename T>
259  using key_integrate_type
260  = decltype(std::declval<const typename T::term_type::key_type &>()
261  .integrate(std::declval<const std::string &>(), std::declval<const symbol_fset &>())
262  .first);
263  // Basic integration requirements for series T, to be satisfied both when the coefficient is a polynomial
264  // and when it is not. ResT is the type of the result of the integration.
265  // NOTE: this used to be a template alias to be used with true_tt in the usual fashion, but there is a pesky bug
266  // in GCC < 5 that results in a segfault of the compiler. This is a workaround.
267  template <typename T, typename ResT, typename = void>
268  struct basic_integrate_requirements {
269  static const bool value = false;
270  };
271  template <typename T, typename ResT>
272  struct basic_integrate_requirements<
273  T, ResT,
274  typename std::enable_if<
275  // Coefficient differentiable, and can call is_zero on the result.
276  has_is_zero<decltype(math::partial(std::declval<const typename T::term_type::cf_type &>(),
277  std::declval<const std::string &>()))>::value
278  &&
279  // The result needs to be addable in-place.
280  is_addable_in_place<ResT>::value &&
281  // It also needs to be ctible from zero.
282  std::is_constructible<ResT, int>::value>::type> {
283  static const bool value = true;
284  };
285  // Machinery for the integration of the coefficient only.
286  // Type resulting from the integration of the coefficient only.
287  template <typename ResT, typename T>
288  using i_cf_only_type = decltype(
289  math::integrate(std::declval<const typename T::term_type::cf_type &>(), std::declval<const std::string &>())
290  * std::declval<const T &>());
291  // Integration of coefficient only is enabled only if the type above is well defined
292  // and it is the same as the result type.
293  template <typename ResT, typename T, typename = void>
294  struct i_cf_only_enabler {
295  static const bool value = false;
296  };
297  template <typename ResT, typename T>
298  struct i_cf_only_enabler<ResT, T,
299  typename std::enable_if<std::is_same<ResT, i_cf_only_type<ResT, T>>::value>::type> {
300  static const bool value = true;
301  };
302  template <typename ResT, typename It, typename T = poisson_series>
303  void integrate_coefficient_only(ResT &retval, It it, const std::string &name,
304  typename std::enable_if<i_cf_only_enabler<ResT, T>::value>::type * = nullptr) const
305  {
306  using term_type = typename base::term_type;
307  using cf_type = typename term_type::cf_type;
308  poisson_series tmp;
309  tmp.set_symbol_set(this->m_symbol_set);
310  tmp.insert(term_type(cf_type(1), it->m_key));
311  retval += math::integrate(it->m_cf, name) * tmp;
312  }
313  template <typename ResT, typename It, typename T = poisson_series>
314  void integrate_coefficient_only(ResT &, It, const std::string &,
315  typename std::enable_if<!i_cf_only_enabler<ResT, T>::value>::type * = nullptr) const
316  {
317  piranha_throw(std::invalid_argument,
318  "unable to perform Poisson series integration: coefficient type is not integrable");
319  }
320  // Empty for SFINAE.
321  template <typename T, typename = void>
322  struct integrate_type_ {
323  };
324  // Non-polynomial coefficient.
325  template <typename T>
326  using npc_res_type = decltype((std::declval<const T &>() * std::declval<const typename T::term_type::cf_type &>())
327  / std::declval<const key_integrate_type<T> &>());
328  template <typename T>
329  struct integrate_type_<
330  T, typename std::enable_if<!std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value
331  && basic_integrate_requirements<T, npc_res_type<T>>::value>::type> {
332  using type = npc_res_type<T>;
333  };
334  // Polynomial coefficient.
335  // Coefficient type divided by the value coming from the integration of the key.
336  template <typename T>
337  using i_cf_type = decltype(std::declval<const typename T::term_type::cf_type &>()
338  / std::declval<const key_integrate_type<T> &>());
339  // Derivative of the type above.
340  template <typename T>
341  using i_cf_type_p
342  = decltype(math::partial(std::declval<const i_cf_type<T> &>(), std::declval<const std::string &>()));
343  // The final return type.
344  template <typename T>
345  using pc_res_type = decltype(std::declval<const i_cf_type_p<T> &>() * std::declval<const T &>());
346  template <typename T>
347  struct integrate_type_<
348  T, typename std::enable_if<
349  std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value
350  && basic_integrate_requirements<T, pc_res_type<T>>::value &&
351  // We need to be able to add in the npc type.
352  is_addable_in_place<pc_res_type<T>, npc_res_type<T>>::value &&
353  // We need to be able to compute the degree of the polynomials and
354  // convert it safely to integer.
355  has_safe_cast<integer, decltype(math::degree(std::declval<const typename T::term_type::cf_type &>(),
356  std::declval<const symbol_fset &>()))>::value
357  &&
358  // We need this conversion in the algorithm below.
359  std::is_constructible<i_cf_type_p<T>, i_cf_type<T>>::value &&
360  // This type needs also to be negated.
361  has_negate<i_cf_type_p<T>>::value>::type> {
362  using type = pc_res_type<T>;
363  };
364  // The final typedef.
365  template <typename T>
366  using integrate_type = typename std::enable_if<is_returnable<typename integrate_type_<T>::type>::value,
367  typename integrate_type_<T>::type>::type;
368  template <typename T = poisson_series>
369  integrate_type<T> integrate_impl(const std::string &s, const typename base::term_type &term,
370  const std::true_type &) const
371  {
372  typedef typename base::term_type term_type;
373  typedef typename term_type::cf_type cf_type;
374  integer degree;
375  try {
376  degree = safe_cast<integer>(math::degree(term.m_cf, {s}));
377  } catch (const safe_cast_failure &) {
379  std::invalid_argument,
380  "unable to perform Poisson series integration: cannot convert polynomial degree to an integer");
381  }
382  // If the variable is in both cf and key, and the cf degree is negative, we cannot integrate.
383  if (degree.sgn() < 0) {
385  std::invalid_argument,
386  "unable to perform Poisson series integration: polynomial coefficient has negative integral degree");
387  }
388  // Init retval and auxiliary quantities for the iteration.
389  auto key_int = term.m_key.integrate(s, this->m_symbol_set);
390  // NOTE: here we are sure that the variable is contained in the monomial.
391  piranha_assert(key_int.first != 0);
392  poisson_series tmp;
393  tmp.set_symbol_set(this->m_symbol_set);
394  // NOTE: not move for .second, as it is needed in the loop below.
395  tmp.insert(term_type(cf_type(1), key_int.second));
396  i_cf_type_p<T> p_cf(term.m_cf / std::move(key_int.first));
397  integrate_type<T> retval(p_cf * tmp);
398  for (integer i(1); i <= degree; ++i) {
399  key_int = key_int.second.integrate(s, this->m_symbol_set);
400  piranha_assert(key_int.first != 0);
401  p_cf = math::partial(p_cf / std::move(key_int.first), s);
402  // Sign change due to the second portion of integration by part.
403  math::negate(p_cf);
404  tmp = poisson_series{};
405  tmp.set_symbol_set(this->m_symbol_set);
406  // NOTE: don't move second.
407  tmp.insert(term_type(cf_type(1), key_int.second));
408  retval += p_cf * tmp;
409  }
410  return retval;
411  }
412  template <typename T = poisson_series>
413  integrate_type<T> integrate_impl(const std::string &, const typename base::term_type &,
414  const std::false_type &) const
415  {
416  piranha_throw(std::invalid_argument,
417  "unable to perform Poisson series integration: coefficient type is not a polynomial");
418  }
419  // Time integration.
420  // First the implementation for divisor series.
421  template <typename T>
422  using ti_type_divisor_
423  = decltype((std::declval<const T &>() * std::declval<const typename T::term_type::cf_type &>())
424  / std::declval<const integer &>());
425  template <typename T>
426  using ti_type_divisor = typename std::enable_if<
427  std::is_constructible<ti_type_divisor_<T>, int>::value && is_addable_in_place<ti_type_divisor_<T>>::value
428  && std::is_base_of<detail::divisor_series_tag, typename T::term_type::cf_type>::value,
429  ti_type_divisor_<T>>::type;
430  template <typename T = poisson_series>
431  ti_type_divisor<T> t_integrate_impl(const std::vector<std::string> &names) const
432  {
433  using return_type = ti_type<T>;
434  // Calling series types.
435  using term_type = typename base::term_type;
436  // The value type of the trigonometric key.
437  using k_value_type = typename term_type::key_type::value_type;
438  // Divisor series types.
439  using d_series_type = typename base::term_type::cf_type;
440  using d_term_type = typename d_series_type::term_type;
441  using d_cf_type = typename d_term_type::cf_type;
442  using d_key_type = typename d_term_type::key_type;
443  // Initialise the return value.
444  return_type retval(0);
445  // Setup of the symbol set.
446  piranha_assert(names.size() == this->m_symbol_set.size());
447  const symbol_fset div_symbols(names.begin(), names.end());
448  piranha_assert(div_symbols.size() == names.size());
449  // A temp vector of integers used to normalise the divisors coming
450  // out of the integration operation from the trig keys.
451  std::vector<integer> tmp_int;
452  // Build the return value.
453  const auto it_f = this->m_container.end();
454  for (auto it = this->m_container.begin(); it != it_f; ++it) {
455  // Clear the tmp integer vector.
456  tmp_int.clear();
457  // Get the vector of trigonometric multipliers.
458  const auto trig_vector = it->m_key.unpack(this->m_symbol_set);
459  // Copy it over to the tmp_int as integer values.
460  std::transform(trig_vector.begin(), trig_vector.end(), std::back_inserter(tmp_int),
461  [](const k_value_type &n) { return integer(n); });
462  // Determine the common divisor.
463  // NOTE: both the divisor and the trigonometric key share the canonical form in which the
464  // first nonzero multiplier is positive, so we don't need to account for sign flips when
465  // constructing a divisor from the trigonometric part. We just need to take care
466  // of the common divisor.
467  integer cd;
468  bool first_nonzero_found = false;
469  for (const auto &n_int : tmp_int) {
470  // NOTE: gcd is safe, operating on integers.
471  math::gcd3(cd, cd, n_int);
472  if (!first_nonzero_found && !math::is_zero(n_int)) {
473  piranha_assert(n_int > 0);
474  first_nonzero_found = true;
475  }
476  }
477  if (unlikely(math::is_zero(cd))) {
478  piranha_throw(std::invalid_argument, "an invalid trigonometric term was encountered while "
479  "attempting a time integration");
480  }
481  // Take the abs of the cd.
482  cd.abs();
483  // Divide the vector by the common divisor.
484  for (auto &n_int : tmp_int) {
485  n_int /= cd;
486  }
487  // Build the temporary divisor series from the trigonometric arguments.
488  d_series_type div_series;
489  div_series.set_symbol_set(div_symbols);
490  // Build the divisor key.
491  d_key_type div_key;
492  div_key.insert(tmp_int.begin(), tmp_int.end(), typename d_key_type::value_type(1));
493  // Finish building the temporary divisor series.
494  div_series.insert(d_term_type(d_cf_type(1), std::move(div_key)));
495  // Temporary Poisson series from the current term, with the trig flavour flipped.
496  poisson_series tmp_ps;
497  tmp_ps.set_symbol_set(this->m_symbol_set);
498  auto tmp_key = it->m_key;
499  tmp_key.set_flavour(!tmp_key.get_flavour());
500  tmp_ps.insert(term_type(it->m_cf, std::move(tmp_key)));
501  // Update the return value.
502  auto tmp = (std::move(tmp_ps) * std::move(div_series)) / cd;
503  // It also needs a negation, if the original trig key is a sine.
504  if (!it->m_key.get_flavour()) {
505  math::negate(tmp);
506  }
507  retval += std::move(tmp);
508  }
509  return retval;
510  }
511  // The final typedef.
512  template <typename T>
513  using ti_type
514  = decltype(std::declval<const T &>().t_integrate_impl(std::declval<const std::vector<std::string> &>()));
515 #endif
516 public:
518  template <typename Cf2>
521 
524  poisson_series() = default;
526  poisson_series(const poisson_series &) = default;
528  poisson_series(poisson_series &&) = default;
532  {
534  }
536 
543  poisson_series &operator=(const poisson_series &other) = default;
545 
550  poisson_series &operator=(poisson_series &&other) = default;
553 
589  template <typename T = poisson_series>
590  sin_type<T> sin() const
591  {
592  return sin_impl();
593  }
595 
605  template <typename T = poisson_series>
606  cos_type<T> cos() const
607  {
608  return cos_impl();
609  }
611 
641  template <typename T = poisson_series>
642  integrate_type<T> integrate(const std::string &name) const
643  {
644  typedef typename base::term_type term_type;
645  typedef typename term_type::cf_type cf_type;
646  // Init the return value.
647  integrate_type<T> retval(0);
648  const auto it_f = this->m_container.end();
649  for (auto it = this->m_container.begin(); it != it_f; ++it) {
650  // Integrate the key first.
651  auto key_int = it->m_key.integrate(name, this->m_symbol_set);
652  // If the variable does not appear in the monomial, try deferring the integration
653  // to the coefficient.
654  if (key_int.first == 0) {
655  integrate_coefficient_only(retval, it, name);
656  continue;
657  }
658  // The variable is in the monomial, let's check if the variable is also in the coefficient.
659  if (math::is_zero(math::partial(it->m_cf, name))) {
660  // No variable in the coefficient, proceed with the integrated key and divide by multiplier.
661  poisson_series tmp;
662  tmp.set_symbol_set(this->m_symbol_set);
663  tmp.insert(term_type(cf_type(1), std::move(key_int.second)));
664  retval += (std::move(tmp) * it->m_cf) / key_int.first;
665  } else {
666  // With the variable both in the coefficient and the key, we only know how to proceed with polynomials.
667  retval += integrate_impl(
668  name, *it, std::integral_constant<bool, std::is_base_of<detail::polynomial_tag, Cf>::value>());
669  }
670  }
671  return retval;
672  }
674 
709  template <typename T = poisson_series>
710  ti_type<T> t_integrate() const
711  {
712  std::vector<std::string> names;
713  std::transform(this->m_symbol_set.begin(), this->m_symbol_set.end(), std::back_inserter(names),
714  [](const std::string &s) { return "\\nu_{" + s + "}"; });
715  return t_integrate_impl(names);
716  }
718 
736  template <typename T = poisson_series>
737  ti_type<T> t_integrate(std::vector<std::string> names) const
738  {
739  if (unlikely(!std::is_sorted(names.begin(), names.end()))) {
740  piranha_throw(std::invalid_argument, "the list of symbol names must be ordered lexicographically");
741  }
742  // Remove duplicates.
743  names.erase(std::unique(names.begin(), names.end()), names.end());
744  if (unlikely(names.size() != this->m_symbol_set.size())) {
745  piranha_throw(std::invalid_argument, "the number of symbols passed in input must be equal to the "
746  "number of symbols of the Poisson series");
747  }
748  return t_integrate_impl(names);
749  }
750 };
751 
752 namespace detail
753 {
754 
755 template <typename Series>
756 using ps_series_multiplier_enabler = typename std::enable_if<std::is_base_of<poisson_series_tag, Series>::value>::type;
757 }
758 
760 template <typename Series>
761 class series_multiplier<Series, detail::ps_series_multiplier_enabler<Series>> : public base_series_multiplier<Series>
762 {
764  template <typename T>
765  using call_enabler = typename std::enable_if<
767  void divide_by_two(Series &s) const
768  {
769  // NOTE: if we ever implement multi-threaded series division we most likely need
770  // to revisit this.
771  piranha_assert(this->m_n_threads > 0u);
772  if (this->m_n_threads == 1u) {
773  // This is possible, as the requirements of series divisibility and trig key
774  // multipliability overlap.
775  s /= 2;
776  } else {
777  using bucket_size_type = typename base::bucket_size_type;
778  using term_type = typename Series::term_type;
779  auto &container = s._container();
780  std::atomic<bucket_size_type> total_erase_count(0u);
781  auto divider
782  = [&container, &total_erase_count, this](bucket_size_type start_idx, bucket_size_type end_idx) {
783  // A vector of terms to be erased at each bucket iteration.
784  std::vector<term_type> term_list;
785  // Total number of terms erased by this thread.
786  bucket_size_type erase_count = 0u;
787  for (; start_idx != end_idx; ++start_idx) {
788  // Reset the list of terms to be erased.
789  term_list.clear();
790  const auto &list = container._get_bucket_list(start_idx);
791  for (const auto &t : list) {
792  t.m_cf /= 2;
793  if (unlikely(t.is_zero(this->m_ss))) {
794  term_list.push_back(t);
795  }
796  }
797  for (const auto &t : term_list) {
798  container._erase(container._find(t, start_idx));
799  erase_count = static_cast<bucket_size_type>(erase_count + 1u);
800  }
801  }
802  // Update the global counter of erased terms.
803  total_erase_count += erase_count;
804  };
805  // Buckets per thread.
806  const auto bpt = static_cast<bucket_size_type>(container.bucket_count() / this->m_n_threads);
807  // Go with the threads.
809  try {
810  for (unsigned i = 0u; i < this->m_n_threads; ++i) {
811  const auto start_idx = static_cast<bucket_size_type>(bpt * i);
812  // Special casing for the last thread.
813  const auto end_idx = (i == this->m_n_threads - 1u) ? container.bucket_count()
814  : static_cast<bucket_size_type>(bpt * (i + 1u));
815  ff_list.push_back(thread_pool::enqueue(i, divider, start_idx, end_idx));
816  }
817  // First let's wait for everything to finish.
818  ff_list.wait_all();
819  // Then, let's handle the exceptions.
820  ff_list.get_all();
821  } catch (...) {
822  ff_list.wait_all();
823  // Clear out the container as it might be in an inconsistent state.
824  container.clear();
825  throw;
826  }
827  // Final size update - all of this is noexcept.
828  const auto tot = total_erase_count.load();
829  piranha_assert(tot <= s.size());
830  container._update_size(static_cast<bucket_size_type>(s.size() - tot));
831  }
832  }
833 
834 public:
836  using base::base;
838 
849  template <typename T = Series, call_enabler<T> = 0>
850  Series operator()() const
851  {
852  auto retval(this->plain_multiplication());
853  divide_by_two(retval);
854  return retval;
855  }
856 };
857 }
858 
859 #endif
typename Series::size_type bucket_size_type
The size type of Series.
void wait_all()
Wait on all the futures.
Type trait for multipliable key.
detail::math_integrate_type< T > integrate(const T &x, const std::string &str)
Integration.
Definition: math.hpp:743
Poisson series class.
mp_integer< 1 > integer
Alias for piranha::mp_integer with 1 limb of static storage.
Definition: mp_integer.hpp:63
detail::math_partial_type< T > partial(const T &x, const std::string &str)
Partial derivative.
Definition: math.hpp:691
poisson_series()=default
Defaulted default constructor.
detail::math_cos_type< T > cos(const T &x)
Cosine.
Definition: math.hpp:530
Exceptions.
const_iterator end() const
Const end iterator.
Definition: hash_set.hpp:883
void negate(T &x)
In-place negation.
Definition: math.hpp:329
STL namespace.
ti_type< T > t_integrate(std::vector< std::string > names) const
Time integration (alternative overload).
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
Definition: forwarding.hpp:50
void get_all()
Get all the futures.
static enqueue_t< F &&, Args &&... > enqueue(unsigned n, F &&f, Args &&... args)
Enqueue task.
auto degree(const T &x) -> decltype(degree_impl< T >()(x))
Total degree.
Definition: math.hpp:1341
Forwarding macros.
Term class.
Definition: term.hpp:66
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
Key key_type
Alias for the key type.
Definition: term.hpp:79
sin_type< T > sin() const
Sine.
poisson_series & operator=(const poisson_series &other)=default
Copy assignment operator.
integrate_type< T > integrate(const std::string &name) const
Integration.
cos_type< T > cos() const
Cosine.
const_iterator begin() const
Const begin iterator.
Definition: hash_set.hpp:859
Cf cf_type
Alias for the coefficient type.
Definition: term.hpp:77
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
Class to store a list of futures.
term< Cf, rtk_monomial > term_type
Alias for term type.
Definition: series.hpp:1266
Root piranha namespace.
Definition: array_key.hpp:52
Type traits.
~poisson_series()
Trivial destructor.
ti_type< T > t_integrate() const
Time integration.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
Type trait to detect series types.
Definition: series_fwd.hpp:49
auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl< T >()(out, a, b))
Ternary GCD.
Definition: math.hpp:2947
#define PIRANHA_FORWARDING_ASSIGNMENT(Derived, Base)
Assignment-forwarding macro.
Definition: forwarding.hpp:68
detail::math_sin_type< T > sin(const T &x)
Sine.
Definition: math.hpp:622
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219
void push_back(std::future< T > &&f)
Move-insert a future.