piranha  0.10
divisor_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_DIVISOR_SERIES_HPP
30 #define PIRANHA_DIVISOR_SERIES_HPP
31 
32 #include <algorithm>
33 #include <boost/container/container_fwd.hpp>
34 #include <boost/iterator/transform_iterator.hpp>
35 #include <initializer_list>
36 #include <iterator>
37 #include <limits>
38 #include <stdexcept>
39 #include <string>
40 #include <type_traits>
41 #include <utility>
42 #include <vector>
43 
44 #include <piranha/base_series_multiplier.hpp>
45 #include <piranha/config.hpp>
46 #include <piranha/detail/divisor_series_fwd.hpp>
47 #include <piranha/detail/polynomial_fwd.hpp>
48 #include <piranha/divisor.hpp>
49 #include <piranha/exceptions.hpp>
50 #include <piranha/forwarding.hpp>
51 #include <piranha/invert.hpp>
52 #include <piranha/ipow_substitutable_series.hpp>
53 #include <piranha/is_cf.hpp>
54 #include <piranha/key_is_multipliable.hpp>
55 #include <piranha/math.hpp>
56 #include <piranha/mp_integer.hpp>
57 #include <piranha/power_series.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/type_traits.hpp>
64 
65 namespace piranha
66 {
67 
68 namespace detail
69 {
70 
71 struct divisor_series_tag {
72 };
73 
74 // Type trait to check the key type in divisor_series.
75 template <typename T>
76 struct is_divisor_series_key {
77  static const bool value = false;
78 };
79 
80 template <typename T>
81 struct is_divisor_series_key<divisor<T>> {
82  static const bool value = true;
83 };
84 
85 // See the workaround description below.
86 template <typename T>
87 struct base_getter {
88  using type = typename T::base;
89 };
90 }
91 
93 
110 template <typename Cf, typename Key>
111 class divisor_series
112  : public power_series<ipow_substitutable_series<
113  substitutable_series<series<Cf, Key, divisor_series<Cf, Key>>, divisor_series<Cf, Key>>,
114  divisor_series<Cf, Key>>,
115  divisor_series<Cf, Key>>,
116  detail::divisor_series_tag
117 {
118  // NOTE: this is a workaround for GCC < 5. The enabler condition for the special invert() method is a struct defined
119  // within this class, and it needs to access the "base" private typedef via the inverse_type<> alias. The enabler
120  // condition is not granted access to the private base typedef (erroneously, since it is defined in the scope of
121  // this class), so we use this friend helper in order to reach the base typedef.
122  template <typename>
123  friend struct detail::base_getter;
124  PIRANHA_TT_CHECK(detail::is_divisor_series_key, Key);
125  using base
127  substitutable_series<series<Cf, Key, divisor_series<Cf, Key>>, divisor_series<Cf, Key>>,
128  divisor_series<Cf, Key>>,
129  divisor_series<Cf, Key>>;
130  // Value type of the divisor.
131  using dv_type = typename Key::value_type;
132  // Partial utils.
133  // Handle exponent increase in a safe way.
134  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
135  static void expo_increase(T &e)
136  {
137  if (unlikely(e == std::numeric_limits<T>::max())) {
138  piranha_throw(std::overflow_error, "overflow in the computation of the partial derivative "
139  "of a divisor series");
140  }
141  e = static_cast<T>(e + T(1));
142  }
143  template <typename T, enable_if_t<!std::is_integral<T>::value, int> = 0>
144  static void expo_increase(T &e)
145  {
146  ++e;
147  }
148  // Safe computation of the integral multiplier.
149  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
150  static auto safe_mult(const T &n, const T &m) -> decltype(n * m)
151  {
152  auto ret(integer(n) * m);
153  ret.neg();
154  return static_cast<decltype(n * m)>(ret);
155  }
156  template <typename T, enable_if_t<!std::is_integral<T>::value, int> = 0>
157  static auto safe_mult(const T &n, const T &m) -> decltype(-(n * m))
158  {
159  return -(n * m);
160  }
161  // This is the first stage - the second part of the chain rule for each term.
162  template <typename T>
163  using d_partial_type_0
164  = decltype(std::declval<const dv_type &>() * std::declval<const dv_type &>() * std::declval<const T &>());
165  template <typename T>
166  using d_partial_type_1 = decltype(std::declval<const T &>() * std::declval<const d_partial_type_0<T> &>());
167  // Requirements on the final type for the first stage.
168  template <typename T>
169  using d_partial_type = enable_if_t<
170  conjunction<std::is_constructible<d_partial_type_1<T>, d_partial_type_0<T>>,
171  std::is_constructible<d_partial_type_1<T>, int>, is_addable_in_place<d_partial_type_1<T>>>::value,
172  d_partial_type_1<T>>;
173  template <typename T = divisor_series>
174  d_partial_type<T> d_partial_impl(typename T::term_type::key_type &key, const symbol_idx &p) const
175  {
176  using term_type = typename base::term_type;
177  using cf_type = typename term_type::cf_type;
178  using key_type = typename term_type::key_type;
179  piranha_assert(key.size() != 0u);
180  // Construct the first part of the derivative.
181  key_type tmp_div;
182  // Insert all the terms that depend on the variable, apart from the
183  // first one.
184  const auto it_f = key.m_container.end();
185  auto it = key.m_container.begin(), it_b(it);
186  auto first(*it), first_copy(*it);
187  // Size type of the multipliers' vector.
188  using vs_type = decltype(first.v.size());
189  ++it;
190  for (; it != it_f; ++it) {
191  tmp_div.m_container.insert(*it);
192  }
193  // Remove the first term from the original key.
194  key.m_container.erase(it_b);
195  // Extract from the first dependent term the aij and the exponent, and multiply+negate them.
196  const auto mult = safe_mult(first.e, first.v[static_cast<vs_type>(p)]);
197  // Increase by one the exponent of the first dep. term.
198  expo_increase(first.e);
199  // Insert the modified first term. Don't move, as we need first below.
200  tmp_div.m_container.insert(first);
201  // Now build the first part of the derivative.
202  divisor_series tmp_ds;
203  tmp_ds.set_symbol_set(this->m_symbol_set);
204  tmp_ds.insert(term_type(cf_type(1), std::move(tmp_div)));
205  // Init the retval.
206  d_partial_type<T> retval(mult * tmp_ds);
207  // Now the second part of the derivative, if appropriate.
208  if (!key.m_container.empty()) {
209  // Build a series with only the first dependent term and unitary coefficient.
210  key_type tmp_div_01;
211  tmp_div_01.m_container.insert(std::move(first_copy));
212  divisor_series tmp_ds_01;
213  tmp_ds_01.set_symbol_set(this->m_symbol_set);
214  tmp_ds_01.insert(term_type(cf_type(1), std::move(tmp_div_01)));
215  // Recurse.
216  retval += tmp_ds_01 * d_partial_impl(key, p);
217  }
218  return retval;
219  }
220  template <typename T = divisor_series>
221  d_partial_type<T> divisor_partial(const typename T::term_type &term, const symbol_idx &p) const
222  {
223  using term_type = typename base::term_type;
224  // Return zero if the variable is not in the series.
225  if (p >= this->m_symbol_set.size()) {
226  return d_partial_type<T>(0);
227  }
228  // Initial split of the divisor.
229  auto sd = term.m_key.split(p, this->m_symbol_set);
230  // If the variable is not in the divisor, just return zero.
231  if (sd.first.size() == 0u) {
232  return d_partial_type<T>(0);
233  }
234  // Init the constant part of the derivative: the coefficient and the part of the divisor
235  // which does not depend on the variable.
236  divisor_series tmp_ds;
237  tmp_ds.set_symbol_set(this->m_symbol_set);
238  tmp_ds.insert(term_type(term.m_cf, std::move(sd.second)));
239  // Construct and return the result.
240  return tmp_ds * d_partial_impl(sd.first, p);
241  }
242  // The final type.
243  template <typename T>
244  using partial_type_ = decltype(
245  math::partial(std::declval<const typename T::term_type::cf_type &>(), std::declval<const std::string &>())
246  * std::declval<const T &>()
247  + std::declval<const d_partial_type<T> &>());
248  template <typename T>
249  using partial_type = enable_if_t<
250  conjunction<std::is_constructible<partial_type_<T>, int>, is_addable_in_place<partial_type_<T>>>::value,
251  partial_type_<T>>;
252  // Integrate utils.
253  template <typename T>
254  using integrate_type_ = decltype(
255  math::integrate(std::declval<const typename T::term_type::cf_type &>(), std::declval<const std::string &>())
256  * std::declval<const T &>());
257  template <typename T>
258  using integrate_type = enable_if_t<
259  conjunction<std::is_constructible<integrate_type_<T>, int>, is_addable_in_place<integrate_type_<T>>>::value,
260  integrate_type_<T>>;
261  // Invert utils.
262  // Type coming out of invert() for the base type. This will also be the final type.
263  template <typename T>
264  using inverse_type = decltype(math::invert(std::declval<const typename detail::base_getter<T>::type &>()));
265  // Enabler for the special inversion algorithm. We need a polynomial in the coefficient hierarchy
266  // and the inversion type must support the operations needed in the algorithm.
267  template <typename T, typename = void>
268  struct has_special_invert {
269  static constexpr bool value = false;
270  };
271  template <typename T>
272  struct has_special_invert<
273  T, enable_if_t<conjunction<
274  detail::poly_in_cf<T>,
275  std::is_constructible<inverse_type<T>, decltype(std::declval<const inverse_type<T> &>()
276  / std::declval<const integer &>())>>::value>> {
277  static constexpr bool value = true;
278  };
279  // Case 0: Series is not suitable for special invert() implementation. Just forward to the base one, via casting.
280  template <typename T = divisor_series, enable_if_t<!has_special_invert<T>::value, int> = 0>
281  inverse_type<T> invert_impl() const
282  {
283  return math::invert(*static_cast<const base *>(this));
284  }
285  // Case 1: Series is suitable for special invert() implementation. This can fail at runtime depending on what is
286  // contained in the coefficients. The return type is the same as the base one.
287  template <typename T = divisor_series, enable_if_t<has_special_invert<T>::value, int> = 0>
288  inverse_type<T> invert_impl() const
289  {
290  return special_invert<inverse_type<T>>(*this);
291  }
292  // Special invert() implementation when we have reached the first polynomial coefficient in the hierarchy.
293  template <typename RetT, typename T,
294  enable_if_t<std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value, int> = 0>
295  RetT special_invert(const T &s) const
296  {
297  if (s.is_single_coefficient() && !s.empty()) {
298  try {
299  using term_type = typename RetT::term_type;
300  using cf_type = typename term_type::cf_type;
301  using key_type = typename term_type::key_type;
302  // Extract the linear combination from the poly coefficient.
303  auto lc = s._container().begin()->m_cf.integral_combination();
304  // NOTE: lc cannot be empty as we are coming in with a non-zero polynomial.
305  piranha_assert(!lc.empty());
306  // NOTE: integral_combination returns a string map, which is guaranteed to be ordered.
307  struct t_iter {
308  const std::string &operator()(const typename decltype(lc)::value_type &p) const
309  {
310  return p.first;
311  }
312  };
313  symbol_fset ss(boost::container::ordered_unique_range_t{},
314  boost::make_transform_iterator(lc.begin(), t_iter{}),
315  boost::make_transform_iterator(lc.end(), t_iter{}));
316  // NOTE: lc contains unique strings, so the symbol_fset should not contain any duplicate.
317  piranha_assert(ss.size() == lc.size());
318  std::vector<integer> v_int;
319  std::transform(lc.begin(), lc.end(), std::back_inserter(v_int),
320  [](const typename decltype(lc)::value_type &p) { return p.second; });
321  // We need to canonicalise the term: switch the sign if the first
322  // nonzero element is negative, and divide by the common denom.
323  bool first_nonzero_found = false, need_negate = false;
324  integer cd;
325  for (auto &n : v_int) {
326  if (!first_nonzero_found && !math::is_zero(n)) {
327  if (n < 0) {
328  need_negate = true;
329  }
330  first_nonzero_found = true;
331  }
332  if (need_negate) {
333  math::negate(n);
334  }
335  // NOTE: gcd(0,n) == n for all n, zero included.
336  // NOTE: the gcd computation here is safe as we are operating on integers.
337  math::gcd3(cd, cd, n);
338  }
339  // GCD on integers should always return non-negative numbers, and cd should never be zero: if all
340  // elements in v_int are zero, we would not have been able to extract the linear combination.
341  piranha_assert(cd.sgn() > 0);
342  // Divide by the cd.
343  for (auto &n : v_int) {
344  n /= cd;
345  }
346  // Now build the key.
347  key_type tmp_key;
348  tmp_key.insert(v_int.begin(), v_int.end(), integer{1});
349  // The return value.
350  RetT retval;
351  retval.set_symbol_set(ss);
352  retval.insert(term_type(cf_type(1), std::move(tmp_key)));
353  // If we negated in the canonicalisation, we need to re-negate
354  // the common divisor before the final division.
355  if (need_negate) {
356  math::negate(cd);
357  }
358  return RetT(retval / cd);
359  } catch (const std::invalid_argument &) {
360  // Interpret invalid_argument as a failure in extracting integral combination,
361  // and move on.
362  }
363  }
364  return math::invert(*static_cast<const base *>(this));
365  }
366  // The coefficient is not a polynomial: recurse to the inner coefficient type, if the current coefficient type
367  // is suitable.
368  template <typename RetT, typename T,
369  enable_if_t<!std::is_base_of<detail::polynomial_tag, typename T::term_type::cf_type>::value, int> = 0>
370  RetT special_invert(const T &s) const
371  {
372  if (s.is_single_coefficient() && !s.empty()) {
373  return special_invert<RetT>(s._container().begin()->m_cf);
374  }
375  return math::invert(*static_cast<const base *>(this));
376  }
377 
378 public:
380  template <typename Cf2>
383  divisor_series() = default;
385  divisor_series(const divisor_series &) = default;
387  divisor_series(divisor_series &&) = default;
391  {
392  PIRANHA_TT_CHECK(is_series, divisor_series);
393  PIRANHA_TT_CHECK(is_cf, divisor_series);
394  }
396 
403  divisor_series &operator=(const divisor_series &other) = default;
405 
410  divisor_series &operator=(divisor_series &&other) = default;
413 
460  template <typename T = divisor_series>
461  inverse_type<T> invert() const
462  {
463  return invert_impl();
464  }
466 
491  template <typename T = divisor_series>
492  partial_type<T> partial(const std::string &name) const
493  {
494  using term_type = typename base::term_type;
495  using cf_type = typename term_type::cf_type;
496  partial_type<T> retval(0);
497  const auto it_f = this->m_container.end();
498  const auto idx = ss_index_of(this->m_symbol_set, name);
499  for (auto it = this->m_container.begin(); it != it_f; ++it) {
500  divisor_series tmp;
501  tmp.set_symbol_set(this->m_symbol_set);
502  tmp.insert(term_type(cf_type(1), it->m_key));
503  retval += math::partial(it->m_cf, name) * tmp + divisor_partial(*it, idx);
504  }
505  return retval;
506  }
508 
527  template <typename T = divisor_series>
528  integrate_type<T> integrate(const std::string &name) const
529  {
530  using term_type = typename base::term_type;
531  using cf_type = typename term_type::cf_type;
532  integrate_type<T> retval(0);
533  const auto it_f = this->m_container.end();
534  // Turn name into symbol position.
535  const auto idx = ss_index_of(this->m_symbol_set, name);
536  for (auto it = this->m_container.begin(); it != it_f; ++it) {
537  if (idx < this->m_symbol_set.size()) {
538  // If the variable is in the symbol set, then we need to make sure
539  // that each multiplier associated to it is zero. Otherwise, the divisor
540  // depends on the variable and we cannot perform the integration.
541  const auto it2_f = it->m_key.m_container.end();
542  for (auto it2 = it->m_key.m_container.begin(); it2 != it2_f; ++it2) {
543  using size_type = decltype(it2->v.size());
544  piranha_assert(idx < it2->v.size());
545  if (unlikely(it2->v[static_cast<size_type>(idx)] != 0)) {
546  piranha_throw(std::invalid_argument, "unable to integrate with respect to divisor variables");
547  }
548  }
549  }
550  divisor_series tmp;
551  tmp.set_symbol_set(this->m_symbol_set);
552  tmp.insert(term_type(cf_type(1), it->m_key));
553  retval += math::integrate(it->m_cf, name) * tmp;
554  }
555  return retval;
556  }
557 };
558 
559 namespace detail
560 {
561 
562 template <typename Series>
563 using divisor_series_multiplier_enabler = enable_if_t<std::is_base_of<divisor_series_tag, Series>::value>;
564 }
565 
567 template <typename Series>
568 class series_multiplier<Series, detail::divisor_series_multiplier_enabler<Series>>
569  : public base_series_multiplier<Series>
570 {
572  template <typename T>
573  using call_enabler
574  = enable_if_t<key_is_multipliable<typename T::term_type::cf_type, typename T::term_type::key_type>::value, int>;
575 
576 public:
578  using base::base;
580 
591  template <typename T = Series, call_enabler<T> = 0>
592  Series operator()() const
593  {
594  return this->plain_multiplication();
595  }
596 };
597 }
598 
599 #endif
auto invert(const T &x) -> decltype(invert_impl< T >()(x))
Compute the inverse.
Definition: invert.hpp:79
detail::math_integrate_type< T > integrate(const T &x, const std::string &str)
Integration.
Definition: math.hpp:743
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
~divisor_series()
Trivial destructor.
Type trait to detect coefficient types.
Definition: is_cf.hpp:55
Exceptions.
const_iterator end() const
Const end iterator.
Definition: hash_set.hpp:883
void negate(T &x)
In-place negation.
Definition: math.hpp:329
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
Definition: forwarding.hpp:50
integrate_type< T > integrate(const std::string &name) const
Integration.
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
typename container_type::size_type size_type
Size type.
Definition: series.hpp:1981
const_iterator begin() const
Const begin iterator.
Definition: hash_set.hpp:859
Cf cf_type
Alias for the coefficient type.
Definition: term.hpp:77
symbol_fset::size_type symbol_idx
Symbol index.
symbol_idx ss_index_of(const symbol_fset &ref, const std::string &name)
Identify the index of a symbol in a symbol_fset.
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
term< Cf, Key > term_type
Alias for term type.
Definition: series.hpp:1266
partial_type< T > partial(const std::string &name) const
Partial derivative.
Root piranha namespace.
Definition: array_key.hpp:52
Type traits.
divisor_series & operator=(const divisor_series &other)=default
Copy assignment operator.
divisor_series()=default
Defaulted default constructor.
container_type m_container
Terms container.
Definition: series.hpp:2805
#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
inverse_type< T > invert() const
Inversion.