| 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