Jean-Marc Sac-Epée on Sat, 04 Nov 2000 03:57:24 +0100


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

Re: Integrating functions


Karim BELABAS a écrit :

> [Jean-Marc Sac-Epée:]
> > Thanks to your help, I know now how to integrate a function  using
> > intnum0:
> >
> > > #include <math.h>
> > > #include <stdio.h>
> > > #include <pari.h>
> > >
> > > GEN integrate(char *f, char *var, GEN a, GEN b, long prec)
> > > {
> > >   entree *ep_var = is_entry(var);
> > >   return intnum0(ep_var, a,b, f, 0,prec);
> > > }
> > >
> > > int
> > > main()
> > > {
> > >   pari_init(1000000,2);
> > >   output(integrate("x^2+3", "x", gzero, gun, DEFAULTPREC));
> > >   return 0;
> > > }
> >
> > Now, I would like to know if it is also possible with more complicated
> > fonctions. For example, it is easy to prove that for each t>0, the
> > polynomial 10*x^5 + 0.1*x^3 - t*x^2 - t has a unique real root. So, we
> > can define the fonction f: t -> this real root.
> >
> > Under gp, it is easy to integrate this function;
> > ? f(t)=real(polroots(10*x^5+0.1*x^3-t*x^2-t)[1])
> > ? intnum(t=0,1,f(t))
> >
> > In a C program, I can define this fonction for example with
> >
> > GEN f(GEN t)
> > {
> >  const long var = 0;
> >  const long prec = 4;
> >  GEN pol;
> >  pol = cgetg(deg+3,t_POL);
> >  pol[1] = evalsigne(1) | evalvarn(var) | evallgef(deg+3);
> >  pol[deg+2]=dbltor(10); pol[deg]=dbltor(0.1);
> >  pol[deg+1]=pol[deg-2]=gzero;
> >  pol[deg-1]=pol[deg-3]=gneg(t);
> >  return(greal(roots(pol,prec)[1]));
> > }
>
> I'd rather use
>
> GEN f(GEN t)
> {
>   const long prec = DEFAULTPREC; /* 64bit portable */
>   const long var = 0;
>   const long deg = 5;
>   GEN *pol, x = cgetg(deg+3,t_POL);
>
>   pol[1] = evalsigne(1) | evalvarn(var) | evallgef(deg+3);
>   pol = (GEN*)(x+2); /* strip codewords, avoid cast problems */
>   pol[0] = gneg(t);
>   pol[1] = gzero;
>   pol[2] = pol[0];
>   pol[3] = dbltor(0.1);
>   pol[4] = gzero;
>   pol[5] = dbltor(10.);
>   return greal(roots(x,prec)[1]);
> }
>
> [possibly not using the (GEN*) cast, but definitely stripping the codewords
> so as to be able to identify the coefficient associated to a given degree
> without mentally translating...]
>
> > But now, what is the correct syntax to integrate f?
>
> Two different ways, both of them kludges.
>
> 1) [not generic but works for your example...]
>   f = "real(polroots(10*x^5+0.1*x^3-t*x^2-t)[1])";
>   ep_var = is_entry("t");
>
> 2) [not recommended: you will need to link gp.o to your binary for that one...]
>   gpinstall(lib_f, "G" , "f", mylib.so);
>   f = "f(t)";
>   ep_var = is_entry("t");
>
> \begin{digression}
>
> In my opinion, it would be a _much_ better idea to copy and slightly modify
> the underlying intnum code (src/language/sumiter.c:qromb() in the above
> example) so as to replace the 'entree *ep' and 'char *ch' parameter with a C
> pointer to function GEN (*f)(GEN). Then all groups of the form
>
>    ep->value = (void*)x;   /* or push_val(ep, x) */
>    y = lisexpr(ch);
>
> can be replaced by
>
>     y = f(x);
>
> all other references to ep can be junked.
>
> This is straightforward (40 lines, about 2 minutes work), yields a routine
> which is easier to use, and should be more efficient [no parsing of GP
> expressions]. (in fact, I've just done it, see P.S below).
>
> \begin{technical}
> A generic, hence better, approach would be to use two parameters 'f' and
> 'data' (to avoid using global variables to properly define an 'f' needing
> external data)
>
>     y = f(x, data);
>
> 'f' being assumed to know how to use 'data' (possibly ignoring it).
>
> In fact all PARI functions using 'entree' arguments should have been written
> in this way, so as to be easy to integrate to library code. The GP version
> would have then been trivial to code using as 'f'
>
> GEN
> eval_GP_fun(GEN x, void *data)
> {
>   Ep(data)->value = (void*)x;
>   return lisexpr(Ch(data));
> }
> [with suitably defined macros Ep and Ch accessing the relevant fields of
> data, after proper casts, etc.]
>
> Of course, if needed, data could include pre/postprocessing, etc. Such a
> scheme is actually already used by a number of internal library functions,
> e.g fincke_pohst [ finds small vectors satisfying "certain conditions" in
> lattices using a generic Fincke-Pohst algorithm. The polredabs and qfminim
> functions use it for instance ]
>
> Since the stable version 2.1 is forthcoming, I'll probably rewrite everything
> in this style for version 2.2.0 (the next alpha...)  [should be an hour work
> now: there are MANY such functions]
> \end{technical}
> \end{digression}
>
> Hope this helps,
>
>     Karim.
>
> ===========================================================================
>
> P.S: In fact, I just wasted the necessary 2 minutes (was about 40 seconds in
> fact) adapting qromb [it's not generic, but you'll adapt it...].
> Now
>
>   integrate(f, stoi(1), stoi(2), DEFAULTPREC)
>
> works without any kludge nor intervention from the parser code. It's a
> shameless copy/paste job, I've not re-indented, or cleaned up the code in
> any way:
>
> #define JMAX 25
> #define JMAXP JMAX+3
> #define KLOC 4
>
> /* we need to make a copy in any case, cf forpari */
> static GEN
> fix(GEN a, long prec)
> {
>   GEN p = cgetr(prec);
>   gaffect(a,p); return p;
> }
>
> GEN
> integrate(GEN (*f)(GEN), GEN a, GEN b, long prec)
> {
>   GEN ss,dss,s,h,p1,p2,qlint,del,x,sum;
>   long av = avma, av1, tetpil,j,j1,j2,lim,it,sig;
>
>   a = fix(a,prec);
>   b = fix(b,prec);
>   qlint=subrr(b,a); sig=signe(qlint);
>   if (!sig)  { avma=av; return gzero; }
>   if (sig<0) { setsigne(qlint,1); s=a; a=b; b=s; }
>
>   s=new_chunk(JMAXP);
>   h=new_chunk(JMAXP);
>   h[0] = (long)realun(prec);
>
>   p1=f(a); if (p1 == a) p1=rcopy(p1);
>   p2=f(b);
>   s[0]=lmul2n(gmul(qlint,gadd(p1,p2)),-1);
>   for (it=1,j=1; j<JMAX; j++,it=it<<1)
>   {
>     h[j]=lshiftr((GEN)h[j-1],-2);
>     av1=avma; del=divrs(qlint,it); x=addrr(a,shiftr(del,-1));
>     for (sum=gzero,j1=1; j1<=it; j1++,x=addrr(x,del))
>     {
>       sum=gadd(sum,f(x));
>     }
>     sum = gmul(sum,del); p1 = gadd((GEN)s[j-1],sum);
>     tetpil = avma;
>     s[j]=lpile(av1,tetpil,gmul2n(p1,-1));
>
>     if (j>=KLOC)
>     {
>       tetpil=avma; ss=polint_i(h+j-KLOC,s+j-KLOC,gzero,KLOC+1,&dss);
>       j1=gexpo(ss); j2=gexpo(dss); lim=bit_accuracy(prec)-j-6;
>       if (j1-j2 > lim || j1 < -lim)
>       {
>         if (gcmp0(gimag(ss))) ss=greal(ss);
>         tetpil=avma; return gerepile(av,tetpil,gmulsg(sig,ss));
>       }
>       avma=tetpil;
>     }
>   }
>   err(intger2); return NULL; /* not reached */
> }
> --
> Karim Belabas                    email: Karim.Belabas@math.u-psud.fr
> Dep. de Mathematiques, Bat. 425
> Universite Paris-Sud             Tel: (00 33) 1 69 15 57 48
> F-91405 Orsay (France)           Fax: (00 33) 1 69 15 60 19
> --
> PARI/GP Home Page: http://www.parigp-home.de/

Thank you very much, Karim, for this very detailed answer.

J.-Marc
begin:vcard 
n:Sac-Epée;Jean-Marc 
tel;fax:03 87 31 52 73
tel;work:03 87 54 72 69
x-mozilla-html:FALSE
url:http://www.mmas.univ-metz.fr/~jmse
org:Université de Metz;Mathématiques
adr:;;;;;;
version:2.1
email;internet:jmse@poncelet.univ-metz.fr
title:Ingénieur de Recherches
x-mozilla-cpt:;0
fn:Jean-Marc Sac-Epée
end:vcard