(* ::Package:: *) BeginPackage["Landen`"]; Print["Landen integration package by Dante Manna, Luis Medina, Victor H. Moll, and Armin Straub accompanying the paper \"A new numerical method for the integration of rational functions\" -- Tulane University -- Version 1.0.0 (2009/02/13)"]; NLandenIntegrate::usage= "NLandenIntegrate[f] numerically computes the definite integral of the rational function f using Landen transformations. The following options are available: MethodOrder - order of the Landen transformations (defaults to 2) WorkingPrecision - the precision used for the Landen iterations (defaults to MachinePrecision) PrecisionGoal - the precision that the method tries to achieve for the integral (defaults to half the WorkingPrecision) MaxIterations - maximal number of Landen iterations to be performed before giving up (defaults to $RecursionLimit) StepMonitor - a function that is called after each Landen iteration with the current rational function as a parameter (as a list of coefficients suitable for RatFromCoeffs) Usage: NLandenIntegrate[1/(1+2x+3x^2)] NLandenIntegrate[1/(1+2x+3x^2),PrecisionGoal\[Rule]40] NLandenIntegrate[1/(1+2x+3x^2),MethodOrder\[Rule]3,StepMonitor\[Rule](Print[RatFromCoeffs[#,x]]&),MaxIterations\[Rule]3,WorkingPrecision\[Rule]Infinity]"; GenerateLandenTransforms::usage= "GenerateLandenTransforms[p] generates the Landen transformations of degree up to p. The following options are available: MethodOrder - order of the Landen transformations (defaults to 2) FunctionName - the Landen transformations will be available under this name (defaults to LandenStep) FileName - If specified the generated Landen transformations (including existing ones of the same name) will be saved to this file Usage: GenerateLandenTransforms[12] GenerateLandenTransforms[8,MethodOrder->3] GenerateLandenTransforms[FileName\[Rule]\"LandenTransforms.m\"]"; GenerateLandenTransform::usage= "GenerateLandenTransform[p,m] generates a Landen transformation for rational functions B/A where p is the degree of A (B is of degree p-2), and m is the order of the transformation. Usage: f=GenerateLandenTransform[3,2]; f[{{b0,b1},{a0,a1,a2,a3}}]"; RatFromCoeffs::usage= "RatFromCoeffs[{Ln,Ld},x] returns the rational function f=A/B in x such that Ln are the coefficients of A, and Ld are the coefficients of B each list starting with the highest order coefficient. Usage: RatFromCoeffs[{{1},{3,2,1}},x]"; CoeffsFromRat::usage= "CoeffsFromRat[f] takes a rational function f=A/B and returns a list {Ln, Ld} where Ln are the coefficients of A, and Ld are the coefficients of B each list starting with the highest order coefficient. The following options are available: Variable - to specify which variable f is to be considered a rational function in (defaults to Automatic) LandenFormat - if set to True the lists Ln,Ld will be left-padded with zeroes so that Length[Ln]+2==Length[Ld] (defaults to False) Usage: CoeffsFromRat[1/(1+2x+3x^2)] CoeffsFromRat[1/(1+a x+3x^2),Variable\[Rule]x] CoeffsFromRat[1/(1+2x+3x^2+x^3),LandenFormat\[Rule]True]"; Begin["Private`"]; (* ::Subsection:: *) (*Landen Integration*) Options[NLandenIntegrate]={ WorkingPrecision->Automatic, PrecisionGoal->Automatic, MaxIterations:>$RecursionLimit, MethodOrder->2, StepMonitor->(None&)}; NLandenIntegrate::largenumerator="The integral does not exist because the numerator is too large."; NLandenIntegrate::lowprec="The precision dropped below the precision goal of `1`."; NLandenIntegrate::noconvergence="No convergence observed after `1` iterations."; NLandenIntegrate::infiniteprecision="You may want to set PrecisionGoal to something less than Infinity or decrease MaxIterations."; NLandenIntegrate::nolandentransform="The Landen transformation of degree `1` and order `2` is not available. You may use GenerateLandenTransforms[`1`,MethodOrder->`2`] to generate it."; NLandenIntegrate[rat_,OptionsPattern[]]:=Module[{p,B,A,B1,A1,AC,c,c1,T,prec,precg}, (* Set up precision parameters. *) prec=OptionValue[WorkingPrecision]; precg=OptionValue[PrecisionGoal]; If[precg===Automatic, If[prec===Automatic,prec=MachinePrecision];precg=prec/2, If[prec===Automatic,prec=2precg]]; If[precg===Infinity&&OptionValue[MaxIterations]===$RecursionLimit, Message[NLandenIntegrate::infiniteprecision]]; (* Convert rational function to list of coefficients. *) If[MatchQ[rat,{_,_}], {B,A}=rat, {B,A}=CoeffsFromRat[rat]; If[Head[A]===Symbol,Return[$Failed]]]; (* Numerator has to have degree at least two less. *) If[Length[B]+2>Length[A], Message[NLandenIntegrate::largenumerator]; Return[Indeterminate]]; B=Join[Table[0,{Length[A]-Length[B]-2}],B]; (* Set up the candidates that the denominator may converge to. *) p=Length[A]-1; If[EvenQ[p]&&FreeQ[A,_Complex], AC=Module[{x},{Reverse[CoefficientList[(1+x^2)^(p/2),x]]}], AC=Module[{x},Table[Reverse[CoefficientList[(x-I)^q (x+I)^(p-q),x]],{q,0,p}]]]; (* Use working precision. *) {B,A}=SetPrecision[{B,A},prec]; {B1,A1,c1}={B,A,1}; (* Pick the Landen transformation to use. *) T=Global`LandenStep[#,OptionValue[MethodOrder]]&; Catch[Do[ {B,A,c}={B1,A1,c1}; With[{BA1=T[{B,A}]}, If[!ListQ[BA1], Message[NLandenIntegrate::nolandentransform,p,OptionValue[MethodOrder]]; Throw[$Failed]]; {B1,A1}=BA1]; (* Normalize so that the denominator has leading coefficient 1. *) If[A1[[1]]!=0, {B1,A1}={B1,A1}/A1[[1]]; c1=B1[[1]]/A1[[1]], c1=Indeterminate]; (* Tell the outside world about our progress. *) OptionValue[StepMonitor][{B1,A1}]; (* Celebrate if error estimates drop below precision goal. *) If[RelError[c1,c]<=10^(-precg)&&Min[Table[RelErrorList[A1,C],{C,AC}]]<=10^(-precg), Throw[c1 Pi]]; (* Or go home if current precision is hopeless. *) If[Precision[A1[[1]]]Automatic, LandenFormat->False}; CoeffsFromRat::notrat="`1` is not a rational function in one variable."; CoeffsFromRat[f_,OptionsPattern[]]:=Module[{vars,x,n,d,Ln,Ld,p}, (* Determine variable. *) If[OptionValue[Variable]===Automatic, vars=Variables[f]; If[Length[vars]==1&&Head[First[vars]]===Symbol, x=First[vars], Message[CoeffsFromRat::notrat,f]; Return[$Failed]], x=OptionValue[Variable]]; (* Get coefficients. *) n=Numerator[f]; d=Denominator[f]; If[PolynomialQ[n,x]&&PolynomialQ[d,x], {Ln,Ld}=Reverse/@{CoefficientList[n,x],CoefficientList[d,x]}; If[OptionValue[LandenFormat]===True, p=Max[Length[Ld],Length[Ln]+2]; Ln=Join[Table[0,{p-2-Length[Ln]}],Ln]; Ld=Join[Table[0,{p-Length[Ld]}],Ld]; ]; Return[{Ln,Ld}]; ]; Message[CoeffsFromRat::notrat,f]; $Failed ]; (* ::Subsection:: *) (*Landen Transform Generator*) GenerateLandenTransform[p_,m_]:=Module[{P,Q,A,B,A1,B1,F,E1,Z,C1,T,M1,M2,z,x,a,b, s=m*p-2,\[Nu]=Floor[p/2],\[Lambda]=Floor[(m*p-2)/2]}, (* R=P/Q such that R (cot (\[Phi]))==cot (m \[Phi])*) P=Sum[(-1)^i*Binomial[m,2*i]*z^(m-2*i),{i,0,Floor[m/2]}]; Q=Sum[(-1)^i*Binomial[m,2*i+1]*z^(m-1-2*i),{i,0,Floor[(m-1)/2]}]; (* B/A is the rational function to be Landen transformed *) A=Sum[a[i] z^(p-i),{i,0,p}]; B=Sum[b[i] z^(p-2-i),{i,0,p-2}]; (* Denominator of the Landen transform *) A1=Resultant[PolynomialRemainder[A,P-x*Q,z],P-x*Q,z,Method->"SylvesterMatrix"]; E1=Sum[Coefficient[A1,x,p-i]*(P)^(p-i)*(Q)^i,{i,0,p}]; C1=Table[Coefficient[B*PolynomialQuotient[E1,A,z],z,s-i],{i,0,s}]; T[x_,a_,b_]:=Sum[(-1)^(a-x+j)*Binomial[a,x-j]*Binomial[b,j],{j,0,x}]; M1[j_,\[Alpha]_,\[Beta]_,\[Gamma]_]:=(-1)^(j+\[Alpha]-\[Beta])*C1[[2*j+1]]*2^(2*(\[Alpha]-\[Beta]))*\[Alpha]*(1/(2*\[Alpha]-\[Beta]))*Binomial[2*\[Alpha]-\[Beta],\[Beta]] *Binomial[\[Nu]-\[Alpha]-1+\[Beta],\[Gamma]]*(T[\[Lambda]+\[Alpha]*m,2*j,s-2*j]+T[\[Lambda]-\[Alpha]*m,2*j,s-2*j]); M2[j_,\[Alpha]_,\[Beta]_,\[Gamma]_]:=(-1)^(j+\[Beta])*C1[[2*j+2]]*2^(2*\[Beta]+1)*Binomial[\[Alpha]+\[Beta],2*\[Beta]+1]*Binomial[\[Nu]-2-\[Beta],\[Gamma]] *(T[\[Lambda]+\[Alpha]*m,2*j+1,s-2*j-1]-T[\[Lambda]-\[Alpha]*m,2*j+1,s-2*j-1]); (* Numerator of the Landen transform *) B1=1/2^s(Sum[Binomial[\[Nu]-1,\[Gamma]]Sum[(-1)^j C1[[2 j+1]] T[\[Lambda],2 j,s-2 j],{j,0,\[Lambda]}]x^(2\[Gamma]),{\[Gamma],0,\[Nu]-1}] +Sum[Sum[Sum[Sum[M1[j,\[Alpha],\[Beta],\[Gamma]],{\[Beta],0,\[Alpha]}],{\[Alpha],1,\[Nu]-1-\[Gamma]}],{j,0,\[Lambda]}]x^(2\[Gamma]),{\[Gamma],0,\[Nu]-2}] +Sum[Sum[Sum[Sum[M1[j,\[Alpha],\[Beta],\[Gamma]],{\[Beta],\[Alpha]-\[Nu]+\[Gamma]+1,\[Alpha]}],{\[Alpha],\[Nu]-\[Gamma],\[Nu]-1}],{j,0,\[Lambda]}]x^(2\[Gamma]),{\[Gamma],1,\[Nu]-1}] +Sum[Sum[Sum[Sum[M2[j,\[Alpha],\[Beta],\[Gamma]],{\[Beta],0,\[Alpha]-1}],{\[Alpha],1,\[Nu]-1-\[Gamma]}],{j,0,\[Lambda]-1}]x^(2\[Gamma]+1),{\[Gamma],0,\[Nu]-2}] +Sum[Sum[Sum[Sum[M2[j,\[Alpha],\[Beta],\[Gamma]],{\[Beta],0,\[Alpha]-1}],{\[Alpha],\[Nu]-\[Gamma],\[Nu]-1}],{j,0,\[Lambda]-1}]x^(2\[Gamma]+1),{\[Gamma],1,\[Nu]-2}]); (* Extract coefficients of the Landen transform F=J/H *) F={Table[Coefficient[B1,x,i],{i,p-2,0,-1}],Table[Coefficient[A1,x,i],{i,p,0,-1}]}; F=Expand[F]; Function[F/.{a[n_]:>#[[2,n+1]],b[n_]:>#[[1,n+1]]}] ]; DefineLandenTransform[p_Integer,m_Integer,name_,T_]:=Module[{DefineLanden,varnames,varpatterns}, DefineLanden[var_,val_]:=(name[var,m]:=val); varnames={Table[ToExpression["b"<>ToString[i]],{i,0,p-2}], Table[ToExpression["a"<>ToString[i]],{i,0,p}]}; varpatterns={Table[ToExpression["b"<>ToString[i]<>"_"],{i,0,p-2}], Table[ToExpression["a"<>ToString[i]<>"_"],{i,0,p}]}; DefineLanden[varpatterns,T[varnames]]]; Options[GenerateLandenTransforms]={ FileName->None, FunctionName->Global`LandenStep, MethodOrder->2}; GenerateLandenTransforms[p0_Integer:0,OptionsPattern[]]:=Module[{p=p0,m,file,name,T,T0}, m=OptionValue[MethodOrder]; file=OptionValue[FileName]; name=OptionValue[FunctionName]; (* Generate the Landen transform of degree p and order m, and use it to establish the Landen transforms of degree less than p, too. *) If[p>1, If[OddQ[p],p++]; T=GenerateLandenTransform[p,m]; Do[ T0=Function[ba,Drop[#,p-q]&/@T[Join[Table[0,{p-q}],#]&/@ba]]; DefineLandenTransform[q,m,name,T0], {q,2,p-1}]; DefineLandenTransform[p,m,name,T]]; If[file=!=None, Put[file]; Save[file,Evaluate[name]]]; ]; End[]; EndPackage[];