piranha  0.10
power_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_POWER_SERIES_HPP
30 #define PIRANHA_POWER_SERIES_HPP
31 
32 #include <algorithm>
33 #include <type_traits>
34 #include <utility>
35 
36 #include <piranha/detail/safe_integral_adder.hpp>
37 #include <piranha/forwarding.hpp>
38 #include <piranha/math.hpp>
39 #include <piranha/safe_cast.hpp>
40 #include <piranha/series.hpp>
41 #include <piranha/symbol_utils.hpp>
42 #include <piranha/type_traits.hpp>
43 
44 namespace piranha
45 {
46 
47 inline namespace impl
48 {
49 
50 // Tag for power series instances.
51 struct power_series_tag {
52 };
53 
54 // Detect power series terms.
55 template <typename T>
56 struct ps_term_score {
57  typedef typename T::cf_type cf_type;
58  typedef typename T::key_type key_type;
59  static const unsigned value
61  + (static_cast<unsigned>(key_has_degree<key_type>::value && key_has_ldegree<key_type>::value) << 1u);
62 };
63 
64 // Common checks on degree/ldegree type for use in enabling conditions below.
65 template <typename T>
66 using common_degree_type_checks
67  = conjunction<std::is_constructible<T, const int &>, is_less_than_comparable<T>, is_container_element<T>>;
68 
69 // Total (low) degree computation.
70 #define PIRANHA_DEFINE_PS_PROPERTY_GETTER(property) \
71  template <typename Term, enable_if_t<ps_term_score<Term>::value == 1u, int> = 0> \
72  inline auto ps_get_##property(const Term &t, const symbol_fset &)->decltype(math::property(t.m_cf)) \
73  { \
74  return math::property(t.m_cf); \
75  } \
76  template <typename Term, enable_if_t<ps_term_score<Term>::value == 2u, int> = 0> \
77  inline auto ps_get_##property(const Term &t, const symbol_fset &s)->decltype(t.m_key.property(s)) \
78  { \
79  return t.m_key.property(s); \
80  } \
81  template < \
82  typename Term, \
83  enable_if_t<ps_term_score<Term>::value == 3u \
84  && conjunction<std::is_integral<decltype(math::property(std::declval<const Term &>().m_cf))>, \
85  std::is_integral<decltype(std::declval<const Term &>().m_key.property( \
86  std::declval<const symbol_fset &>()))>>::value, \
87  int> = 0> \
88  inline auto ps_get_##property(const Term &t, const symbol_fset &s) \
89  ->decltype(math::property(t.m_cf) + t.m_key.property(s)) \
90  { \
91  using ret_type = decltype(math::property(t.m_cf) + t.m_key.property(s)); \
92  ret_type retval(math::property(t.m_cf)); \
93  detail::safe_integral_adder(retval, static_cast<ret_type>(t.m_key.property(s))); \
94  return retval; \
95  } \
96  template <typename Term, \
97  enable_if_t< \
98  ps_term_score<Term>::value == 3u \
99  && disjunction< \
100  negation<std::is_integral<decltype(math::property(std::declval<const Term &>().m_cf))>>, \
101  negation<std::is_integral<decltype(std::declval<const Term &>().m_key.property( \
102  std::declval<const symbol_fset &>()))>>>::value, \
103  int> = 0> \
104  inline auto ps_get_##property(const Term &t, const symbol_fset &s) \
105  ->decltype(math::property(t.m_cf) + t.m_key.property(s)) \
106  { \
107  return math::property(t.m_cf) + t.m_key.property(s); \
108  } \
109  template <typename T> \
110  using ps_##property##_type_ = decltype( \
111  ps_get_##property(std::declval<const typename T::term_type &>(), std::declval<const symbol_fset &>())); \
112  template <typename T> \
113  using ps_##property##_type \
114  = enable_if_t<common_degree_type_checks<ps_##property##_type_<T>>::value, ps_##property##_type_<T>>;
115 PIRANHA_DEFINE_PS_PROPERTY_GETTER(degree)
116 PIRANHA_DEFINE_PS_PROPERTY_GETTER(ldegree)
117 #undef PIRANHA_DEFINE_PS_PROPERTY_GETTER
118 
119 // Partial (low) degree computation.
120 #define PIRANHA_DEFINE_PARTIAL_PS_PROPERTY_GETTER(property) \
121  template <typename Term, enable_if_t<ps_term_score<Term>::value == 1u, int> = 0> \
122  inline auto ps_get_##property(const Term &t, const symbol_fset &names, const symbol_idx_fset &, \
123  const symbol_fset &) \
124  ->decltype(math::property(t.m_cf, names)) \
125  { \
126  return math::property(t.m_cf, names); \
127  } \
128  template <typename Term, enable_if_t<ps_term_score<Term>::value == 2u, int> = 0> \
129  inline auto ps_get_##property(const Term &t, const symbol_fset &, const symbol_idx_fset &p, const symbol_fset &s) \
130  ->decltype(t.m_key.property(p, s)) \
131  { \
132  return t.m_key.property(p, s); \
133  } \
134  template < \
135  typename Term, \
136  enable_if_t<ps_term_score<Term>::value == 3u \
137  && conjunction<std::is_integral<decltype(math::property( \
138  std::declval<const Term &>().m_cf, std::declval<const symbol_fset &>()))>, \
139  std::is_integral<decltype(std::declval<const Term &>().m_key.property( \
140  std::declval<const symbol_idx_fset &>(), \
141  std::declval<const symbol_fset &>()))>>::value, \
142  int> = 0> \
143  inline auto ps_get_##property(const Term &t, const symbol_fset &names, const symbol_idx_fset &p, \
144  const symbol_fset &s) \
145  ->decltype(math::property(t.m_cf, names) + t.m_key.property(p, s)) \
146  { \
147  using ret_type = decltype(math::property(t.m_cf, names) + t.m_key.property(p, s)); \
148  ret_type retval(math::property(t.m_cf, names)); \
149  detail::safe_integral_adder(retval, static_cast<ret_type>(t.m_key.property(p, s))); \
150  return retval; \
151  } \
152  template < \
153  typename Term, \
154  enable_if_t<ps_term_score<Term>::value == 3u \
155  && disjunction<negation<std::is_integral<decltype(math::property( \
156  std::declval<const Term &>().m_cf, std::declval<const symbol_fset &>()))>>, \
157  negation<std::is_integral<decltype(std::declval<const Term &>().m_key.property( \
158  std::declval<const symbol_idx_fset &>(), \
159  std::declval<const symbol_fset &>()))>>>::value, \
160  int> = 0> \
161  inline auto ps_get_##property(const Term &t, const symbol_fset &names, const symbol_idx_fset &p, \
162  const symbol_fset &s) \
163  ->decltype(math::property(t.m_cf, names) + t.m_key.property(p, s)) \
164  { \
165  return math::property(t.m_cf, names) + t.m_key.property(p, s); \
166  } \
167  template <typename T> \
168  using ps_p##property##_type_ = decltype( \
169  ps_get_##property(std::declval<const typename T::term_type &>(), std::declval<const symbol_fset &>(), \
170  std::declval<const symbol_idx_fset &>(), std::declval<const symbol_fset &>())); \
171  template <typename T> \
172  using ps_p##property##_type \
173  = enable_if_t<common_degree_type_checks<ps_p##property##_type_<T>>::value, ps_p##property##_type_<T>>;
174 PIRANHA_DEFINE_PARTIAL_PS_PROPERTY_GETTER(degree)
175 PIRANHA_DEFINE_PARTIAL_PS_PROPERTY_GETTER(ldegree)
176 #undef PIRANHA_DEFINE_PARTIAL_PS_PROPERTY_GETTER
177 
178 // Total degree truncation.
179 // Case 1: coefficient can truncate, no degree or ldegree in key.
180 template <typename Term, typename T,
181  enable_if_t<has_truncate_degree<typename Term::cf_type, T>::value && (ps_term_score<Term>::value >> 1u) == 0u,
182  int> = 0>
183 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &)
184 {
185  return std::make_pair(true, Term(math::truncate_degree(t.m_cf, max_degree), t.m_key));
186 }
187 
188 // Case 2: coefficient cannot truncate, degree and ldegree in key, degrees are greater_than comparable.
189 // NOTE: here we do not have support for key truncation (yet), so we decide based on the low degree of the key:
190 // if it is larger than the max degree, remove the term, otherwise keep it - it is an all-or-nothing scenario.
191 template <typename Term, typename T,
192  enable_if_t<
193  (ps_term_score<Term>::value >> 1u) == 1u
194  && conjunction<negation<has_truncate_degree<typename Term::cf_type, T>>,
195  is_greater_than_comparable<decltype(std::declval<const typename Term::key_type &>()
196  .ldegree(std::declval<const symbol_fset &>())),
197  T>>::value,
198  int> = 0>
199 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &s)
200 {
201  if (t.m_key.ldegree(s) > max_degree) {
202  // Term must be discarded.
203  return std::make_pair(false, Term());
204  } else {
205  // Keep the term as it is.
206  return std::make_pair(true, Term(t.m_cf, t.m_key));
207  }
208 }
209 
210 // Case 3: coefficient can truncate, degree and ldegree in key, the new max degree type can be used in the
211 // coefficient truncation.
212 // NOTE: again, no key truncation, thus we decrement the real degree of the coefficient by the low degree of the
213 // key. This way we will keep all the important parts, plus some garbage.
214 template <typename Term, typename T,
215  enable_if_t<has_truncate_degree<typename Term::cf_type,
216  decltype(std::declval<const T &>()
217  - std::declval<const typename Term::key_type &>().ldegree(
218  std::declval<const symbol_fset &>()))>::value
219  && (ps_term_score<Term>::value >> 1u) == 1u,
220  int> = 0>
221 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &s)
222 {
223  // The truncation level for the coefficient must be modified in order to take
224  // into account the degree of the key.
225  return std::make_pair(true, Term(math::truncate_degree(t.m_cf, max_degree - t.m_key.ldegree(s)), t.m_key));
226 }
227 
228 // Partial degree truncation.
229 // Case 1: coefficient can truncate, no degree or ldegree in key.
230 template <typename Term, typename T,
231  enable_if_t<has_truncate_degree<typename Term::cf_type, T>::value && (ps_term_score<Term>::value >> 1u) == 0u,
232  int> = 0>
233 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &names,
234  const symbol_idx_fset &, const symbol_fset &)
235 {
236  return std::make_pair(true, Term(math::truncate_degree(t.m_cf, max_degree, names), t.m_key));
237 }
238 
239 // Case 2: coefficient cannot truncate, degree and ldegree in key, degrees are greater_than comparable.
240 template <typename Term, typename T,
241  enable_if_t<
242  (ps_term_score<Term>::value >> 1u) == 1u
243  && conjunction<negation<has_truncate_degree<typename Term::cf_type, T>>,
244  is_greater_than_comparable<
245  decltype(std::declval<const typename Term::key_type &>().ldegree(
246  std::declval<const symbol_idx_fset &>(), std::declval<const symbol_fset &>())),
247  T>>::value,
248  int> = 0>
249 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &,
250  const symbol_idx_fset &p, const symbol_fset &s)
251 {
252  if (t.m_key.ldegree(p, s) > max_degree) {
253  return std::make_pair(false, Term());
254  } else {
255  return std::make_pair(true, Term(t.m_cf, t.m_key));
256  }
257 }
258 
259 // Case 3: coefficient can truncate, degree and ldegree in key, the new max degree type can be used in the
260 // coefficient truncation.
261 template <typename Term, typename T,
262  enable_if_t<has_truncate_degree<typename Term::cf_type,
263  decltype(std::declval<const T &>()
264  - std::declval<const typename Term::key_type &>().ldegree(
265  std::declval<const symbol_idx_fset &>(),
266  std::declval<const symbol_fset &>()))>::value
267  && (ps_term_score<Term>::value >> 1u) == 1u,
268  int> = 0>
269 inline std::pair<bool, Term> ps_truncate_term(const Term &t, const T &max_degree, const symbol_fset &names,
270  const symbol_idx_fset &p, const symbol_fset &s)
271 {
272  return std::make_pair(true,
273  Term(math::truncate_degree(t.m_cf, max_degree - t.m_key.ldegree(p, s), names), t.m_key));
274 }
275 }
276 
278 
313 template <typename Series, typename Derived>
314 class power_series : public Series, power_series_tag
315 {
316  PIRANHA_TT_CHECK(is_series, Series);
317  using base = Series;
318  // Enabler for total degree truncation.
319  template <typename T>
320  using t_truncate_term_t
321  = decltype(ps_truncate_term(std::declval<const typename base::term_type &>(), std::declval<const T &>(),
322  std::declval<const symbol_fset &>()));
323  template <typename T>
324  using truncate_degree_enabler = enable_if_t<is_detected<t_truncate_term_t, T>::value, int>;
325  // Enabler for partial degree truncation.
326  template <typename T>
327  using p_truncate_term_t
328  = decltype(ps_truncate_term(std::declval<const typename base::term_type &>(), std::declval<const T &>(),
329  std::declval<const symbol_fset &>(), std::declval<const symbol_idx_fset &>(),
330  std::declval<const symbol_fset &>()));
331  template <typename T>
332  using truncate_pdegree_enabler = enable_if_t<is_detected<p_truncate_term_t, T>::value, int>;
333  // Lift definitions from the impl namespace.
334  template <typename T>
335  using degree_type = ps_degree_type<T>;
336  template <typename T>
337  using ldegree_type = ps_ldegree_type<T>;
338  template <typename T>
339  using pdegree_type = ps_pdegree_type<T>;
340  template <typename T>
341  using pldegree_type = ps_pldegree_type<T>;
342 
343 public:
345  power_series() = default;
347  power_series(const power_series &) = default;
349  power_series(power_series &&) = default;
352 
359  power_series &operator=(const power_series &other) = default;
361 
366  power_series &operator=(power_series &&other) = default;
370  {
371  PIRANHA_TT_CHECK(is_series, power_series);
372  PIRANHA_TT_CHECK(std::is_base_of, power_series, Derived);
373  }
375 
389  template <typename T = power_series>
390  degree_type<T> degree() const
391  {
392  using term_type = typename T::term_type;
393  auto it = std::max_element(
394  this->m_container.begin(), this->m_container.end(), [this](const term_type &t1, const term_type &t2) {
395  return ps_get_degree(t1, this->m_symbol_set) < ps_get_degree(t2, this->m_symbol_set);
396  });
397  return (it == this->m_container.end()) ? degree_type<T>(0) : ps_get_degree(*it, this->m_symbol_set);
398  }
400 
415  template <typename T = power_series>
416  ldegree_type<T> ldegree() const
417  {
418  using term_type = typename T::term_type;
419  auto it = std::min_element(
420  this->m_container.begin(), this->m_container.end(), [this](const term_type &t1, const term_type &t2) {
421  return ps_get_ldegree(t1, this->m_symbol_set) < ps_get_ldegree(t2, this->m_symbol_set);
422  });
423  return (it == this->m_container.end()) ? ldegree_type<T>(0) : ps_get_ldegree(*it, this->m_symbol_set);
424  }
426 
443  template <typename T = power_series>
444  pdegree_type<T> degree(const symbol_fset &names) const
445  {
446  using term_type = typename T::term_type;
447  const auto idx = ss_intersect_idx(this->m_symbol_set, names);
448  auto it = std::max_element(this->m_container.begin(), this->m_container.end(),
449  [this, &idx, &names](const term_type &t1, const term_type &t2) {
450  return ps_get_degree(t1, names, idx, this->m_symbol_set)
451  < ps_get_degree(t2, names, idx, this->m_symbol_set);
452  });
453  return (it == this->m_container.end()) ? pdegree_type<T>(0)
454  : ps_get_degree(*it, names, idx, this->m_symbol_set);
455  }
457 
474  template <typename T = power_series>
475  pldegree_type<T> ldegree(const symbol_fset &names) const
476  {
477  using term_type = typename T::term_type;
478  const auto idx = ss_intersect_idx(this->m_symbol_set, names);
479  auto it = std::min_element(this->m_container.begin(), this->m_container.end(),
480  [this, &idx, &names](const term_type &t1, const term_type &t2) {
481  return ps_get_ldegree(t1, names, idx, this->m_symbol_set)
482  < ps_get_ldegree(t2, names, idx, this->m_symbol_set);
483  });
484  return (it == this->m_container.end()) ? pldegree_type<T>(0)
485  : ps_get_ldegree(*it, names, idx, this->m_symbol_set);
486  }
488 
509  template <typename T, truncate_degree_enabler<T> = 0>
510  Derived truncate_degree(const T &max_degree) const
511  {
512  Derived retval;
513  retval.m_symbol_set = this->m_symbol_set;
514  const auto it_f = this->m_container.end();
515  for (auto it = this->m_container.begin(); it != it_f; ++it) {
516  auto tmp = ps_truncate_term(*it, max_degree, retval.m_symbol_set);
517  if (tmp.first) {
518  retval.insert(std::move(tmp.second));
519  }
520  }
521  return retval;
522  }
524 
542  template <typename T, truncate_pdegree_enabler<T> = 0>
543  Derived truncate_degree(const T &max_degree, const symbol_fset &names) const
544  {
545  Derived retval;
546  retval.m_symbol_set = this->m_symbol_set;
547  const auto idx = ss_intersect_idx(this->m_symbol_set, names);
548  const auto it_f = this->m_container.end();
549  for (auto it = this->m_container.begin(); it != it_f; ++it) {
550  auto tmp = ps_truncate_term(*it, max_degree, names, idx, retval.m_symbol_set);
551  if (tmp.first) {
552  retval.insert(std::move(tmp.second));
553  }
554  }
555  return retval;
556  }
557 };
558 
559 inline namespace impl
560 {
561 
562 // Enabler for the implementation of degree-related math functions for power_series.
563 template <typename Series>
564 using ps_degree_enabler = enable_if_t<std::is_base_of<power_series_tag, Series>::value>;
565 }
566 
567 namespace math
568 {
569 
571 
575 template <typename Series>
576 struct degree_impl<Series, ps_degree_enabler<Series>> {
578 
588  template <typename... Args>
589  auto operator()(const Series &s, const Args &... args) const -> decltype(s.degree(args...))
590  {
591  return s.degree(args...);
592  }
593 };
594 
596 
600 template <typename Series>
601 struct ldegree_impl<Series, ps_degree_enabler<Series>> {
603 
613  template <typename... Args>
614  auto operator()(const Series &s, const Args &... args) const -> decltype(s.ldegree(args...))
615  {
616  return s.ldegree(args...);
617  }
618 };
619 
621 
625 template <typename Series, typename T>
626 struct truncate_degree_impl<Series, T, ps_degree_enabler<Series>> {
628 
639  template <typename... Args>
640  auto operator()(const Series &s, const T &max_degree, const Args &... args) const
641  -> decltype(s.truncate_degree(max_degree, args...))
642  {
643  return s.truncate_degree(max_degree, args...);
644  }
645 };
646 }
647 }
648 
649 #endif
static const bool value
Value of the type trait.
Definition: math.hpp:2085
static const bool value
Value of the type trait.
Definition: math.hpp:2052
Default functor for the implementation of piranha::math::ldegree().
Definition: math.hpp:1377
symbol_idx_fset ss_intersect_idx(const symbol_fset &s1, const symbol_fset &s2)
Find the indices of the intersection of two symbol_fset.
~power_series()
Trivial destructor.
ldegree_type< T > ldegree() const
Total low degree.
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
Definition: forwarding.hpp:50
pdegree_type< T > degree(const symbol_fset &names) const
Partial degree.
Forwarding macros.
Term class.
Definition: term.hpp:66
Implementation of the piranha::math::truncate_degree() functor.
Definition: math.hpp:1652
static const bool value
Value of the type trait.
Definition: math.hpp:1917
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
power_series & operator=(const power_series &other)=default
Copy assignment operator.
auto operator()(const Series &s, const Args &... args) const -> decltype(s.ldegree(args...))
Call operator.
Derived truncate_degree(const T &max_degree, const symbol_fset &names) const
Partial degree truncation.
Root piranha namespace.
Definition: array_key.hpp:52
T truncate_degree(const T &x, const U &max_degree)
Truncation based on the total degree.
Definition: math.hpp:1699
Type traits.
pldegree_type< T > ldegree(const symbol_fset &names) const
Partial low degree.
auto operator()(const Series &s, const Args &... args) const -> decltype(s.degree(args...))
Call operator.
auto ldegree(const T &x) -> decltype(ldegree_impl< T >()(x))
Total low degree.
Definition: math.hpp:1393
static const bool value
Value of the type trait.
Definition: math.hpp:1892
Default functor for the implementation of piranha::math::degree().
Definition: math.hpp:1325
boost::container::flat_set< symbol_idx > symbol_idx_fset
Flat set of symbol indices.
Power series toolbox.
power_series()=default
Defaulted default constructor.
auto operator()(const Series &s, const T &max_degree, const Args &... args) const -> decltype(s.truncate_degree(max_degree, args...))
Call operator.
Type trait to detect series types.
Definition: series_fwd.hpp:49
Derived truncate_degree(const T &max_degree) const
Total degree truncation.
#define PIRANHA_FORWARDING_ASSIGNMENT(Derived, Base)
Assignment-forwarding macro.
Definition: forwarding.hpp:68
degree_type< T > degree() const
Total degree.