/** @file integration_kernel.cpp * * Implementation of GiNaC's integration kernels for iterated integrals. */ /* * GiNaC Copyright (C) 1999-2020 Johannes Gutenberg University Mainz, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "integration_kernel.h" #include "add.h" #include "mul.h" #include "operators.h" #include "power.h" #include "relational.h" #include "symbol.h" #include "constant.h" #include "numeric.h" #include "function.h" #include "pseries.h" #include "utils.h" #include "inifcns.h" #include #include #include namespace GiNaC { // anonymous namespace for helper function namespace { /** * * Returns the Kronecker symbol in the case where * a: integer * n: unit or prime number * * If n is an odd prime number, the routine returns the Legendre symbol. * * If n is a unit (e.g n equals 1 or -1) or if n is an even prime number (the only case is n=2) * the routine returns the special values defined below. * * Ref.: Toshitsune Miyake, Modular Forms, Chapter 3.1 * */ numeric kronecker_symbol_prime(const numeric & a, const numeric & n) { if ( n == 1 ) { return 1; } else if ( n == -1 ) { if ( a >= 0 ) { return 1; } else { return -1; } } else if ( n == 2 ) { if ( GiNaC::smod(a,8) == 1 ) { return 1; } else if ( GiNaC::smod(a,8) == -1 ) { return 1; } else if ( GiNaC::smod(a,8) == 3 ) { return -1; } else if ( GiNaC::smod(a,8) == -3 ) { return -1; } else { return 0; } } // n is an odd prime number return GiNaC::smod( pow(a,(n-1)/2), n); } /** * * n: positive integer * * a: discriminant of a quadratic field, defines primitive character phi * b: discriminant of a quadratic field, defines primitive character psi * L=|a|: conductor of primitive character phi * M=|b|: conductor of primitive character psi * * k: modular weight * * This function computes * \f[ * \sum\limits_{d | n} \psi(d) \phi(n/d) d^{k-1} * \f] * * Ref.: Eq.(5.3.1), William A. Stein, Modular Forms, A computational Approach; * * Eq.(32), arXiv:1704.08895 * */ numeric divisor_function(const numeric & n, const numeric & a, const numeric & b, const numeric & k) { ex res = 0; for (numeric i1=1; i1<=n; i1++) { if ( irem(n,i1) == 0 ) { numeric ratio = n/i1; res += primitive_dirichlet_character(ratio,a) * primitive_dirichlet_character(i1,b) * pow(i1,k-1); } } return ex_to(res); } /** * * k: modular weight * * a: discriminant of a quadratic field, defines primitive character phi * b: discriminant of a quadratic field, defines primitive character psi * L=|a|: conductor of primitive character phi * M=|b|: conductor of primitive character psi * * This function computes the constant term of the q-expansion of an Eisenstein series. * * The coefficient is given by the equation below eq.(5.3.1) in William A. Stein, Modular Forms, A computational Approach; * * or by eq.(33), arXiv:1704.08895 * */ numeric coefficient_a0(const numeric & k, const numeric & a, const numeric & b) { ex conductor = abs(a); numeric a0; if ( conductor == 1 ) { a0 = -numeric(1,2)/k*generalised_Bernoulli_number(k,b); } else { a0 = 0; } return a0; } /** * * k: modular weight * q: exp(2 Pi I tau/M) * * a: discriminant of a quadratic field, defines primitive character phi * b: discriminant of a quadratic field, defines primitive character psi * L=|a|: conductor of primitive character phi * M=|b|: conductor of primitive character psi * * N: truncation order * * Returns the q-expansion of an Eisenstein to order N (the q^(N-1)-term is included, q^N is neglected). * * Ref.: Eq.(5.3.1), William A. Stein, Modular Forms, A computational Approach; * * Eq.(32), arXiv:1704.08895 * */ ex eisenstein_series(const numeric & k, const ex & q, const numeric & a, const numeric & b, const numeric & N) { ex res = coefficient_a0(k,a,b); for (numeric i1=1; i1(e) || is_a(e) ) { return series_to_poly(e.series(qbar,order)); } else { return e.map(*this); } } ex qbar; int order; }; /** * * \f[ * Li_{-n}(x), n>=0 * \f] * * This is a rational function in x. * * To speed things up, we cache it. * */ class Li_negative { // ctors public: Li_negative(); // non-virtual functions public: ex get_symbolic_value(int n, const ex & x_val); ex get_numerical_value(int n, const ex & x_val); // member variables : protected: static std::vector cache_vec; static symbol x; }; Li_negative::Li_negative() {} ex Li_negative::get_symbolic_value(int n, const ex & x_val) { int n_cache = cache_vec.size(); if ( n >= n_cache ) { for (int j=n_cache; j<=n; j++) { ex f; if ( j == 0 ) { f = x/(1-x); } else { f = normal( x*diff(cache_vec[j-1],x)); } cache_vec.push_back( f ); } } return cache_vec[n].subs(x==x_val); } ex Li_negative::get_numerical_value(int n, const ex & x_val) { symbol x_symb("x_symb"); ex f = this->get_symbolic_value(n,x_symb); ex res = f.subs(x_symb==x_val).evalf(); return res; } // initialise static data members std::vector Li_negative::cache_vec; symbol Li_negative::x = symbol("x"); } // end of anonymous namespace /** * * Returns the decomposition of the positive integer n into prime numbers * in the form * lst( lst(p1,...,pr), lst(a1,...,ar) ) * such that * n = p1^a1 ... pr^ar. * */ ex ifactor(const numeric & n) { if ( !n.is_pos_integer() ) throw (std::runtime_error("ifactor(): argument not a positive integer")); lst p_lst, exp_lst; // implementation for small integers numeric n_temp = n; for (numeric p=2; p<=n; p++) { if ( p.info(info_flags::prime) ) { numeric exp_temp = 0; while ( irem(n_temp, p) == 0 ) { n_temp = n_temp/p; exp_temp++; } if ( exp_temp>0 ) { p_lst.append(p); exp_lst.append(exp_temp); } } if ( n_temp == 1 ) break; } if ( n_temp != 1 ) throw (std::runtime_error("ifactor(): probabilistic primality test failed")); lst res = {p_lst,exp_lst}; return res; } /** * * Returns true if the integer n is either one or the discriminant of a quadratic number field. * * Returns false otherwise. * * Ref.: Toshitsune Miyake, Modular Forms, Chapter 3.1 * */ bool is_discriminant_of_quadratic_number_field(const numeric & n) { if ( n == 0 ) { return false; } if ( n == 1 ) { return true; } lst prime_factorisation = ex_to(ifactor(abs(n))); lst p_lst = ex_to(prime_factorisation.op(0)); lst e_lst = ex_to(prime_factorisation.op(1)); size_t n_primes = p_lst.nops(); if ( n_primes > 0 ) { // take the last prime numeric p = ex_to(p_lst.op(n_primes-1)); if ( p.is_odd() ) { if ( e_lst.op(n_primes-1) != 1 ) { return false; } numeric pstar = p; if ( mod(p,4) == 3 ) { pstar = -p; } return is_discriminant_of_quadratic_number_field(n/pstar); } } // power of two now if ( (n==-4) || (n==-8) || (n==8) || (n==-32) || (n==32) || (n==-64) || (n==128) ) { return true; } return false; } /** * * Returns the Kronecker symbol * a: integer * n: integer * * This routine defines * kronecker_symbol(1,0) = 1 * kronecker_symbol(-1,0) = 1 * kronecker_symbol(a,0) = 0, a != 1,-1 * * In particular * kronecker_symbol(-1,0) = 1 (in agreement with Sage) * * Ref.: Toshitsune Miyake, Modular Forms, Chapter 3.1 * */ numeric kronecker_symbol(const numeric & a, const numeric & n) { // case n=0 first, include kronecker_symbol(0,0)=0 if ( n == 0 ) { if ( (a == 1) || (a == -1) ) { return 1; } else { return 0; } } numeric unit = 1; numeric n_pos = n; if ( n_pos<0 ) { unit = -1; n_pos = -n; } ex res = kronecker_symbol_prime(a,unit); numeric n_odd = n_pos; numeric alpha = 0; while ( n_odd.is_even() ) { alpha++; n_odd = n_odd/2; } if ( alpha>0 ) { res *= pow(kronecker_symbol_prime(a,2),alpha); } lst temp_lst = ex_to(ifactor(n_odd)); lst prime_lst = ex_to(temp_lst.op(0)); lst expo_lst = ex_to(temp_lst.op(1)); for (auto it_p = prime_lst.begin(), it_e = expo_lst.begin(); it_p != prime_lst.end(); it_p++, it_e++) { res *= pow(kronecker_symbol_prime(a,ex_to(*it_p)),ex_to(*it_e)); } return ex_to(res); } /** * * Defines a primitive Dirichlet character through the Kronecker symbol. * * n: integer * a: discriminant of a quadratic field * |a|: conductor * * The character takes the values -1,0,1. * */ numeric primitive_dirichlet_character(const numeric & n, const numeric & a) { return kronecker_symbol(a,n); } /** * * Defines a Dirichlet character through the Kronecker symbol. * * n: integer * a: discriminant of a quadratic field * |a|: conductor * N: modulus, needs to be multiple of |a| * * The character takes the values -1,0,1. * */ numeric dirichlet_character(const numeric & n, const numeric & a, const numeric & N) { if ( gcd(n,N) == 1 ) { return primitive_dirichlet_character(n,a); } return 0; } /** * * The generalised Bernoulli number. * * k: index / modular weight * * b: discriminant of a quadratic field, defines primitive character psi * M=|b|: conductor of primitive character psi * * The generalised Bernoulli number is computed from the series expansion of the generating function. * The generating function is given in eq.(34), arXiv:1704.08895 * */ numeric generalised_Bernoulli_number(const numeric & k, const numeric & b) { int k_int = k.to_int(); symbol x("x"); numeric conductor = abs(b); ex gen_fct = 0; for (numeric i1=1; i1<=conductor; i1++) { gen_fct += primitive_dirichlet_character(i1,b) * x*exp(i1*x)/(exp(conductor*x)-1); } gen_fct = series_to_poly(gen_fct.series(x,k_int+1)); ex B = factorial(k) * gen_fct.coeff(x,k_int); return ex_to(B); } /** * * The Bernoulli polynomials * */ ex Bernoulli_polynomial(const numeric & k, const ex & x) { int k_int = k.to_int(); symbol t("t"); ex gen_fct = t*exp(x*t)/(exp(t)-1); gen_fct = series_to_poly(gen_fct.series(t,k_int+1)); ex B = factorial(k) * gen_fct.coeff(t,k_int); return B; } GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(integration_kernel, basic, print_func(&integration_kernel::do_print)) integration_kernel::integration_kernel() : inherited(), cache_step_size(100), series_vec() { } int integration_kernel::compare_same_type(const basic &other) const { return 0; } ex integration_kernel::series(const relational & r, int order, unsigned options) const { if ( r.rhs() != 0 ) { throw (std::runtime_error("integration_kernel::series: non-zero expansion point not implemented")); } return Laurent_series(r.lhs(),order); } /** * * This routine returns true, if the integration kernel has a trailing zero. * */ bool integration_kernel::has_trailing_zero(void) const { if ( cln::zerop( series_coeff(0) ) ) { return false; } return true; } /** * * This routine returns true, if the integration kernel can be evaluated numerically. * */ bool integration_kernel::is_numeric(void) const { return true; } /** * * Subclasses have either to implement series_coeff_impl * or the two methods Laurent_series and uses_Laurent_series. * * The method series_coeff_impl can be used, if a single coefficient can be computed * independently of the others. * * The method Laurent_series can be used, if it is more efficient to compute a Laurent series * in one shot and to determine a range of coefficients from this Laurent series. * */ cln::cl_N integration_kernel::series_coeff(int i) const { int n_vec = series_vec.size(); if ( i >= n_vec ) { int N = cache_step_size*(i/cache_step_size+1); if ( uses_Laurent_series() ) { symbol x("x"); // series_vec[0] gives coefficient of 1/z, series_vec[N-1] coefficient of z^(N-2), // thus expansion up to order (N-1) is required ex temp = Laurent_series(x, N-1); for (int j=n_vec; j(temp.coeff(x,j-1).evalf()).to_cl_N() ); } } else { for (int j=n_vec; j(lambda.evalf()).to_cl_N(); cln::cl_N pre_cln = ex_to(pre.evalf()).to_cl_N(); cln::cl_F one = cln::cl_float(1, cln::float_format(Digits)); cln::cl_N res = 0; cln::cl_N resbuf; cln::cl_N subexpr; if ( N_trunc == 0 ) { // sum until precision is reached bool flag_accidental_zero = false; int N = 0; do { resbuf = res; subexpr = series_coeff(N); res += pre_cln * subexpr * cln::expt(lambda_cln,N-1+shift); flag_accidental_zero = cln::zerop(subexpr); N++; } while ( (res != resbuf) || flag_accidental_zero ); } else { // N_trunc > 0, sum up the first N_trunc terms for (int N=0; N(&basic_log_kernel::do_print)) basic_log_kernel::basic_log_kernel() : inherited() { } int basic_log_kernel::compare_same_type(const basic &other) const { return 0; } cln::cl_N basic_log_kernel::series_coeff_impl(int i) const { if ( i == 0 ) { return 1; } return 0; } void basic_log_kernel::do_print(const print_context & c, unsigned level) const { c.s << "basic_log_kernel()"; } GINAC_BIND_UNARCHIVER(basic_log_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(multiple_polylog_kernel, integration_kernel, print_func(&multiple_polylog_kernel::do_print)) multiple_polylog_kernel::multiple_polylog_kernel() : inherited(), z(_ex1) { } multiple_polylog_kernel::multiple_polylog_kernel(const ex & arg_z) : inherited(), z(arg_z) { } int multiple_polylog_kernel::compare_same_type(const basic &other) const { const multiple_polylog_kernel &o = static_cast(other); return z.compare(o.z); } size_t multiple_polylog_kernel::nops() const { return 1; } ex multiple_polylog_kernel::op(size_t i) const { if ( i != 0 ) { throw(std::range_error("multiple_polylog_kernel::op(): out of range")); } return z; } ex & multiple_polylog_kernel::let_op(size_t i) { ensure_if_modifiable(); if ( i != 0 ) { throw(std::range_error("multiple_polylog_kernel::let_op(): out of range")); } return z; } bool multiple_polylog_kernel::is_numeric(void) const { return z.evalf().info(info_flags::numeric); } cln::cl_N multiple_polylog_kernel::series_coeff_impl(int i) const { if ( i == 0 ) { return 0; } return -cln::expt(ex_to(z.evalf()).to_cl_N(),-i); } void multiple_polylog_kernel::do_print(const print_context & c, unsigned level) const { c.s << "multiple_polylog_kernel("; z.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(multiple_polylog_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(ELi_kernel, integration_kernel, print_func(&ELi_kernel::do_print)) ELi_kernel::ELi_kernel() : inherited(), n(_ex0), m(_ex0), x(_ex0), y(_ex0) { } ELi_kernel::ELi_kernel(const ex & arg_n, const ex & arg_m, const ex & arg_x, const ex & arg_y) : inherited(), n(arg_n), m(arg_m), x(arg_x), y(arg_y) { } int ELi_kernel::compare_same_type(const basic &other) const { const ELi_kernel &o = static_cast(other); int cmpval; cmpval = n.compare(o.n); if ( cmpval) { return cmpval; } cmpval = m.compare(o.m); if ( cmpval) { return cmpval; } cmpval = x.compare(o.x); if ( cmpval) { return cmpval; } return y.compare(o.y); } size_t ELi_kernel::nops() const { return 4; } ex ELi_kernel::op(size_t i) const { switch (i) { case 0: return n; case 1: return m; case 2: return x; case 3: return y; default: throw (std::out_of_range("ELi_kernel::op() out of range")); } } ex & ELi_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return n; case 1: return m; case 2: return x; case 3: return y; default: throw (std::out_of_range("ELi_kernel::let_op() out of range")); } } bool ELi_kernel::is_numeric(void) const { return (n.info(info_flags::nonnegint) && m.info(info_flags::numeric) && x.evalf().info(info_flags::numeric) && y.evalf().info(info_flags::numeric)); } cln::cl_N ELi_kernel::series_coeff_impl(int i) const { if ( i == 0 ) { return 0; } int n_int = ex_to(n).to_int(); int m_int = ex_to(m).to_int(); cln::cl_N x_cln = ex_to(x.evalf()).to_cl_N(); cln::cl_N y_cln = ex_to(y.evalf()).to_cl_N(); cln::cl_N res_cln = 0; for (int j=1; j<=i; j++) { if ( (i % j) == 0 ) { int k = i/j; res_cln += cln::expt(x_cln,j)/cln::expt(cln::cl_I(j),n_int) * cln::expt(y_cln,k)/cln::expt(cln::cl_I(k),m_int); } } return res_cln; } /** * * Returns the value of ELi_{n,m}(x,y,qbar) * */ ex ELi_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { return get_numerical_value_impl(qbar, 1, 1, N_trunc); } void ELi_kernel::do_print(const print_context & c, unsigned level) const { c.s << "ELi_kernel("; n.print(c); c.s << ","; m.print(c); c.s << ","; x.print(c); c.s << ","; y.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(ELi_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(Ebar_kernel, integration_kernel, print_func(&Ebar_kernel::do_print)) Ebar_kernel::Ebar_kernel() : inherited(), n(_ex0), m(_ex0), x(_ex0), y(_ex0) { } Ebar_kernel::Ebar_kernel(const ex & arg_n, const ex & arg_m, const ex & arg_x, const ex & arg_y) : inherited(), n(arg_n), m(arg_m), x(arg_x), y(arg_y) { } int Ebar_kernel::compare_same_type(const basic &other) const { const Ebar_kernel &o = static_cast(other); int cmpval; cmpval = n.compare(o.n); if ( cmpval) { return cmpval; } cmpval = m.compare(o.m); if ( cmpval) { return cmpval; } cmpval = x.compare(o.x); if ( cmpval) { return cmpval; } return y.compare(o.y); } size_t Ebar_kernel::nops() const { return 4; } ex Ebar_kernel::op(size_t i) const { switch (i) { case 0: return n; case 1: return m; case 2: return x; case 3: return y; default: throw (std::out_of_range("Ebar_kernel::op() out of range")); } } ex & Ebar_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return n; case 1: return m; case 2: return x; case 3: return y; default: throw (std::out_of_range("Ebar_kernel::let_op() out of range")); } } bool Ebar_kernel::is_numeric(void) const { return (n.info(info_flags::nonnegint) && m.info(info_flags::numeric) && x.evalf().info(info_flags::numeric) && y.evalf().info(info_flags::numeric)); } cln::cl_N Ebar_kernel::series_coeff_impl(int i) const { if ( i == 0 ) { return 0; } int n_int = ex_to(n).to_int(); int m_int = ex_to(m).to_int(); cln::cl_N x_cln = ex_to(x.evalf()).to_cl_N(); cln::cl_N y_cln = ex_to(y.evalf()).to_cl_N(); cln::cl_N res_cln = 0; for (int j=1; j<=i; j++) { if ( (i % j) == 0 ) { int k = i/j; res_cln += (cln::expt(x_cln,j)*cln::expt(y_cln,k)-cln::expt(cln::cl_I(-1),n_int+m_int)*cln::expt(x_cln,-j)*cln::expt(y_cln,-k))/cln::expt(cln::cl_I(j),n_int)/cln::expt(cln::cl_I(k),m_int); } } return res_cln; } /** * * Returns the value of Ebar_{n,m}(x,y,qbar) * */ ex Ebar_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { return get_numerical_value_impl(qbar, 1, 1, N_trunc); } void Ebar_kernel::do_print(const print_context & c, unsigned level) const { c.s << "Ebar_kernel("; n.print(c); c.s << ","; m.print(c); c.s << ","; x.print(c); c.s << ","; y.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(Ebar_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(Kronecker_dtau_kernel, integration_kernel, print_func(&Kronecker_dtau_kernel::do_print)) Kronecker_dtau_kernel::Kronecker_dtau_kernel() : inherited(), n(_ex0), z(_ex0), K(_ex1), C_norm(_ex1) { } Kronecker_dtau_kernel::Kronecker_dtau_kernel(const ex & arg_n, const ex & arg_z, const ex & arg_K, const ex & arg_C_norm) : inherited(), n(arg_n), z(arg_z), K(arg_K), C_norm(arg_C_norm) { } int Kronecker_dtau_kernel::compare_same_type(const basic &other) const { const Kronecker_dtau_kernel &o = static_cast(other); int cmpval; cmpval = n.compare(o.n); if ( cmpval) { return cmpval; } cmpval = z.compare(o.z); if ( cmpval) { return cmpval; } cmpval = K.compare(o.K); if ( cmpval) { return cmpval; } return C_norm.compare(o.C_norm); } size_t Kronecker_dtau_kernel::nops() const { return 4; } ex Kronecker_dtau_kernel::op(size_t i) const { switch (i) { case 0: return n; case 1: return z; case 2: return K; case 3: return C_norm; default: throw (std::out_of_range("Kronecker_dtau_kernel::op() out of range")); } } ex & Kronecker_dtau_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return n; case 1: return z; case 2: return K; case 3: return C_norm; default: throw (std::out_of_range("Kronecker_dtau_kernel::let_op() out of range")); } } bool Kronecker_dtau_kernel::is_numeric(void) const { return (n.info(info_flags::nonnegint) && z.evalf().info(info_flags::numeric) && K.info(info_flags::posint) && C_norm.evalf().info(info_flags::numeric)); } cln::cl_N Kronecker_dtau_kernel::series_coeff_impl(int i) const { numeric n_num = ex_to(n); int n_int = n_num.to_int(); // case n=0 if ( n_num == 0 ) { if ( i == 0 ) { ex res = -C_norm*K; return ex_to(res.evalf()).to_cl_N(); } return 0; } // case n=1 if ( n_num == 1 ) { return 0; } // case n>1 if ( i == 0 ) { ex res = C_norm*K / factorial(n_num-2) * bernoulli(n_num)/n_num; return ex_to(res.evalf()).to_cl_N(); } // n>1, i>0 // if K>1 the variable i needs to be a multiple of K int K_int = ex_to(K).to_int(); if ( (i % K_int) != 0 ) { return 0; } int i_local = i/K_int; ex w = exp(ex_to((2*Pi*I*z).evalf())); cln::cl_N w_cln = ex_to(w).to_cl_N(); cln::cl_N res_cln = 0; for (int j=1; j<=i_local; j++) { if ( (i_local % j) == 0 ) { res_cln += (cln::expt(w_cln,j)+cln::expt(cln::cl_I(-1),n_int)*cln::expt(w_cln,-j)) * cln::expt(cln::cl_I(i_local/j),n_int-1); } } ex pre = -C_norm*K/factorial(n_num-2); return ex_to(pre.evalf()).to_cl_N() * res_cln; } /** * * Returns the value of the g^(n)(z,K*tau), where tau is given by qbar. * */ ex Kronecker_dtau_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { numeric n_num = ex_to(n); if ( n_num == 0 ) { return 1; } // use the direct formula here if ( n_num == 1 ) { ex wbar = exp(ex_to((2*Pi*I*z).evalf())); ex res = -2*Pi*I*( numeric(1,2)*(1+wbar)/(1-wbar) + Ebar_kernel(0,0,wbar,1).get_numerical_value(pow(qbar,K),N_trunc)); return ex_to(res.evalf()); } ex pre = pow(2*Pi*I,n_num)/C_norm/K/(n_num-1); return get_numerical_value_impl(qbar, pre, 1, N_trunc); } void Kronecker_dtau_kernel::do_print(const print_context & c, unsigned level) const { c.s << "Kronecker_dtau_kernel("; n.print(c); c.s << ","; z.print(c); c.s << ","; K.print(c); c.s << ","; C_norm.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(Kronecker_dtau_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(Kronecker_dz_kernel, integration_kernel, print_func(&Kronecker_dz_kernel::do_print)) Kronecker_dz_kernel::Kronecker_dz_kernel() : inherited(), n(_ex0), z_j(_ex0), tau(_ex0), K(_ex1), C_norm(_ex1) { } Kronecker_dz_kernel::Kronecker_dz_kernel(const ex & arg_n, const ex & arg_z_j, const ex & arg_tau, const ex & arg_K, const ex & arg_C_norm) : inherited(), n(arg_n), z_j(arg_z_j), tau(arg_tau), K(arg_K), C_norm(arg_C_norm) { } int Kronecker_dz_kernel::compare_same_type(const basic &other) const { const Kronecker_dz_kernel &o = static_cast(other); int cmpval; cmpval = n.compare(o.n); if ( cmpval) { return cmpval; } cmpval = z_j.compare(o.z_j); if ( cmpval) { return cmpval; } cmpval = tau.compare(o.tau); if ( cmpval) { return cmpval; } cmpval = K.compare(o.K); if ( cmpval) { return cmpval; } return C_norm.compare(o.C_norm); } size_t Kronecker_dz_kernel::nops() const { return 5; } ex Kronecker_dz_kernel::op(size_t i) const { switch (i) { case 0: return n; case 1: return z_j; case 2: return tau; case 3: return K; case 4: return C_norm; default: throw (std::out_of_range("Kronecker_dz_kernel::op() out of range")); } } ex & Kronecker_dz_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return n; case 1: return z_j; case 2: return tau; case 3: return K; case 4: return C_norm; default: throw (std::out_of_range("Kronecker_dz_kernel::let_op() out of range")); } } bool Kronecker_dz_kernel::is_numeric(void) const { return (n.info(info_flags::nonnegint) && z_j.evalf().info(info_flags::numeric) && tau.evalf().info(info_flags::numeric) && K.info(info_flags::posint) && C_norm.evalf().info(info_flags::numeric)); } cln::cl_N Kronecker_dz_kernel::series_coeff_impl(int i) const { numeric n_num = ex_to(n); int n_int = n_num.to_int(); ex w_j_inv = exp(ex_to((-2*Pi*I*z_j).evalf())); cln::cl_N w_j_inv_cln = ex_to(w_j_inv).to_cl_N(); ex qbar = exp(ex_to((2*Pi*I*K*tau).evalf())); // case n=0 if ( n_num == 0 ) { return 0; } // case n=1 if ( n_num == 1 ) { if ( i == 1 ) { return ex_to((C_norm * 2*Pi*I).evalf()).to_cl_N(); } return 0; } // case n=2 if ( n_num == 2 ) { if ( ex_to(z_j.evalf()).is_zero() ) { if ( i == 0 ) { return ex_to((C_norm).evalf()).to_cl_N(); } else if ( i == 1 ) { return 0; } else { ex res = -bernoulli(i)/numeric(i); if ( numeric(i).is_even() ) { Ebar_kernel Ebar = Ebar_kernel( 1-i, 0, numeric(1), numeric(1) ); res += Ebar.get_numerical_value(qbar); } res *= -pow(2*Pi*I,i)*C_norm/factorial(i-1); return ex_to(res.evalf()).to_cl_N(); } } else { // z_j is not zero if ( i == 0 ) { return 0; } else { Li_negative my_Li_negative; ex res = 0; if ( i == 1 ) { res = numeric(1,2); } Ebar_kernel Ebar = Ebar_kernel( 1-i, 0, w_j_inv, numeric(1) ); res += my_Li_negative.get_numerical_value(i-1,w_j_inv) + Ebar.get_numerical_value(qbar); res *= -pow(2*Pi*I,i)*C_norm/factorial(i-1); return ex_to(res.evalf()).to_cl_N(); } } } // case n>2 ex res = 0; if ( i == 1 ) { res += - bernoulli(n_num-1)/(n_num-1); } if ( i > 0 ) { if ( ex_to(z_j.evalf()).is_zero() ) { if ( (numeric(i)+n_num).is_even() ) { Ebar_kernel Ebar = Ebar_kernel( 1-i, 2-n_num, numeric(1), numeric(1) ); res += pow(2*Pi*I,i-1)/factorial(i-1) * Ebar.get_numerical_value(qbar); } } else { // z_j is not zero Ebar_kernel Ebar = Ebar_kernel( 1-i, 2-n_num, w_j_inv, numeric(1) ); res += pow(2*Pi*I,i-1)/factorial(i-1) * Ebar.get_numerical_value(qbar); } } res *= - C_norm * 2*Pi*I/factorial(n_num-2); return ex_to(res.evalf()).to_cl_N(); } /** * * Returns the value of the g^(n-1)(z-z_j,K*tau). * */ ex Kronecker_dz_kernel::get_numerical_value(const ex & z, int N_trunc) const { numeric n_num = ex_to(n); if ( n_num == 1 ) { return 1; } ex pre = pow(2*Pi*I,n-2)/C_norm; return get_numerical_value_impl(z, pre, 0, N_trunc); } void Kronecker_dz_kernel::do_print(const print_context & c, unsigned level) const { c.s << "Kronecker_dz_kernel("; n.print(c); c.s << ","; z_j.print(c); c.s << ","; tau.print(c); c.s << ","; K.print(c); c.s << ","; C_norm.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(Kronecker_dz_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(Eisenstein_kernel, integration_kernel, print_func(&Eisenstein_kernel::do_print)) Eisenstein_kernel::Eisenstein_kernel() : inherited(), k(_ex0), N(_ex0), a(_ex0), b(_ex0), K(_ex0), C_norm(_ex1) { } Eisenstein_kernel::Eisenstein_kernel(const ex & arg_k, const ex & arg_N, const ex & arg_a, const ex & arg_b, const ex & arg_K, const ex & arg_C_norm) : inherited(), k(arg_k), N(arg_N), a(arg_a), b(arg_b), K(arg_K), C_norm(arg_C_norm) { } int Eisenstein_kernel::compare_same_type(const basic &other) const { const Eisenstein_kernel &o = static_cast(other); int cmpval; cmpval = k.compare(o.k); if ( cmpval) { return cmpval; } cmpval = N.compare(o.N); if ( cmpval) { return cmpval; } cmpval = a.compare(o.a); if ( cmpval) { return cmpval; } cmpval = b.compare(o.b); if ( cmpval) { return cmpval; } cmpval = K.compare(o.K); if ( cmpval) { return cmpval; } return C_norm.compare(o.C_norm); } /** * * The series method for this class returns the qbar-expansion of the modular form, * without an additional factor of C_norm/qbar. * * This allows for easy use in the class modular_form_kernel. * */ ex Eisenstein_kernel::series(const relational & r, int order, unsigned options) const { if ( r.rhs() != 0 ) { throw (std::runtime_error("integration_kernel::series: non-zero expansion point not implemented")); } ex qbar = r.lhs(); ex res = q_expansion_modular_form(qbar, order); res = res.series(qbar,order); return res; } size_t Eisenstein_kernel::nops() const { return 6; } ex Eisenstein_kernel::op(size_t i) const { switch (i) { case 0: return k; case 1: return N; case 2: return a; case 3: return b; case 4: return K; case 5: return C_norm; default: throw (std::out_of_range("Eisenstein_kernel::op() out of range")); } } ex & Eisenstein_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return k; case 1: return N; case 2: return a; case 3: return b; case 4: return K; case 5: return C_norm; default: throw (std::out_of_range("Eisenstein_kernel::let_op() out of range")); } } bool Eisenstein_kernel::is_numeric(void) const { return (k.info(info_flags::nonnegint) && N.info(info_flags::posint) && a.info(info_flags::integer) && b.info(info_flags::integer) && K.info(info_flags::posint) && C_norm.evalf().info(info_flags::numeric)); } ex Eisenstein_kernel::Laurent_series(const ex & x, int order) const { ex res = C_norm * q_expansion_modular_form(x, order+1)/x; res = res.series(x,order); return res; } /** * * Returns the value of the modular form. * */ ex Eisenstein_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { ex pre = numeric(1)/C_norm; return get_numerical_value_impl(qbar, pre, 1, N_trunc); } bool Eisenstein_kernel::uses_Laurent_series() const { return true; } ex Eisenstein_kernel::q_expansion_modular_form(const ex & q, int order) const { numeric k_num = ex_to(k); numeric N_num = ex_to(N); numeric a_num = ex_to(a); numeric b_num = ex_to(b); numeric K_num = ex_to(K); if ( (k==2) && (a==1) && (b==1) ) { return B_eisenstein_series(q, N_num, K_num, order); } return E_eisenstein_series(q, k_num, N_num, a_num, b_num, K_num, order); } void Eisenstein_kernel::do_print(const print_context & c, unsigned level) const { c.s << "Eisenstein_kernel("; k.print(c); c.s << ","; N.print(c); c.s << ","; a.print(c); c.s << ","; b.print(c); c.s << ","; K.print(c); c.s << ","; C_norm.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(Eisenstein_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(Eisenstein_h_kernel, integration_kernel, print_func(&Eisenstein_h_kernel::do_print)) Eisenstein_h_kernel::Eisenstein_h_kernel() : inherited(), k(_ex0), N(_ex0), r(_ex0), s(_ex0), C_norm(_ex1) { } Eisenstein_h_kernel::Eisenstein_h_kernel(const ex & arg_k, const ex & arg_N, const ex & arg_r, const ex & arg_s, const ex & arg_C_norm) : inherited(), k(arg_k), N(arg_N), r(arg_r), s(arg_s), C_norm(arg_C_norm) { } int Eisenstein_h_kernel::compare_same_type(const basic &other) const { const Eisenstein_h_kernel &o = static_cast(other); int cmpval; cmpval = k.compare(o.k); if ( cmpval) { return cmpval; } cmpval = N.compare(o.N); if ( cmpval) { return cmpval; } cmpval = r.compare(o.r); if ( cmpval) { return cmpval; } cmpval = s.compare(o.s); if ( cmpval) { return cmpval; } return C_norm.compare(o.C_norm); } /** * * The series method for this class returns the qbar-expansion of the modular form, * without an additional factor of C_norm/qbar. * * This allows for easy use in the class modular_form_kernel. * */ ex Eisenstein_h_kernel::series(const relational & r, int order, unsigned options) const { if ( r.rhs() != 0 ) { throw (std::runtime_error("integration_kernel::series: non-zero expansion point not implemented")); } ex qbar = r.lhs(); ex res = q_expansion_modular_form(qbar, order); res = res.series(qbar,order); return res; } size_t Eisenstein_h_kernel::nops() const { return 5; } ex Eisenstein_h_kernel::op(size_t i) const { switch (i) { case 0: return k; case 1: return N; case 2: return r; case 3: return s; case 4: return C_norm; default: throw (std::out_of_range("Eisenstein_h_kernel::op() out of range")); } } ex & Eisenstein_h_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return k; case 1: return N; case 2: return r; case 3: return s; case 4: return C_norm; default: throw (std::out_of_range("Eisenstein_h_kernel::let_op() out of range")); } } bool Eisenstein_h_kernel::is_numeric(void) const { return (k.info(info_flags::nonnegint) && N.info(info_flags::posint) && r.info(info_flags::integer) && s.info(info_flags::integer) && C_norm.evalf().info(info_flags::numeric)); } ex Eisenstein_h_kernel::Laurent_series(const ex & x, int order) const { ex res = C_norm * q_expansion_modular_form(x, order+1)/x; res = res.series(x,order); return res; } /** * * Returns the value of the modular form. * */ ex Eisenstein_h_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { ex pre = numeric(1)/C_norm; return get_numerical_value_impl(qbar, pre, 1, N_trunc); } bool Eisenstein_h_kernel::uses_Laurent_series() const { return true; } /** * * The constant coefficient in the Fourier expansion. * */ ex Eisenstein_h_kernel::coefficient_a0(const numeric & k, const numeric & r, const numeric & s, const numeric & N) const { if ( k == 1 ) { if ( irem(s,N) != 0 ) { return numeric(1,4) - mod(s,N)/numeric(2)/N; } else if ( (irem(r,N)==0) && (irem(s,N)==0) ) { return 0; } else { return I*numeric(1,4)*cos(Pi*mod(r,N)/N)/sin(Pi*mod(r,N)/N); } } // case k > 1 return -Bernoulli_polynomial(k,mod(s,N)/N)/numeric(2)/k; } /** * * The higher coefficients in the Fourier expansion. * */ ex Eisenstein_h_kernel::coefficient_an(const numeric & n, const numeric & k, const numeric & r, const numeric & s, const numeric & N) const { ex res = 0; for (numeric m=1; m<=n; m++) { if ( irem(n,m) == 0 ) { for (numeric c1=0; c1(k); numeric r_num = ex_to(r); numeric s_num = ex_to(s); numeric N_num = ex_to(N); ex res = coefficient_a0(k_num,r_num,s_num,N_num); for (numeric i1=1; i1(&modular_form_kernel::do_print)) modular_form_kernel::modular_form_kernel() : inherited(), k(_ex0), P(_ex0), C_norm(_ex1) { } modular_form_kernel::modular_form_kernel(const ex & arg_k, const ex & arg_P, const ex & arg_C_norm) : inherited(), k(arg_k), P(arg_P), C_norm(arg_C_norm) { } int modular_form_kernel::compare_same_type(const basic &other) const { const modular_form_kernel &o = static_cast(other); int cmpval; cmpval = k.compare(o.k); if ( cmpval) { return cmpval; } cmpval = P.compare(o.P); if ( cmpval) { return cmpval; } return C_norm.compare(o.C_norm); } /** * * The series method for this class returns the qbar-expansion of the modular form, * without an additional factor of C_norm/qbar. * */ ex modular_form_kernel::series(const relational & r, int order, unsigned options) const { if ( r.rhs() != 0 ) { throw (std::runtime_error("integration_kernel::series: non-zero expansion point not implemented")); } ex qbar = r.lhs(); subs_q_expansion do_subs_q_expansion(qbar, order); ex res = do_subs_q_expansion(P).series(qbar,order); return res; } size_t modular_form_kernel::nops() const { return 3; } ex modular_form_kernel::op(size_t i) const { switch (i) { case 0: return k; case 1: return P; case 2: return C_norm; default: throw (std::out_of_range("modular_form_kernel::op() out of range")); } } ex & modular_form_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return k; case 1: return P; case 2: return C_norm; default: throw (std::out_of_range("modular_form_kernel::let_op() out of range")); } } bool modular_form_kernel::is_numeric(void) const { bool flag = (k.info(info_flags::nonnegint) && C_norm.evalf().info(info_flags::numeric)); if ( !flag ) { return false; } symbol qbar("qbar"); // test with a random number and random expansion return series_to_poly(P.series(qbar,18)).subs(qbar==numeric(1,937)).evalf().info(info_flags::numeric); } ex modular_form_kernel::Laurent_series(const ex & qbar, int order) const { ex res = series_to_poly(P.series(qbar,order+1)); res = C_norm * res/qbar; res = res.series(qbar,order); return res; } /** * * Returns the value of the modular form. * */ ex modular_form_kernel::get_numerical_value(const ex & qbar, int N_trunc) const { ex pre = numeric(1)/C_norm; return get_numerical_value_impl(qbar, pre, 1, N_trunc); } bool modular_form_kernel::uses_Laurent_series() const { return true; } ex modular_form_kernel::q_expansion_modular_form(const ex & q, int N_order) const { return this->series(q==0,N_order); } void modular_form_kernel::do_print(const print_context & c, unsigned level) const { c.s << "modular_form_kernel("; k.print(c); c.s << ","; P.print(c); c.s << ","; C_norm.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(modular_form_kernel); GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(user_defined_kernel, integration_kernel, print_func(&user_defined_kernel::do_print)) user_defined_kernel::user_defined_kernel() : inherited(), f(_ex0), x(_ex0) { } user_defined_kernel::user_defined_kernel(const ex & arg_f, const ex & arg_x) : inherited(), f(arg_f), x(arg_x) { } int user_defined_kernel::compare_same_type(const basic &other) const { const user_defined_kernel &o = static_cast(other); int cmpval; cmpval = f.compare(o.f); if ( cmpval) { return cmpval; } return x.compare(o.x); } size_t user_defined_kernel::nops() const { return 2; } ex user_defined_kernel::op(size_t i) const { switch (i) { case 0: return f; case 1: return x; default: throw (std::out_of_range("user_defined_kernel::op() out of range")); } } ex & user_defined_kernel::let_op(size_t i) { ensure_if_modifiable(); switch (i) { case 0: return f; case 1: return x; default: throw (std::out_of_range("user_defined_kernel::let_op() out of range")); } } bool user_defined_kernel::is_numeric(void) const { // test with a random number return f.subs(x==numeric(1,937)).evalf().info(info_flags::numeric); } ex user_defined_kernel::Laurent_series(const ex & x_up, int order) const { ex res = f.series(x,order).subs(x==x_up); return res; } bool user_defined_kernel::uses_Laurent_series() const { return true; } void user_defined_kernel::do_print(const print_context & c, unsigned level) const { c.s << "user_defined_kernel("; f.print(c); c.s << ","; x.print(c); c.s << ")"; } GINAC_BIND_UNARCHIVER(user_defined_kernel); } // namespace GiNaC