29 #ifndef PIRANHA_POLYNOMIAL_HPP 30 #define PIRANHA_POLYNOMIAL_HPP 33 #include <boost/numeric/conversion/cast.hpp> 37 #include <initializer_list> 46 #include <type_traits> 50 #include <piranha/base_series_multiplier.hpp> 51 #include <piranha/config.hpp> 52 #include <piranha/debug_access.hpp> 53 #include <piranha/detail/atomic_flag_array.hpp> 54 #include <piranha/detail/cf_mult_impl.hpp> 55 #include <piranha/detail/divisor_series_fwd.hpp> 56 #include <piranha/detail/parallel_vector_transform.hpp> 57 #include <piranha/detail/poisson_series_fwd.hpp> 58 #include <piranha/detail/polynomial_fwd.hpp> 59 #include <piranha/detail/safe_integral_adder.hpp> 60 #include <piranha/detail/sfinae_types.hpp> 63 #include <piranha/ipow_substitutable_series.hpp> 64 #include <piranha/is_cf.hpp> 65 #include <piranha/key_is_multipliable.hpp> 66 #include <piranha/kronecker_array.hpp> 67 #include <piranha/kronecker_monomial.hpp> 68 #include <piranha/math.hpp> 69 #include <piranha/monomial.hpp> 70 #include <piranha/mp_integer.hpp> 71 #include <piranha/pow.hpp> 72 #include <piranha/power_series.hpp> 73 #include <piranha/safe_cast.hpp> 74 #include <piranha/series.hpp> 75 #include <piranha/series_multiplier.hpp> 76 #include <piranha/settings.hpp> 77 #include <piranha/substitutable_series.hpp> 78 #include <piranha/symbol_utils.hpp> 79 #include <piranha/t_substitutable_series.hpp> 80 #include <piranha/thread_pool.hpp> 81 #include <piranha/trigonometric_series.hpp> 82 #include <piranha/tuning.hpp> 92 struct polynomial_tag {
97 struct is_polynomial_key {
98 static const bool value =
false;
101 template <
typename T>
102 struct is_polynomial_key<kronecker_monomial<T>> {
103 static const bool value =
true;
106 template <
typename T,
typename U>
107 struct is_polynomial_key<monomial<T, U>> {
108 static const bool value =
true;
112 template <
typename Key>
113 struct key_has_is_linear {
114 template <
typename U>
115 using is_linear_t = decltype(std::declval<const U &>().is_linear(std::declval<const symbol_fset &>()));
116 static const bool value = std::is_same<detected_t<is_linear_t, Key>, std::pair<bool, symbol_idx>>::value;
148 template <
typename Cf,
typename Key>
151 trigonometric_series<ipow_substitutable_series<
152 substitutable_series<t_substitutable_series<series<Cf, Key, polynomial<Cf, Key>>, polynomial<Cf, Key>>,
153 polynomial<Cf, Key>>,
154 polynomial<Cf, Key>>>,
155 polynomial<Cf, Key>>,
156 detail::polynomial_tag
159 PIRANHA_TT_CHECK(detail::is_polynomial_key, Key);
164 template <
typename,
typename>
170 template <
typename,
typename>
180 template <
typename Str>
181 void construct_from_string(Str &&str)
183 using term_type =
typename base::term_type;
189 template <
typename T = Key,
190 enable_if_t<conjunction<detail::key_has_is_linear<T>,
has_safe_cast<
integer, Cf>>::value,
int> = 0>
191 std::map<std::string, integer> integral_combination()
const 193 std::map<std::string, integer> retval;
196 if (unlikely(!p.first)) {
197 piranha_throw(std::invalid_argument,
"polynomial is not an integral linear combination");
199 retval[*(this->
m_symbol_set.nth(p.second))] = safe_cast<integer>(it->m_cf);
205 enable_if_t<disjunction<negation<detail::key_has_is_linear<T>>, negation<has_safe_cast<integer, Cf>>>::value,
207 std::map<std::string, integer> integral_combination()
const 210 "the polynomial type does not support the extraction of a linear combination");
214 template <
typename T,
typename =
void>
215 struct integrate_type_ {
218 template <
typename T>
219 using key_integrate_type
220 = decltype(std::declval<const typename T::term_type::key_type &>()
221 .
integrate(std::declval<const std::string &>(), std::declval<const symbol_fset &>())
225 template <
typename T,
typename ResT>
226 using basic_integrate_requirements =
typename std::enable_if<
229 std::declval<const std::string &>()))>::value
232 detail::true_tt<key_integrate_type<T>>::value &&
236 std::is_constructible<ResT, int>::value>::type;
238 template <
typename T>
239 using nic_res_type = decltype((std::declval<const T &>() * std::declval<const typename T::term_type::cf_type &>())
240 / std::declval<
const key_integrate_type<T> &>());
241 template <
typename T>
242 struct integrate_type_<
243 T, typename
std::enable_if<!is_integrable<typename T::term_type::cf_type>::value
244 && detail::true_tt<basic_integrate_requirements<T, nic_res_type<T>>>::value>::type> {
245 using type = nic_res_type<T>;
249 template <
typename T>
250 using key_partial_type
251 = decltype(std::declval<const typename T::term_type::key_type &>()
252 .
partial(std::declval<const symbol_idx &>(), std::declval<const symbol_fset &>())
255 template <
typename T>
256 using i_cf_type = decltype(
257 math::integrate(std::declval<const typename T::term_type::cf_type &>(), std::declval<const std::string &>()));
259 template <
typename T>
260 using i_cf_type_p = decltype(std::declval<
const i_cf_type<T> &>() * std::declval<
const key_partial_type<T> &>());
262 template <
typename T>
263 using ic_res_type = decltype(std::declval<
const i_cf_type_p<T> &>() * std::declval<const T &>());
264 template <
typename T>
265 struct integrate_type_<
266 T, typename
std::enable_if<
267 is_integrable<typename T::term_type::cf_type>::value
268 && detail::true_tt<basic_integrate_requirements<T, ic_res_type<T>>>::value &&
270 is_addable_in_place<ic_res_type<T>, nic_res_type<T>>::value &&
272 has_safe_cast<integer,
273 decltype(std::declval<const typename T::term_type::key_type &>().degree(
274 std::declval<const symbol_idx_fset &>(), std::declval<const symbol_fset &>()))>::value
277 std::is_constructible<i_cf_type_p<T>, i_cf_type<T>>::value &&
279 std::is_assignable<i_cf_type_p<T> &, i_cf_type_p<T>>::value &&
281 has_negate<i_cf_type_p<T>>::value>::type> {
282 using type = ic_res_type<T>;
285 template <
typename T>
286 using integrate_type =
typename std::enable_if<is_returnable<typename integrate_type_<T>::type>::value,
287 typename integrate_type_<T>::type>::type;
289 template <
typename T = polynomial>
290 integrate_type<T> integrate_impl(
const std::string &s,
const typename base::term_type &
term,
291 const std::true_type &)
const 293 typedef typename base::term_type
term_type;
308 "unable to perform polynomial integration: cannot extract the integral form of an exponent");
313 "unable to perform polynomial integration: negative integral exponent");
320 integrate_type<T> retval(i_cf * tmp);
323 auto partial_key = tmp_key.partial(*idx.begin(), this->
m_symbol_set);
330 tmp_key = std::move(partial_key.second);
333 tmp.insert(
term_type(cf_type(1), tmp_key));
334 retval += i_cf * tmp;
339 template <
typename T = polynomial>
340 integrate_type<T> integrate_impl(
const std::string &,
const typename base::term_type &,
341 const std::false_type &)
const 344 "unable to perform polynomial integration: coefficient type is not integrable");
348 template <
typename T>
350 = decltype(std::declval<Key const &>().
pow(std::declval<const T &>(), std::declval<const symbol_fset &>()));
351 template <
typename T>
352 using pow_ret_type = enable_if_t<is_detected<key_pow_t, T>::value,
354 std::declval<const T &>()))>;
356 template <
typename Series>
357 using inverse_type = decltype(std::declval<const Series &>().
pow(-1));
360 template <
typename T>
361 using degree_type = decltype(
math::degree(std::declval<const T &>()));
362 template <
typename T>
363 using pdegree_type = decltype(
math::degree(std::declval<const T &>(), std::declval<const symbol_fset &>()));
366 template <
typename T>
367 using at_degree_enabler =
typename std::enable_if<
369 && std::is_same<decltype(std::declval<const degree_type<T> &>() - std::declval<
const degree_type<T> &>()),
370 degree_type<T>>::value
374 template <
typename T,
typename U>
375 using at_degree_set_enabler =
376 typename std::enable_if<detail::true_tt<at_degree_enabler<T>>::value &&
has_safe_cast<degree_type<T>, U>::value,
386 template <
typename T = polynomial>
387 static degree_type<T> &get_at_degree_max()
391 static degree_type<T> at_degree_max(0);
392 return at_degree_max;
395 template <
typename Str>
397 typename std::enable_if<std::is_same<typename std::decay<Str>::type, std::string>::value
398 || std::is_same<typename std::decay<Str>::type,
char *>::value
399 || std::is_same<typename std::decay<Str>::type,
const char *>::value,
402 template <
typename T>
403 using find_cf_enabler =
typename std::enable_if<
404 std::is_constructible<typename base::term_type::key_type, decltype(std::begin(std::declval<const T &>())),
405 decltype(std::end(std::declval<const T &>())),
const symbol_fset &>::value
408 template <
typename T>
409 using find_cf_init_list_enabler = find_cf_enabler<std::initializer_list<T>>;
410 template <
typename Iterator>
411 Cf find_cf_impl(Iterator
begin, Iterator
end)
const 421 template <
typename T>
423 typename std::enable_if<std::is_same<T, decltype(std::declval<const T &>() * std::declval<const T &>())>::value,
426 template <
typename T,
typename U>
428 typename std::enable_if<std::is_same<T, decltype(std::declval<const T &>() * std::declval<const T &>())>::value
430 && detail::true_tt<at_degree_enabler<T>>::value,
434 template <
typename Functor>
437 return series_merge_f(p1, p2, runner);
440 template <
typename T>
441 static void truncation_clear_pow_cache(
int mode,
const T &max_degree,
const symbol_fset &names)
444 if (s_at_degree_mode != mode || get_at_degree_max() != max_degree || names != s_at_degree_names) {
451 template <
typename Cf2>
479 template <
typename Str, str_enabler<Str> = 0>
482 construct_from_string(std::forward<Str>(name));
529 template <
typename T>
530 pow_ret_type<T>
pow(
const T &x)
const 532 using ret_type = pow_ret_type<T>;
533 typedef typename ret_type::term_type
term_type;
541 retval.insert(
term_type(std::move(cf), std::move(key)));
556 template <
typename Series = polynomial>
559 return this->
pow(-1);
590 template <
typename T = polynomial>
591 integrate_type<T>
integrate(
const std::string &name)
const 593 typedef typename base::term_type
term_type;
595 integrate_type<T> retval(0);
598 const auto aug_ss = [
this, &name]() ->
symbol_fset {
610 auto key_int = it->m_key.integrate(name, this->
m_symbol_set);
612 retval += (tmp * it->m_cf) / key_int.first;
636 template <
typename U,
typename T = polynomial, at_degree_set_enabler<T, U> = 0>
640 auto new_degree =
safe_cast<degree_type<T>>(max_degree);
644 auto &at_dm = get_at_degree_max();
645 std::lock_guard<std::mutex> lock(s_at_degree_mutex);
648 truncation_clear_pow_cache(1, new_degree,
symbol_fset{});
649 s_at_degree_mode = 1;
651 at_dm = std::move(new_degree);
653 s_at_degree_names.clear();
675 template <
typename U,
typename T = polynomial, at_degree_set_enabler<T, U> = 0>
679 auto new_degree =
safe_cast<degree_type<T>>(max_degree);
680 auto new_names = names;
681 auto &at_dm = get_at_degree_max();
682 std::lock_guard<std::mutex> lock(s_at_degree_mutex);
683 truncation_clear_pow_cache(2, new_degree, new_names);
684 s_at_degree_mode = 2;
685 at_dm = std::move(new_degree);
686 s_at_degree_names = std::move(new_names);
700 template <
typename T = polynomial, at_degree_enabler<T> = 0>
703 degree_type<T> new_degree(0);
704 auto &at_dm = get_at_degree_max();
705 std::lock_guard<std::mutex> lock(s_at_degree_mutex);
706 s_at_degree_mode = 0;
707 at_dm = std::move(new_degree);
708 s_at_degree_names.clear();
726 template <
typename T = polynomial, at_degree_enabler<T> = 0>
729 std::lock_guard<std::mutex> lock(s_at_degree_mutex);
730 return std::make_tuple(s_at_degree_mode, get_at_degree_max(), s_at_degree_names);
753 template <
typename T, find_cf_enabler<T> = 0>
756 return find_cf_impl(std::begin(c), std::end(c));
773 template <
typename T, find_cf_init_list_enabler<T> = 0>
776 return find_cf_impl(std::begin(l), std::end(l));
799 template <
typename T = polynomial, um_enabler<T> = 0>
805 return um_tm_implementation(p1, p2, runner);
834 template <
typename U,
typename T = polynomial, tm_enabler<T, U> = 0>
842 return um_tm_implementation(p1, p2, runner);
871 template <
typename U,
typename T = polynomial, tm_enabler<T, U> = 0>
881 return um_tm_implementation(p1, p2, runner);
886 static std::mutex s_at_degree_mutex;
887 static int s_at_degree_mode;
892 template <
typename Cf,
typename Key>
893 std::mutex polynomial<Cf, Key>::s_at_degree_mutex;
895 template <
typename Cf,
typename Key>
896 int polynomial<Cf, Key>::s_at_degree_mode = 0;
898 template <
typename Cf,
typename Key>
899 symbol_fset polynomial<Cf, Key>::s_at_degree_names;
905 template <
typename T>
906 struct is_kronecker_monomial {
907 static const bool value =
false;
910 template <
typename T>
911 struct is_kronecker_monomial<kronecker_monomial<T>> {
912 static const bool value =
true;
915 template <
typename T>
917 static const bool value =
false;
920 template <
typename T,
typename S>
921 struct is_monomial<monomial<T, S>> {
922 static const bool value =
true;
926 template <
typename S,
typename T>
927 class has_set_auto_truncate_degree : sfinae_types
930 template <
typename S1,
typename T1>
931 static auto test(
const S1 &,
const T1 &t) -> decltype(S1::set_auto_truncate_degree(t),
void(), yes());
935 static const bool value = std::is_same<yes, decltype(test(std::declval<S>(), std::declval<T>()))>::value;
938 template <
typename S,
typename T>
939 const bool has_set_auto_truncate_degree<S, T>::value;
941 template <
typename S>
942 class has_get_auto_truncate_degree : sfinae_types
944 template <
typename S1>
945 static auto test(
const S1 &) -> decltype(S1::get_auto_truncate_degree(),
void(), yes());
949 static const bool value = std::is_same<yes, decltype(test(std::declval<S>()))>::value;
952 template <
typename S>
953 const bool has_get_auto_truncate_degree<S>::value;
956 template <
typename Series>
957 using poly_multiplier_enabler =
typename std::enable_if<std::is_base_of<detail::polynomial_tag, Series>::value>::type;
977 template <
typename Series>
983 template <
typename T>
984 using cf_t =
typename T::term_type::cf_type;
986 template <
typename T>
987 using key_t =
typename T::term_type::key_type;
990 struct update_minmax {
991 template <
typename T>
992 std::pair<T, T> operator()(
const std::pair<T, T> &p,
const T &v)
const 994 return std::make_pair(v < p.first ? v : p.first, v > p.second ? v : p.second);
1000 typename std::enable_if<
1001 detail::is_monomial<key_t<T>>::value && !std::is_integral<typename key_t<T>::value_type>::value,
int>::type
1003 void check_bounds()
const 1008 typename T = Series,
1009 typename std::enable_if<
1010 detail::is_monomial<key_t<T>>::value && std::is_integral<typename key_t<T>::value_type>::value,
int>::type
1012 void check_bounds()
const 1014 using expo_type =
typename key_t<T>::value_type;
1015 using term_type =
typename Series::term_type;
1016 using mm_vec = std::vector<std::pair<expo_type, expo_type>>;
1019 piranha_assert(this->m_v1.size() != 0u && this->m_v2.size() != 0u);
1023 auto monomial_checker = [
this](
const term_type &t) {
return t.m_key.size() == this->m_ss.size(); };
1024 (void)monomial_checker;
1027 auto thread_func = [&mut,
this, &monomial_checker](
unsigned t_idx,
const v_ptr *vp, mm_vec *mmv) {
1028 piranha_assert(t_idx < this->m_n_threads);
1030 const auto block_size = vp->size() / this->m_n_threads;
1031 auto start = vp->data() + t_idx * block_size;
1033 = vp->data() + ((t_idx == this->m_n_threads - 1u) ? vp->size() : ((t_idx + 1u) * block_size));
1037 piranha_assert(this->m_n_threads > 1u);
1040 piranha_assert(monomial_checker(**start));
1042 mm_vec minmax_values;
1045 std::transform((*start)->m_key.begin(), (*start)->m_key.end(), std::back_inserter(minmax_values),
1046 [](
const expo_type &v) {
return std::make_pair(v, v); });
1049 for (; start != end; ++start) {
1050 piranha_assert(monomial_checker(**start));
1055 std::transform(minmax_values.begin(), minmax_values.end(), (*start)->m_key.begin(),
1056 minmax_values.begin(), update_minmax{});
1058 if (this->m_n_threads == 1u) {
1060 piranha_assert(mmv->empty());
1062 *mmv = std::move(minmax_values);
1064 std::lock_guard<std::mutex> lock(mut);
1067 *mmv = std::move(minmax_values);
1069 piranha_assert(minmax_values.size() == mmv->size());
1072 = [](
const std::pair<expo_type, expo_type> &p1,
const std::pair<expo_type, expo_type> &p2) {
1073 return std::make_pair(p1.first < p2.first ? p1.first : p2.first,
1074 p1.second > p2.second ? p1.second : p2.second);
1076 std::transform(minmax_values.begin(), minmax_values.end(), mmv->begin(), mmv->begin(), updater);
1081 mm_vec minmax_values1, minmax_values2;
1082 check_bounds_impl(minmax_values1, minmax_values2, thread_func);
1085 std::vector<std::pair<integer, integer>> minmax_values;
1087 minmax_values1.begin(), minmax_values1.end(), minmax_values2.begin(), std::back_inserter(minmax_values),
1088 [](
const std::pair<expo_type, expo_type> &p1,
const std::pair<expo_type, expo_type> &p2) {
1091 piranha_assert(minmax_values.size() == minmax_values1.size());
1092 piranha_assert(minmax_values.size() == minmax_values2.size());
1094 for (decltype(minmax_values.size()) i = 0u; i < minmax_values.size(); ++i) {
1096 (void)static_cast<expo_type>(minmax_values[i].first);
1097 (void)static_cast<expo_type>(minmax_values[i].second);
1099 piranha_throw(std::overflow_error,
"monomial components are out of bounds");
1103 template <
typename T = Series,
1104 typename std::enable_if<detail::is_kronecker_monomial<key_t<T>>::value,
int>::type = 0>
1105 void check_bounds()
const 1107 using value_type =
typename key_t<Series>::value_type;
1110 using mm_vec = std::vector<std::pair<value_type, value_type>>;
1111 piranha_assert(this->m_v1.size() != 0u && this->m_v2.size() != 0u);
1115 piranha_assert(this->m_ss.size() < ka::get_limits().size());
1117 auto thread_func = [&mut,
this](
unsigned t_idx,
const v_ptr *vp, mm_vec *mmv) {
1118 piranha_assert(t_idx < this->m_n_threads);
1119 const auto block_size = vp->size() / this->m_n_threads;
1120 auto start = vp->data() + t_idx * block_size;
1122 = vp->data() + ((t_idx == this->m_n_threads - 1u) ? vp->size() : ((t_idx + 1u) * block_size));
1124 piranha_assert(this->m_n_threads > 1u);
1127 mm_vec minmax_values;
1132 auto tmp_vec = (*start)->m_key.unpack(this->m_ss);
1134 std::transform(tmp_vec.begin(), tmp_vec.end(), std::back_inserter(minmax_values),
1135 [](
const value_type &v) {
return std::make_pair(v, v); });
1137 for (; start != end; ++start) {
1138 tmp_vec = (*start)->m_key.unpack(this->m_ss);
1139 std::transform(minmax_values.begin(), minmax_values.end(), tmp_vec.begin(), minmax_values.begin(),
1142 if (this->m_n_threads == 1u) {
1143 piranha_assert(mmv->empty());
1144 *mmv = std::move(minmax_values);
1146 std::lock_guard<std::mutex> lock(mut);
1148 *mmv = std::move(minmax_values);
1150 piranha_assert(minmax_values.size() == mmv->size());
1152 = [](
const std::pair<value_type, value_type> &p1,
const std::pair<value_type, value_type> &p2) {
1153 return std::make_pair(p1.first < p2.first ? p1.first : p2.first,
1154 p1.second > p2.second ? p1.second : p2.second);
1156 std::transform(minmax_values.begin(), minmax_values.end(), mmv->begin(), mmv->begin(), updater);
1160 mm_vec minmax_values1, minmax_values2;
1161 check_bounds_impl(minmax_values1, minmax_values2, thread_func);
1162 std::vector<std::pair<integer, integer>> minmax_values;
1164 minmax_values1.begin(), minmax_values1.end(), minmax_values2.begin(), std::back_inserter(minmax_values),
1165 [](
const std::pair<value_type, value_type> &p1,
const std::pair<value_type, value_type> &p2) {
1169 const auto &minmax_vec
1170 = std::get<0u>(ka::get_limits()[
static_cast<decltype(ka::get_limits().size())
>(this->m_ss.size())]);
1171 piranha_assert(minmax_values.size() == minmax_vec.size());
1172 piranha_assert(minmax_values.size() == minmax_values1.size());
1173 piranha_assert(minmax_values.size() == minmax_values2.size());
1174 for (decltype(minmax_values.size()) i = 0u; i < minmax_values.size(); ++i) {
1175 if (unlikely(minmax_values[i].first < -minmax_vec[i] || minmax_values[i].second > minmax_vec[i])) {
1176 piranha_throw(std::overflow_error,
"Kronecker monomial components are out of bounds");
1181 template <
typename MmVec,
typename Func>
1182 void check_bounds_impl(MmVec &minmax_values1, MmVec &minmax_values2, Func &thread_func)
const 1184 if (this->m_n_threads == 1u) {
1185 thread_func(0u, &(this->m_v1), &minmax_values1);
1186 thread_func(0u, &(this->m_v2), &minmax_values2);
1192 for (
unsigned i = 0u; i < this->m_n_threads; ++i) {
1208 for (
unsigned i = 0u; i < this->m_n_threads; ++i) {
1223 template <
typename T>
1224 using call_enabler =
typename std::enable_if<
1228 template <typename T, typename std::enable_if<!std::is_integral<T>::value,
int>::type = 0>
1229 static T degree_sub(
const T &a,
const T &b)
1233 template <typename T, typename std::enable_if<std::is_integral<T>::value,
int>::type = 0>
1234 static T degree_sub(
const T &a,
const T &b)
1237 detail::safe_integral_subber(retval, b);
1241 template <
typename T = Series,
1242 typename std::enable_if<detail::is_kronecker_monomial<typename T::term_type::key_type>::value,
int>::type
1244 Series um_impl()
const 1246 return untruncated_kronecker_mult();
1248 template <
typename T = Series,
1249 typename std::enable_if<!detail::is_kronecker_monomial<typename T::term_type::key_type>::value,
int>::type
1251 Series um_impl()
const 1253 return this->plain_multiplication();
1281 if (unlikely(this->m_v1.empty() || this->m_v2.empty() || this->m_ss.size() == 0u)) {
1319 template <
typename T = Series, call_enabler<T> = 0>
1389 template <
typename T,
typename... Args>
1395 using term_type =
typename Series::term_type;
1397 using degree_type = decltype(ps_get_degree(term_type{}, this->m_ss));
1399 namespace sph = std::placeholders;
1400 static_assert(std::is_same<T, degree_type>::value,
"Invalid degree type");
1401 static_assert(detail::has_get_auto_truncate_degree<Series>::value,
"Invalid series type");
1403 using d_size_type =
typename std::vector<degree_type>::size_type;
1404 std::vector<degree_type> v_d1(safe_cast<d_size_type>(this->m_v1.size())),
1405 v_d2(safe_cast<d_size_type>(this->m_v2.size()));
1406 detail::parallel_vector_transform(
1407 this->m_n_threads, this->m_v1, v_d1,
1408 std::bind(term_degree_getter{}, sph::_1, std::cref(this->m_ss), std::cref(args)...));
1409 detail::parallel_vector_transform(
1410 this->m_n_threads, this->m_v2, v_d2,
1411 std::bind(term_degree_getter{}, sph::_1, std::cref(this->m_ss), std::cref(args)...));
1414 std::vector<size_type> idx_vector(
safe_cast<
typename std::vector<size_type>::size_type>(this->m_v2.size()));
1415 std::iota(idx_vector.begin(), idx_vector.end(),
size_type(0u));
1417 std::stable_sort(idx_vector.begin(), idx_vector.end(), [&v_d2](
const size_type &i1,
const size_type &i2) {
1418 return v_d2[
static_cast<d_size_type
>(i1)] < v_d2[static_cast<d_size_type>(i2)];
1421 decltype(this->m_v2) v2_copy(this->m_v2.size());
1422 decltype(v_d2) v_d2_copy(v_d2.size());
1423 std::transform(idx_vector.begin(), idx_vector.end(), v2_copy.begin(),
1424 [
this](
const size_type &i) {
return this->m_v2[i]; });
1425 std::transform(idx_vector.begin(), idx_vector.end(), v_d2_copy.begin(),
1426 [&v_d2](
const size_type &i) {
return v_d2[
static_cast<d_size_type
>(i)]; });
1427 this->m_v2 = std::move(v2_copy);
1428 v_d2 = std::move(v_d2_copy);
1430 const auto sl = _get_skip_limits(v_d1, v_d2, max_degree);
1431 auto lf = [&sl](
const size_type &idx1) {
1432 return sl[
static_cast<typename std::vector<size_type>::size_type
>(idx1)];
1434 return this->plain_multiplication(lf);
1463 template <
typename T>
1464 std::vector<typename base::size_type>
_get_skip_limits(
const std::vector<T> &v_d1,
const std::vector<T> &v_d2,
1465 const T &max_degree)
const 1473 using d_size_type =
typename std::vector<T>::size_type;
1474 piranha_assert(std::is_sorted(v_d2.begin(), v_d2.end()));
1476 std::vector<size_type> idx_vector(
safe_cast<
typename std::vector<size_type>::size_type>(this->m_v2.size()));
1477 std::iota(idx_vector.begin(), idx_vector.end(),
size_type(0u));
1479 std::vector<size_type> retval;
1480 for (
const auto &d1 : v_d1) {
1489 const T comp = degree_sub(max_degree, d1);
1490 const auto it = std::upper_bound(
1491 idx_vector.begin(), idx_vector.end(), comp,
1492 [&v_d2](
const T &c,
const size_type &idx) {
return c < v_d2[static_cast<d_size_type>(idx)]; });
1493 retval.push_back(it == idx_vector.end() ?
static_cast<size_type>(idx_vector.size()) : *it);
1496 auto retval_checker = [&retval, &v_d1, &v_d2, &max_degree,
this]() ->
bool {
1497 for (decltype(retval.size()) i = 0u; i < retval.size(); ++i) {
1499 if (retval[i] == v_d2.size()) {
1502 if (retval[i] > v_d2.size()) {
1505 if (!(v_d2[static_cast<d_size_type>(retval[i])]
1506 > this->degree_sub(max_degree, v_d1[static_cast<d_size_type>(i)]))) {
1512 (void)retval_checker;
1513 piranha_assert(retval.size() == this->m_v1.size());
1514 piranha_assert(retval_checker());
1521 template <typename T, typename std::enable_if<!is_mp_rational<T>::value,
int>::type = 0>
1522 static void fma_wrap(T &a,
const T &b,
const T &c)
1526 template <typename T, typename std::enable_if<is_mp_rational<T>::value,
int>::type = 0>
1527 static void fma_wrap(T &a,
const T &b,
const T &c)
1533 template <
typename T = Series,
1534 typename std::enable_if<!detail::has_get_auto_truncate_degree<T>::value,
int>::type = 0>
1535 Series plain_multiplication_wrapper()
const 1537 return this->plain_multiplication();
1540 template <
typename T = Series,
1541 typename std::enable_if<detail::has_get_auto_truncate_degree<T>::value,
int>::type = 0>
1542 Series plain_multiplication_wrapper()
const 1544 const auto t = T::get_auto_truncate_degree();
1545 if (std::get<0u>(t) == 0) {
1547 return this->plain_multiplication();
1550 if (std::get<0u>(t) == 1) {
1552 return _truncated_multiplication(std::get<1u>(t));
1554 piranha_assert(std::get<0u>(t) == 2);
1557 return _truncated_multiplication(std::get<1u>(t), std::get<2u>(t), idx);
1562 struct term_degree_sorter {
1563 using term_type =
typename Series::term_type;
1564 template <
typename... Args>
1565 bool operator()(term_type
const *p1, term_type
const *p2,
const symbol_fset &ss,
const Args &... args)
const 1567 return ps_get_degree(*p1, args..., ss) < ps_get_degree(*p2, args..., ss);
1570 struct term_degree_getter {
1571 using term_type =
typename Series::term_type;
1572 template <
typename... Args>
1573 auto operator()(term_type
const *p,
const symbol_fset &ss,
const Args &... args)
const 1574 -> decltype(ps_get_degree(*p, args..., ss))
1576 return ps_get_degree(*p, args..., ss);
1581 template <
typename T = Series,
1582 typename std::enable_if<!detail::is_kronecker_monomial<typename T::term_type::key_type>::value,
int>::type
1584 Series execute()
const 1586 return plain_multiplication_wrapper();
1589 template <
typename T = Series,
1590 typename std::enable_if<detail::has_get_auto_truncate_degree<T>::value,
int>::type = 0>
1591 bool check_truncation()
const 1593 const auto t = T::get_auto_truncate_degree();
1594 return std::get<0u>(t) != 0;
1596 template <
typename T = Series,
1597 typename std::enable_if<!detail::has_get_auto_truncate_degree<T>::value,
int>::type = 0>
1598 bool check_truncation()
const 1604 template <
typename T = Series,
1605 typename std::enable_if<detail::is_kronecker_monomial<typename T::term_type::key_type>::value,
int>::type
1607 Series execute()
const 1609 if (check_truncation()) {
1610 return plain_multiplication_wrapper();
1612 return untruncated_kronecker_mult();
1614 template <
typename T = Series,
1615 typename std::enable_if<detail::is_kronecker_monomial<typename T::term_type::key_type>::value,
int>::type
1617 Series untruncated_kronecker_mult()
const 1620 const auto size1 = this->m_v1.size(), size2 = this->m_v2.size();
1623 bool estimate =
true;
1625 if (
integer(size1) * size2 <
integer(e_thr) * e_thr && this->m_n_threads == 1u) {
1633 return this->plain_multiplication();
1637 retval.set_symbol_set(this->m_ss);
1639 if (unlikely(!size1 || !size2)) {
1649 = this->
template estimate_final_series_size<1u, typename base::template plain_multiplier<false>>();
1651 retval._container().rehash(boost::numeric_cast<typename Series::size_type>(
1652 std::ceil(static_cast<double>(est) / retval._container().max_load_factor())),
1654 piranha_assert(retval._container().bucket_count());
1655 sparse_kronecker_multiplication(retval);
1658 void sparse_kronecker_multiplication(Series &retval)
const 1660 using bucket_size_type =
typename base::bucket_size_type;
1661 using size_type =
typename base::size_type;
1662 using term_type =
typename Series::term_type;
1667 using task_type = std::tuple<size_type, size_type, size_type>;
1669 auto &v1 = this->m_v1;
1670 auto &v2 = this->m_v2;
1671 const auto size1 = v1.size();
1672 const auto size2 = v2.size();
1673 auto &container = retval._container();
1676 auto r_bucket = [&container](term_type
const *p) {
return container._bucket_from_hash(p->hash()); };
1678 auto term_cmp = [&r_bucket](term_type
const *p1, term_type
const *p2) {
return r_bucket(p1) < r_bucket(p2); };
1679 std::stable_sort(v1.begin(), v1.end(), term_cmp);
1680 std::stable_sort(v2.begin(), v2.end(), term_cmp);
1687 auto task_cmp = [&r_bucket, &v1, &v2](
const task_type &t1,
const task_type &t2) {
1688 return r_bucket(v1[std::get<0u>(t1)]) + r_bucket(v2[std::get<1u>(t1)])
1689 < r_bucket(v1[std::get<0u>(t2)]) + r_bucket(v2[std::get<1u>(t2)]);
1694 auto task_split = [block_size](
const task_type &t, std::vector<task_type> &out) {
1695 size_type start = std::get<1u>(t), end = std::get<2u>(t);
1696 while (static_cast<size_type>(end - start) > block_size) {
1697 out.emplace_back(std::get<0u>(t), start, static_cast<size_type>(start + block_size));
1698 start =
static_cast<size_type
>(start + block_size);
1701 out.emplace_back(std::get<0u>(t), start, end);
1705 const auto it_end = container.end();
1708 auto task_consume = [&v1, &v2, &container, it_end,
this](
const task_type &task, term_type &tmp_term) {
1710 term_type
const *t1 = v1[std::get<0u>(task)];
1712 term_type
const **start2 = &(v2[std::get<1u>(task)]), **end2 = &(v2[std::get<2u>(task)]);
1714 using int_type = decltype(t1->m_key.get_int());
1716 const auto &cf1 = t1->m_cf;
1717 const int_type key1 = t1->m_key.get_int();
1719 for (; start2 != end2; ++start2) {
1721 const auto &cur = **start2;
1724 tmp_term.m_key.set_int(static_cast<int_type>(key1 + cur.m_key.get_int()));
1726 auto bucket_idx = container._bucket(tmp_term);
1727 const auto it = container._find(tmp_term, bucket_idx);
1732 cf_mult_impl(tmp_term.m_cf, cf1, cur.m_cf);
1733 container._unique_insert(tmp_term, bucket_idx);
1738 this->fma_wrap(it->m_cf, cf1, cur.m_cf);
1742 if (this->m_n_threads == 1u) {
1746 std::vector<task_type> tasks;
1747 for (decltype(v1.size()) i = 0u; i < size1; ++i) {
1748 task_split(std::make_tuple(i, size_type(0u), size2), tasks);
1751 std::stable_sort(tasks.begin(), tasks.end(), task_cmp);
1754 for (
const auto &t : tasks) {
1755 task_consume(t, tmp_term);
1757 this->sanitise_series(retval, this->m_n_threads);
1758 this->finalise_series(retval);
1760 retval._container().clear();
1766 const bucket_size_type bucket_count = container.bucket_count();
1770 const unsigned zm = 10u;
1771 const bucket_size_type n_zones =
static_cast<bucket_size_type
>(
integer(this->m_n_threads) * zm);
1773 const bucket_size_type bpz =
static_cast<bucket_size_type
>(bucket_count / n_zones);
1775 std::vector<std::vector<task_type>> task_table;
1776 task_table.resize(
safe_cast<decltype(task_table.size())>(n_zones));
1783 = [&v1, &v2, &r_bucket](size_type first, size_type last, bucket_size_type zb, size_type i) -> size_type {
1784 piranha_assert(first <= last);
1785 bucket_size_type ib = r_bucket(v1[i]);
1790 const auto cmp =
static_cast<bucket_size_type
>(zb - ib);
1791 size_type idx, step, count =
static_cast<size_type
>(last - first);
1792 while (count > 0u) {
1794 step =
static_cast<size_type
>(count / 2u);
1795 idx =
static_cast<size_type
>(idx + step);
1796 if (r_bucket(v2[idx]) < cmp) {
1797 idx =
static_cast<size_type
>(idx + 1u);
1799 if (count <= step + 1u) {
1802 count =
static_cast<size_type
>(count - (step + 1u));
1810 auto table_filler = [&task_table, bpz,
this, bucket_count, size1, size2, &l_bound, &task_split,
1811 &task_cmp](
const unsigned &thread_idx) {
1812 for (
unsigned n = 0u; n < zm; ++n) {
1813 std::vector<task_type> cur_tasks;
1815 bucket_size_type a =
static_cast<bucket_size_type
>(thread_idx * bpz * zm + n * bpz);
1817 if (n == zm - 1u && thread_idx == this->m_n_threads - 1u) {
1821 b =
static_cast<bucket_size_type
>(a + bpz);
1824 for (size_type i = 0u; i < size1; ++i) {
1825 auto t = std::make_tuple(i, l_bound(0u, size2, a, i), l_bound(0u, size2, b, i));
1826 if (std::get<1u>(t) == 0u && std::get<2u>(t) == 0u) {
1831 task_split(t, cur_tasks);
1836 for (size_type i = 0u; i < size1; ++i) {
1837 auto t = std::make_tuple(i, l_bound(0u, size2, static_cast<bucket_size_type>(a + bucket_count), i),
1838 l_bound(0u, size2, static_cast<bucket_size_type>(b + bucket_count), i));
1839 if (std::get<1u>(t) == 0u && std::get<2u>(t) == 0u) {
1842 task_split(t, cur_tasks);
1845 std::stable_sort(cur_tasks.begin(), cur_tasks.end(), task_cmp);
1847 task_table[
static_cast<decltype(task_table.size())
>(thread_idx * zm + n)] = std::move(cur_tasks);
1851 future_list<decltype(table_filler(0u))> ff_list;
1853 for (
unsigned i = 0u; i < this->m_n_threads; ++i) {
1865 auto table_checker = [&task_table, size1, size2, &r_bucket, bpz, bucket_count, &v1, &v2]() ->
bool {
1871 for (decltype(task_table.size()) i = 0u; i < task_table.size(); ++i) {
1872 const auto &v = task_table[i];
1874 bucket_size_type a =
static_cast<bucket_size_type
>(bpz * i), b;
1876 if (i == task_table.size() - 1u) {
1879 b =
static_cast<bucket_size_type
>(a + bpz);
1881 for (
const auto &t : v) {
1882 auto idx1 = std::get<0u>(t), start2 = std::get<1u>(t), end2 = std::get<2u>(t);
1883 using int_type = decltype(v1[idx1]->m_key.get_int());
1884 piranha_assert(start2 <= end2);
1885 tot_n += end2 - start2;
1886 for (; start2 != end2; ++start2) {
1887 tmp_term.m_key.set_int(
1888 static_cast<int_type>(v1[idx1]->m_key.get_int() + v2[start2]->m_key.get_int()));
1889 auto b_idx = r_bucket(&tmp_term);
1890 if (b_idx < a || b_idx >= b) {
1896 return tot_n ==
integer(size1) * size2;
1898 (void)table_checker;
1899 piranha_assert(table_checker());
1901 detail::atomic_flag_array af(safe_cast<std::size_t>(task_table.size()));
1903 auto thread_functor = [&task_table, &af, &task_consume](
const unsigned &thread_idx) {
1904 using t_size_type = decltype(task_table.size());
1908 auto t_idx =
static_cast<t_size_type
>(t_size_type(thread_idx) * zm);
1909 const auto start_t_idx = t_idx;
1912 if (!af[static_cast<std::size_t>(t_idx)].test_and_set()) {
1914 const auto &cur_tasks = task_table[t_idx];
1915 for (
const auto &t : cur_tasks) {
1916 task_consume(t, tmp_term);
1920 t_idx =
static_cast<t_size_type
>(t_idx + 1u);
1921 if (t_idx == task_table.size()) {
1925 if (t_idx == start_t_idx) {
1931 future_list<decltype(thread_functor(0u))> ft_list;
1933 for (
unsigned i = 0u; i < this->m_n_threads; ++i) {
1941 this->sanitise_series(retval, this->m_n_threads);
1942 this->finalise_series(retval);
1946 retval._container().clear();
size_type size() const
Series size.
math_pow_t< T, U > pow(const T &x, const U &y)
Exponentiation.
~polynomial()
Trivial destructor.
Cf m_cf
Coefficient member.
void wait_all()
Wait on all the futures.
inverse_type< Series > invert() const
Inversion.
Type trait for multipliable key.
detail::math_integrate_type< T > integrate(const T &x, const std::string &str)
Integration.
Multiprecision integer class.
mp_integer< 1 > integer
Alias for piranha::mp_integer with 1 limb of static storage.
Equality-comparable type trait.
detail::math_partial_type< T > partial(const T &x, const std::string &str)
Partial derivative.
polynomial(Str &&name)
Constructor from symbol name.
Type trait to detect coefficient types.
static polynomial untruncated_multiplication(const polynomial &p1, const polynomial &p2)
Untruncated multiplication.
const_iterator find(const key_type &k) const
Find element.
Trigonometric series toolbox.
symbol_idx_fset ss_intersect_idx(const symbol_fset &s1, const symbol_fset &s2)
Find the indices of the intersection of two symbol_fset.
In-place addable type trait.
const_iterator end() const
Const end iterator.
void negate(T &x)
In-place negation.
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
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.
Series _untruncated_multiplication() const
Untruncated multiplication.
static std::tuple< int, degree_type< T >, symbol_fset > get_auto_truncate_degree()
Query the status of the degree-based auto-truncation mechanism.
static void clear_pow_cache()
Clear the internal cache of natural powers.
Type trait to detect the presence of the piranha::math::is_zero() function.
partial_type< Series > partial(const std::string &name) const
Partial derivative.
integrate_type< T > integrate(const std::string &name) const
Integration.
static polynomial truncated_multiplication(const polynomial &p1, const polynomial &p2, const U &max_degree, const symbol_fset &names)
Truncated multiplication (partial degree).
typename v_ptr::size_type size_type
The size type of base_series_multiplier::v_ptr.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
Key key_type
Alias for the key type.
Type trait to detect if types can be used in piranha::math::truncate_degree().
polynomial & operator=(const polynomial &other)=default
Copy assignment operator.
const_iterator begin() const
Const begin iterator.
Series operator()() const
Perform multiplication.
Cf cf_type
Alias for the coefficient type.
Series _truncated_multiplication(const T &max_degree, const Args &... args) const
Truncated multiplication.
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.
Class to store a list of futures.
Toolbox for substitutable series.
term< Cf, Key > term_type
Alias for term type.
const_iterator begin() const
Begin iterator.
static unsigned long get_estimate_threshold()
Get the series estimation threshold.
Type trait to detect the availability of piranha::math::multiply_accumulate().
Type trait for integrable types.
void multiply_accumulate(T &x, const T &y, const T &z)
Multiply-accumulate.
const_iterator end() const
End iterator.
static polynomial truncated_multiplication(const polynomial &p1, const polynomial &p2, const U &max_degree)
Truncated multiplication (total degree).
Cf find_cf(const T &c) const
Find coefficient.
static void unset_auto_truncate_degree()
Disable degree-based auto-truncation.
static void set_auto_truncate_degree(const U &max_degree)
Set total-degree-based auto-truncation.
void insert(T &&term)
Insert term.
static unsigned long get_multiplication_block_size()
Get the multiplication block size.
static bool get_parallel_memory_set()
Get the parallel_memory_set flag.
boost::container::flat_set< symbol_idx > symbol_idx_fset
Flat set of symbol indices.
Type trait to detect piranha::safe_cast().
Toolbox for series suitable for integral power substitution.
polynomial()=default
Defaulted default constructor.
series_multiplier(const Series &s1, const Series &s2)
Constructor.
std::vector< typename base::size_type > _get_skip_limits(const std::vector< T > &v_d1, const std::vector< T > &v_d2, const T &max_degree) const
Establish skip limits for truncated multiplication.
void set_symbol_set(const symbol_fset &args)
Symbol set setter.
symbol_fset m_symbol_set
Symbol set.
container_type m_container
Terms container.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
Type trait to detect series types.
static void set_auto_truncate_degree(const U &max_degree, const symbol_fset &names)
Set partial-degree-based auto-truncation.
pow_ret_type< T > pow(const T &x) const
Override default exponentiation method.
#define PIRANHA_FORWARDING_ASSIGNMENT(Derived, Base)
Assignment-forwarding macro.
std::vector< typename Series::term_type const * > v_ptr
Alias for a vector of const pointers to series terms.
Exception to signal failure in piranha::safe_cast().
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
piranha::power_series< trigonometric_series< ipow_substitutable_series< substitutable_series< t_substitutable_series< series< Cf, Key, polynomial< Cf, Key > >, polynomial< Cf, Key > >, polynomial< Cf, Key > >, polynomial< Cf, Key > > >, polynomial< Cf, Key > >::degree degree_type< T > degree() const
Total degree.
void push_back(std::future< T > &&f)
Move-insert a future.
Cf find_cf(std::initializer_list< T > l) const
Find coefficient.