Thomas Baruchel on Fri, 04 Nov 2016 10:22:49 +0100


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

A function for inverting power series


Hi,

first of all, this is my first post on pari-dev. I would be happy to
share with you a function that I wrote after having found a (new ?)
algorithm for inverting a power series.

Before that, maybe a misprint in the PDF userguide for the library: The section 11.4.5.5 tells "prints the error message : s." but is 's' the right thing to put here; the variable 's' doesn't occur in the prototype above.

I posted here:

http://mathoverflow.net/questions/253904/a-fast-and-possibly-new-algorithm-for-performing-the-division-of-large-numbers

about my idea and you will notice that some examples of code are provided,
among which a C-function for the pari library.

Maybe it will be more convenient to compile a standalone file containing
some benchmark tests. The remaining of my message is a piece of C code
with a 'main' function. Could you compile it and see if it would be worth
adding the function ser_inv2 (since 'ser_inv' already exists). This function
behaves not too bad for inverting power series with a small number of terms,
most favorable cases being small powers of 2 like 8, 16, 32.x;

Best regards, th. baruchel.


/* to be compiled with    gcc test.c -lpari       */

#include <pari/pari.h>
#include <time.h>

/*
 * Beginning of the function is hacked from div_ser in src/basemath/gen1.c
 */
static GEN
inv_ser2(GEN y)
{
  long i, j, n, l = -valp(y), ly = lg(y);
  GEN y_lead, p1, p2, z;
  long vy = varn(y);
  long k = 1;
  while(k<ly-2) k <<= 1;
  k >>= 1;

  if (!signe(y)) pari_err_INV("inv_ser2", y);

  pari_sp av_base = avma;

  y_lead = gel(y,2);
  if (gequal0(y_lead)) /* normalize denominator if leading term is 0 */
  {
    pari_warn(warner,"normalizing a series with 0 leading term");
    for (l--, ly--,y++; ly > 2; l--, ly--, y++)
    {
      y_lead = gel(y,2);
      if (!gequal0(y_lead)) break;
    }
    if (ly <= 2) pari_err_INV("inv_ser2", y);
  }

  z = cgetg(ly,t_SER);
  z[1] = evalvalp(l) | evalvarn(vy) | evalsigne(1);
  gel(z,2) = gdiv(gen_1, y_lead);

  p2 = cgetg(ly, t_VECSMALL);
  for (i=3; i<ly; i++)
  {
    p1 = gel(y,i);
    if (isrationalzero(p1)) p1 = NULL;
    gel(p2,i) = p1;
  }

  for (n=1; n < ly-2;) {
    /* use uncomputed coefficients in z as a temporary "buffer" */
    pari_sp av0 = avma;
    for (i=2; i<n+2; i++) {
      p1 = gen_0;
      for (j=2, l=n+i; j<n+2; j++, l--) {
        if (p2[l]) p1 = gsub(p1, gmul(gel(z,j), gel(p2, l)));
      }
      gel(z, n+i) = p1;
    }
    pari_sp av1 = avma;
    for (i=n+1; i>1; i--) {
      p1 = gen_0;
      for (j=2, l=n+i; j<i+1; j++, l--) {
        p1 = gadd(p1, gmul(gel(z,j), gel(z,l)));
      }
      gel(z, n+i) = p1;
    }
    stackdummy(av0, av1);
    /* compute an additional term if needed */
    n <<= 1; k >>= 1;
    if(k&(ly-2)) {
      i = (++n)+1; p1 = gen_0;
      for (j=2, l=i; j<i; j++, l--)
        if (p2[l]) p1 = gsub(p1, gmul(gel(z,j), gel(p2,l)));
      gel(z,i) = gdiv(p1, y_lead);
    }
  }
  return gerepileupto(av_base, normalize(z));
}

int main(void) {
    int i, j, n;
    clock_t start;
    static GEN t;
    static GEN a;

    pari_init(50000000,1000);

    t = gp_read_str("2/3+2*x+3*x^2+4*x^3 + O(x^8)");
    pari_printf("\nReciprocal of %Ps\n", t);

    a = ginv(t); pari_printf("(ginv) %Ps\n", a); cgiv(a);
    a = inv_ser2(t); pari_printf("(test) %Ps\n", a); cgiv(a);


    t = gp_read_str("2/3+2*x+3*x^2+4*x^3 + O(x^7)");
    pari_printf("\nReciprocal of %Ps\n", t);

    a = ginv(t); pari_printf("(ginv) %Ps\n", a); cgiv(a);
    a = inv_ser2(t); pari_printf("(test) %Ps\n", a); cgiv(a);


    /* Benchmark (1) */
    t = gp_read_str("2/3+2*x+3*x^2+4*x^3");

    pari_sp av = avma;
    for(n=8; n<=32; n<<=1) {
      a = gerepileupto(av, gadd(t, ggrando(pol_x(varn(t)), n)));
      pari_printf("\nTime comparison for %Ps\n", a);

      printf("Using ginv from libpari...");
      start = clock(); for (i=0;i<10000;i++) { cgiv(ginv(a)); }
      printf(" %f millisec.\n", ((double) (clock()-start))*1000/CLOCKS_PER_SEC);

      printf("Using inv_ser2...         ");
      start = clock(); for (i=0;i<10000;i++) { cgiv(inv_ser2(a)); }
      printf(" %f millisec.\n", ((double) (clock()-start))*1000/CLOCKS_PER_SEC);
    }

    /* Benchmark (2) */

    for(n=24; n<=36; n += 12) {
      a = gerepileupto(av, gadd(t, ggrando(pol_x(varn(t)), n)));
      pari_printf("\nTime comparison for %Ps\n", a);

      printf("Using ginv from libpari...");
      start = clock(); for (i=0;i<10000;i++) { cgiv(ginv(a)); }
      printf(" %f millisec.\n", ((double) (clock()-start))*1000/CLOCKS_PER_SEC);

      printf("Using inv_ser2...         ");
      start = clock(); for (i=0;i<10000;i++) { cgiv(inv_ser2(a)); }
      printf(" %f millisec.\n", ((double) (clock()-start))*1000/CLOCKS_PER_SEC);
    }
}