# Solver for differential equations of order 2 with rational function coeffs
#
# Finds all solutions that can be expressed in terms of 0F1 and 1F1 functions
#such as Bessel{I,J,K,Y}(nu, sqrt (f)) or Airy{A,B}(f)
# or Whittaker{M,W}(mu, nu, f) or Kummer{M,U}(mu, nu, f) with mu, nu
# constants and f an arbitrary rational function.
#
# Version 1.0 --- September 23, 2011
#
# Copyright: Quan Yuan, Mark van Hoeij and Ruben Debeerst.
macro(
EXP_INT = `DEtools/kovacicsols/e_int`,
EXP_SOLS = `DEtools/Expsols`,
NO_SOL = "No 0F1 or 1F1 solutions",
KUMMER = `dsolveBessel/KummerScore`
):
BesselSolver := proc()
local ans;
# use frontend to replace non-algebraic constants by names
ans := traperror(frontend(BesselSolver_doit,[args],
[{`+`,`*`,RootOf,radical,nonreal},
indets([args[2..-1]],function)]));
if ans=lasterror then
if ans=NO_SOL then
ans := 0
else
error ans
fi
fi;
if _EnvListOutput = true and ans=0 then ans := [] fi;
ans
end:
# Input:
# i) a differential operator L and (optional) the domain y
# ii) a differential equation L and the dependent variable y
# output: 0F1 or 1F1 type solution, i.e Bessel, Whittaker, or Kummer type solution if such solution exists
# otherwise return either 0 or NO_SOL.
BesselSolver_doit := proc(L,y)
#option trace;
local Dx,x,i, ext,res, Sirr, Sreg, mayB, caseB, caseW, T, sol,Sa;
i := NULL;
if nargs>1 then
if type(y,list(name)) and nops(y)=2 then
_Envdiffopdomain:=y
elif type(y,function) then
x := op(y);
_Envdiffopdomain:=[Dx,x];
return procname(DEtools['de2diffop'](L,y))
else
error "wrong number or type of arguments"
fi
fi;
if not assigned(_Envdiffopdomain) then
error "domain is not assigned"
fi;
Dx,x := op(_Envdiffopdomain);
if degree(L, Dx)<>2 then
error "input should be of order 2"
fi;
ext := indets([args], {radical, nonreal}); # All algebraics not in RootOf notation
if ext<>{} then
i := [args];
ext := radfield(ext); # p[1] translates to RootOf notation, p[2] translates back.
return op(eval( [procname(op(eval(i, ext[1])))], ext[2]))
fi;
#Convert everything into RootOf notation to avoid
#that sometimes Maple command doesn't work properly in radical case.
i:={Dx,x};
ext := indets(L, {name,RootOf}) minus i;
if ext = {} then
Normalizer := normal;
else
Normalizer := evala;
fi;
Sirr, Sreg, Sa,mayB, caseB, caseW := singInfo(L,ext,T);
#collect information about exponent difference and possible cases from singularities.
userinfo(1,dsolveBessel,"Exponents difference", Sirr,Sreg);
if mayB then
sol := findBessel(L,Sirr,Sreg,Sa,caseB, ext,x,T);
if sol <> 0 then
return sol;
fi;
fi;
if caseW>0 then
findWhittaker(L, Sirr, Sreg, Sa,ext, T, x, caseB, caseW)
else
error NO_SOL
fi
end:
findWhittaker := proc(L, oldSirr, Sreg,Sa,ext,T,x, oldcaseB, caseW)
#option trace;
local vf,v,c,f,Sirr,mult,i,k,s,mulist,ans,mu, vflist, j,Srest,caseB,Sir;
Sir, Srest,caseB:= updateSin(oldSirr,Sreg,Sa,ext,T,x, oldcaseB);
if caseW = 3 then
for i in Sir do
if type(i[3],list) then
v := coeff(i[3,1],T,0)/ldegree(i[3,1],T)/2;
v := evala(v^2*i[3,2])
else
v := coeff(i[3],T,0)/ldegree(i[3],T)/2;
v := x^2 - poly_d(v, x, ext)
fi;
if assigned(c) and evala(c-v)<>0 then error NO_SOL fi;
c := v
od;
Sirr := SqrtConst(Sir, ext, T, x, c)[1,1]
else
c := 1;
Sirr := Sir;
fi;
vflist := NULL;
for i in [findBesselvf(caseB, Sirr,Srest,ext,T)] do
if nops(i)=3 then
# The logarithmic case
for j in i[1..2], [abs(i[1]-1/2), i[2]] do
if j[1]=i[3] then
vflist := j, vflist
else
vflist := vflist, j
fi
od
else
vflist := vflist, i
fi;
od;
for vf in [vflist] do
v, f := op(vf);
userinfo(2,dsolveBessel,"Trying v,c,f = ", v,c,f);
ans := [1,-1];
for i from 1 to nops(Sirr) while c=1 do
mult := -ldegree(Sirr[i,3],T);
userinfo(3,dsolveBessel,"[p,mult]",[Sirr[i,1],mult]);
k := coeff(Sirr[i,3],T,0);
k := [seq(k+j,j=0..mult-1)];
mulist := k/(2*mult);
if i=1 then
ans := mulist
else
ans := [seq(`if`(member(true,
[seq(seq(type(2*evala(j-k*s),integer)
,k=mulist),s={1,-1})]
),j,NULL),j=ans)]
fi;
userinfo(4,dsolveBessel,"mu's so far:",ans);
if ans = [] then break fi;
od;
if ans<>[] then f := convert(f,sqrfree,x) fi;
userinfo(1,dsolveBessel,"remaining mu's:",ans);
for mu in ans do
userinfo(1,dsolveBessel,"testing mu",mu);
s := sign(length([-mu,-2*f]) - length([mu,2*f]));
if s=0 then s := `if`(abs_value(mu)=mu, 1, -1) fi;
i := whittakerequiv(L, s*mu, v, s*2*f, c);
if i<>0 then
return SimplifyAnswer(i)
fi
od;
od;
0
end:
# Input: list of singularities Sirr and Srest, extensions ext, name T
# Output: list of possible pairs [v,f]
findBesselvf := proc(caseB, Sirr, Srest, ext, T)
local f, inf;
f := besselsubst(Sirr,T,ext);
userinfo(1,dsolveBessel,"Possibilities for f ",f);
inf := member(infinity, map(i->i[1],Sirr));
if caseB >1 then
# find bessel function with v \nin \Q
findBesselvfirrat(Srest,f,inf,ext)
elif caseB=0 then
# find bessel function with v=0
findBesselvfln(Srest,f,ext)
elif caseB=1 then
# find bessel function with v\in\Q
findBesselvfrat(Srest,f,ext)
else
# find bessel function with v\in\Q where all singularity
# differences are integers
findBesselvfint(f,inf,ext,Srest)
fi;
end:
# Input: set of singularities SC with exp differences not in K,
# set of possible rational functions f, a boolean inf=(\inf\in\Sirr),
# extensions ext
# Output: list of possible pairs [v,f] with v\nin K
# such that exp-gauge-transformations of B(v,f(x)) can be a solution
# of the differential operator belonging to SC
findBesselvfirrat := proc(SC,f,inf,ext)
local Dx,x,z,i,g,gg,h,hh,d,r,a,b,l,v,sol,vf,ordeRoot,hasx;
Dx,x := op(_Envdiffopdomain);
vf := NULL;
#gen-exp differences are sqrt(.)+rational
#make minpolys for sqrt(.)
hasx := 0;
for i from 1 to nops(SC) do
d[i] := SC[i,3];
if has(d[i],x) then hasx := hasx+1 fi;
od;
if hasx = 0 then
return findBesselvfK(args)
elif hasx < nops(SC) then
error NO_SOL
fi;
userinfo(3,dsolveBessel,"minpolys for gen-exp differences",
[seq(d[i],i=1..nops(SC))] );
# all differences are a rational multiple of d[1]
# compute these rational coefficients
a := coeff(d[1],x,0);
r[1] := 1;
for i from 2 to nops(SC) do
b := coeff(d[i],x,0);
r[i] := sqrt(Normalizer(b/a));
if not type(r[i],rational) then return NULL fi;
od;
userinfo(4,dsolveBessel,"ratios for gen-exp differences",
[seq(r[i],i=1..nops(SC))] );
l := ilcm( seq(denom(r[i]),i=1..nops(SC)) );
h := mul(SC[i,4]^(r[i]*l),i=1..nops(SC));
userinfo(2,dsolveBessel,"numerator must be a power of",h);
# test all possibilities of f (try +/- each polar part)
for i from 1 to 2^(nops(f)-1) do
g := Normalizer(possibility(f,i));
userinfo(3,dsolveBessel,"Testing possibility", g);
# get c from first singularity in SC
g := changeconstant(g,x,SC[1,1],ext);
if g=NULL then next; fi;
# test if other singularities in SC are zeros
if not testzeros(g,x,map(j->j[1],SC)) then next; fi;
gg:=numer(g);
if degree(h,x)=0 then
a:=1;
else
a:=degree(gg,x)/degree(h,x);
fi;
# now h^a=g
if not type(a,integer) then
next
fi;
gg:=collect(gg/lcoeff(gg,x),x,Normalizer);
hh:=collect(h/lcoeff(h,x),x,Normalizer);
if Normalizer(gg-hh^a)=0 then
if SC[1,1]=infinity then
ordeRoot := degree(denom(g),x)-degree(gg,x)
else
# h^a contains i'th factor a*r[i]*l times, and r[1]=1
ordeRoot := a*l
fi;
v := sqrt(factor(-coeff(d[1],x,0)), 'symbolic');
v := v / (2*ordeRoot);
vf := vf, [v,g];
fi;
od;
vf
end:
# Input: set of singularities SC with non-rational exp differences in K,
# set of possible rational functions f, a boolean inf=(\inf\in\Sirr),
# extensions ext
# Output: list of possible pairs [v,f]
# such that exp-gauge-transformations of B(v,f(x)) can be a solution
# of the differential operator belonging to SC
findBesselvfK := proc(SC, f, inf, ext)
local Dx,x,z,i,g,gg,h,hh,r0,r1,a,l,v,vv,k,sol,vf,ordeRoot,flag;
Dx,x := op(_Envdiffopdomain);
vf := NULL;
# all differences are a rational multiple of d[1]
# compute these rational coefficients
r0[1] := 0;
r1[1] := 1;
for i from 2 to nops(SC) do
r0[i],r1[i] := compare(SC[i,3],SC[1,3]);
od;
userinfo(4,dsolveBessel,"ratios for exp differences",
[seq(r1[i],i=1..nops(SC))] );
l := ilcm( seq(denom(r1[i]),i=1..nops(SC)) );
h := mul(SC[i,4]^(r1[i]*l),i=1..nops(SC));
userinfo(2,dsolveBessel,"numerator must be a power of",h);
# test all possibilities of f
for i from 1 to 2^(nops(f)-1) do
g := Normalizer(possibility(f,i));
userinfo(3,dsolveBessel,"Testing possibility", g);
# get c from first singularity in SC
g := changeconstant(g,x,SC[1,1],ext);
if g=NULL then next; fi;
# test if other singularities in SC are zeros
if not testzeros(g,x,map(j->j[1],SC)) then next; fi;
gg:=numer(g);
if degree(h,x)=0 then
a:=1;
else
a:=degree(gg,x)/degree(h,x);
fi;
if not type(a,integer) then next fi;
gg:=collect(gg/lcoeff(gg,x),x,Normalizer);
hh:=collect(h/lcoeff(h,x),x,Normalizer);
if Normalizer(gg-hh^a) <> 0 then next fi;
# now h^a=gg
if SC[1,1]=infinity then
ordeRoot := degree(denom(g),x)-degree(gg,x)
else
# h^a contains the i'th factor a*l times
ordeRoot := a*l
fi;
v:=SC[1,3]/(2*ordeRoot);
vv := seq(v+k/(2*ordeRoot),k=0..(2*ordeRoot-1));
userinfo(2,dsolveBessel,"set for v", vv);
# test v with other zeros
for v in vv do
flag := true;
for k from 2 to nops(SC) do
ordeRoot := `if`(SC[k,1]=infinity,
degree(denom(g),x) - degree(gg,x),
r1[k]*a*l);
if not type(Normalizer(v*2*ordeRoot-SC[k,3])
,integer) then
flag := false; break
fi;
od;
if flag then vf := vf, [v,g] fi;
od;
od;
vf
end:
# Output: a new variable
newvar := proc()
local t;
t
end:
# Input: A,B in k
# Output: r0,r1 such that A=r0+r1*B
compare := proc(A,B)
local iszero,E,sol;
iszero := A- (r0+r1*B);
iszero := evala(Expand(numer(Normalizer(iszero))));
iszero := convert(iszero,RootOf);
do
E := indets(iszero,RootOf);
if E = {} then break fi;
iszero := subs(E[1]=newvar(),iszero);
od;
sol := SolveTools:-Linear({coeffs(expand(iszero),
indets(iszero,{name,symbol}) minus {r0,r1})},{r0,r1});
if not type(sol,set) or not type(map(rhs,sol),set(rational)) then
error NO_SOL
fi;
op(subs(sol, [r0,r1]))
end:
# Input: set of singularities Sln with exp differences in Z and log in
# formal solution, set of possible rational functions f, a boolean
# inf=(\inf\in\Sirr), extensions ext
# Output: list of possible pairs [v,f] with v=0
# such that exp-gauge-transformations of B(v,f(x)) can be a solution
# of the differential operator belonging to SC
findBesselvfln := proc(Sln,f,ext)
local Dx,x,i,j,g,sol,v,vf;
Dx,x := op(_Envdiffopdomain);
vf := NULL;
# test all possibilities of f
for i from 1 to 2^(nops(f)-1) do
g := Normalizer(possibility(f,i));
userinfo(3,dsolveBessel,"Testing possibility", g);
# compute constant
g := changeconstant(g,x,Sln[1,1],ext);
if g=NULL then next; fi;
# test for other zeros
if not testzeros(g,x,map(j->j[1],Sln)) then next; fi;
v := 2*max(degree(numer(g),x),degree(denom(g),x));
v := add(j[3]*j[5], j=Sln)/v;
vf := vf, [ceil(v),g,v]
od;
vf
end:
# Input: set of singularities SQ with exp differences in Q,
# set of possible rational functions f, extensions ext
# Output: list of possible pairs [v,f] with v in Q
# such that exp-gauge-transformations of B(v,f(x)) can be a solution
# of the differential operator belonging to SC
findBesselvfrat := proc(SQ,f,ext)
local Dx,x,i,j,k,g,h,fctrs,mult,v,vv,c,sol, newh,vf;
Dx,x := op(_Envdiffopdomain);
vf := NULL;
for i from 1 to 2^(nops(f)-1) do
g := possibility(f,i);
userinfo(3,dsolveBessel,"Testing possibility", g);
# get constant from first singularity
g := changeconstant(g,x,SQ[1,1],ext);
if g=NULL then next; fi;
if not testzeros(g,x,map(j->j[1],SQ)) then next; fi;
h := numer(g);
for j from 1 to nops(SQ) do
# look for multiplicities of the zeros
mult[j] := 0;
if SQ[j,1] = infinity then
mult[j] := degree(denom(g))-degree(numer(g));
else
mult[j] := 0;
while evala(Rem(h, SQ[j,4], x, 'newh')) = 0 do
mult[j] := mult[j] + 1;
h := newh
od;
# mult[j] \neq 0 since we already know
# that this is a zero
fi;
# possibilities for v
v := [seq(op([SQ[j,3]-i,SQ[j,3]+i]),i=0..mult[j])]
/ (2*mult[j]);
if j=1 then
vv := [seq(`if`(MatchInt([0,1/2,op(v[1..i-1])],
v[i]), NULL, abs(v[i])), i=1..nops(v))]
else
vv := [seq(`if`(MatchInt(v,i),i,NULL),i=vv)]
fi;
if nops(vv) = 0 then break; fi;
od;
userinfo(3,dsolveBessel,"multiplicities of factors",
[seq(mult[j],j=1..nops(SQ))]);
userinfo(2,dsolveBessel,
"possibilities for v", vv);
# if we get here, then we have a function g and a parameter v
# where for each singularity [x,t,d] in SQ there is a
# corresponding factor t in g, and it's multiplicity m is
# such that d=2*v*m. The rest-polynomial h has now to be a
# p-power such that 2*v*p \in \Z
for v in vv do
# rest must be a p-power
c:=lcoeff(h,x);
newh:=ispower(h/c,x,denom(2*v));
if Normalizer(h/c-newh^denom(2*v)) = 0 then
vf := vf, [v,g];
fi;
od;
od;
vf
end:
# Input: set of possible rational functions f, a boolean inf=(\inf\in\Sirr),
# extensions ext
# Output: list of possible pairs [v,f] with v in Q
# such that exp-gauge-transformations of B(v,f(x)) can be a solution
# of a differential operator with no half-regular singularities
findBesselvfint := proc(f,inf,ext, Sr)
local Dx,x,ff,n,p,i,g,lc,a,k,v,sol,C,c,cc,fctrs,vf,s;
Dx,x := op(_Envdiffopdomain);
vf:= NULL;
for i from 1 to 2^(nops(f)-1) do
ff := Normalizer(possibility(f,i)+C);
userinfo(3,dsolveBessel,"Testing possibility", ff);
if inf then
n := degree(numer(ff));
else
n := degree(denom(ff));
fi;
for p from 2 to n do if irem(n,p)=0 then
userinfo(3,dsolveBessel,
"Test whether numerator is a p-power", p);
# if inf then test p-power and get c
# if not then get c from p power or c is zero
g := numer(ff);
lc := lcoeff(g,x);
a := ispower(collect(g/lc,x,Normalizer),x,p);
userinfo(5,dsolveBessel, "Is a power of", a);
s := numer(Normalizer(evala(Content(g-lc*a^p,x))));
s := evala(Factors(s,ext));
cc := {seq(`if`(degree(k[1],C)=1,
-coeff(k[1],C,0)/coeff(k[1],C,1),NULL),k=s[2])};
if not inf then
# test c=0
fctrs := sqrfree(subs(C=0,g),x);
if add(modp(k[2],p), k=fctrs[2])=0 then
# the multiplicity of each factor of g
# is a multiple of p
cc:=cc union {0};
fi;
fi;
userinfo(2,dsolveBessel, "possibilities for c", cc);
for c in cc do
g := subs(C=c,ff);
for k from 1 to p-1 do
v := k/(2*p);
if denom(2*v)
[] and UpdateV(v,g,x,Sr,'v') then
vf := [v,g], vf
else
vf := vf, [v,g]
fi;
od;
od;
fi od;
od;
vf
end:
# Update v in such a way to make sure that no gauge transformation
# is used when none is needed. This procedure is not needed for
# correctness or completeness, but only to make the output look nicer.
UpdateV := proc(v,g,x,Sr,newv)
local s,m,mg,h,N,n1,n2;
m := {op(map(s -> s[3], Sr))};
m := min(op(m));
for s in Sr while s[3]>m do od;
if s[1]=infinity then
mg := degree(denom(g),x)-degree(numer(g),x)
else
mg := 0; h := numer(g);
while evala(Rem(h, s[4], x, 'h'))=0 do mg := mg+1 od
fi;
if mg=0 then return false fi;
n1 := solve( (v+N)*2*mg = m, N);
if type(n1,integer) then
newv := v+n1;
return true
fi;
n2 := solve( (N-v)*2*mg = m, N);
if type(n2,integer) then
newv := n2-v;
return true
fi;
if n1>1 then
if abs(round(n1)-n1) <= abs(round(n2)-n2) then
newv := v+round(n1)
else
newv := round(n2)-v
fi
fi;
false
end:
# Input: set of singularities Sirr of a differential operator L with
# non-constant generalized exponent
# Output: the set {f1,...fn} such that if L has a Bessel solution, then it is
# of the form r1*E(x)*B(v,f(x))+r2*(E(x)*B(v,f(x)))', where
# f=(-1)^k1*f1+...+(-1)^kn*fn+C, r1,r2\in\C(x), ki\in\Z and E(x) is a
# solution of a first order differential equation
besselsubst := proc(Sirr,T,ext)
local i,d,f,ff,j;
f := NULL;
for i from 1 to nops(Sirr) do
d := 1/2 * Sirr[i,3];
ff := add( coeff(d,T,j)/j*Sirr[i,2]^j, j=ldegree(d,T) .. -1);
f := f, evala(Trace(ff,ext,ext))
od;
[f]
end:
# Input: a list f=[f1,...fn] and an integer 1<=k<=2^n
# Output: the sum +-f1+-...+-fn, where the k-th possibility
# of signs is choosen
possibility := proc(f,k)
local b,n,a,i;
n := nops(f);
if k>2^n then error "there are just 2^n possibilities" fi;
b := k-1;
for i from n by -1 to 1 do
b := iquo(b,2,a[i])
od;
add((-1)^a[i]*f[i],i=1..n)
end:
# Input: a function f(x) and a point p
# Output: f(x)+c such that f(p)+c=0,
# if p=\infty then f(x) is returned
changeconstant := proc(f,x,p,ext)
local c,pp,ff,C;
if p=infinity then
return f;
fi;
if type(p, RootOf) then
pp:=evala(Norm(x-p,ext,ext));
ff:=numer(Normalizer(f+C));
c:=SolveTools:-Linear({coeffs(evala(Rem(ff,pp,x)),x)},{C});
if c=NULL then
NULL
else
Normalizer(f+subs(c[1],C))
fi;
else
Normalizer(f-subs(x=p,f))
fi;
end:
# Input: a function f(x) and a set of points pp
# Output: true, if all points in pp are a zero of the numerator
# and false otherwise
# for \infty the condition changes to
# degree(denom(f))>degree(numer(f))
testzeros := proc(f,x,pp)
local p,g;
g:=numer(f);
for p in pp do
if p=infinity then
if not degree(g,x) sqrt(c) * (x,mu)
eq :=diff(Y(x),x,x)-(4*evala(v^2)+c*x^2-4*c*mu*x-1)/4/x^2*Y(x);
eq := PDEtools['dchange']({x=subs(x=t,f)},eq,[t],
params=(indets([args],{name,symbol}) minus {x}) );
L2 := DEtools['de2diffop'](eq,Y(t)):
L2 := s_equiv(L2,L);
if L2 <> 0 then
wc := sqrt(c, 'symbolic');
t:=1/sqrt(1/c,'symbolic');if length(t) {} then
if member(i, Sodd) then
disc := evala(Norm(disc, ext, ext))
else
next
fi
fi;
S := S, Normalizer(disc)
od;
#if S = NULL then
# We found no points where c was easy to read, this
# means we'll have to resort to Subfields to get the
# potential values of c.
#S := seq(`if`(type(i[3],list),
# sqrfree(evala(Norm(x^2-i[3][2],ext,ext)),x)[2,1,1], i[4])
#,i=Sirr);
S := seq(`if`(type(i[3],list),
subs(_Z=x,op(1, lhs(
evala(Primfield({`if`(degree(i[4],x)>0, RootOf(i[4],x), NULL), RootOf(sqrfree(evala(Norm(x^2-i[3][2],ext,ext)),x)[2,1,1],x) }))[1,1]
)))
, i[4])
,i=Sirr);
S := evala(Subfields({S}, 2, ext, x));
S := {seq(evala(Norm(x-i,ext,ext)), i=S)};
userinfo(2,dsolveBessel,"subfields",S);
#else
# # Found at least some point where c could be read, so
# # just pick the first one and try to simplify it a bit:
# S := sort([S],length)[1];
# S := {PolynomialTools:-Shorten(x^2 - S,x)}
#fi;
# The set of all possible c's other than c=1.
S := {seq(Normalizer(discrim(i,x)/4), i=S)};
userinfo(2,dsolveBessel,"possible c's other than c=1 in f=sqrt(c)*g",S);
fi;
for c in S do
# adjust Sirr if possible
flag := true;
res := NULL;
for i in Sirr do
extp := ext union indets(i[1],RootOf);
pol := `if`(type(i[3],list), i[3,2], 1)*x^2 - c;
pol := evala(Primpart(pol,x));
pol := evala(Factors(pol, extp))[2];
if nops(pol)=1 then
flag := false; break
fi;
pol := collect(pol[1,1],x);
wc := coeff(pol,x,0)/coeff(pol,x,1);
i3 := `if`(type(i[3],list), i[3,1], i[3]) / wc;
res := res, subsop(3 = collect(i3,T,evala), i)
od;
if flag then
ans := ans, [[res], c]
fi
od;
{ans}
end:
updateSin := proc (oldSirr,Sreg,Sa,ext,T,x,oldCaseB)
#option trace;
local Sirr, Srest,Sr, s,extp, R,caseB,d,c,R1;
Sirr :=NULL;
Srest := NULL;
Sr := Sreg;
caseB:=oldCaseB;
if Sr = [] then
Sr := Sa;
caseB:= -1
fi;
for s in oldSirr do
extp := indets(s[1],RootOf) union ext;
R := indets(s[3],RootOf) minus extp;
if nops(R) >0 then
if nops(R)<>1 then
error "should not happen"
fi;
R := R[1];
d := evala(discrim( evala(Norm(x-R,extp,extp)), x));
c := evala(Factors(x^2-d,{R} union extp))[2][1][1];
c := coeff(c,x,0);
c := s[3]/c;
s[3] := [collect(c,T,evala),d]; ;
fi;
Sirr := Sirr,s;
od;
for s in Sr do
extp := indets(s[1],RootOf) union ext;
R := indets(s[3],RootOf) minus extp;
R1 := indets(s[3],RootOf) minus ext;
if nops(R) >0 then
s[3] := x^2-s[3];
elif nops(R1)>0 then
s[3] := poly_d(s[3], x, ext)
else
s[3] := abs_value(s[3])
fi;
Srest := Srest,s;
od;
[Sirr],[Srest], caseB
end:
Kummerequiv := proc(L,mu,nu,f,x)
local eq,t,L2,Y;
# Kummer equation
eq := x * diff(Y(x),x,x) + (nu-x)*diff(Y(x),x) - mu*Y(x);
eq := PDEtools['dchange']({x=subs(x=t,f)},eq,[t],
params=(indets([args],{name,symbol}) minus {x}) );
L2 := DEtools['de2diffop'](eq,Y(t)):
L2 := s_equiv(L2,L);
if L2 <> 0 then
L2, [KummerM(mu, nu, f), KummerU(mu, nu, f)]
else
0
fi
end:
# Minpoly of c should be an integer shift of x^2-const, if so, return x^2-const
poly_d := proc(c, x, ext)
local t,d;
d := sqrfree(evala(Norm(x-c,ext,ext)),x)[2,1,1];
d := collect(d/lcoeff(d,x),x,Normalizer);
t := coeff(d,x,1)/2;
if degree(d,x)<>2 or not type(t,integer) then
error NO_SOL
fi;
collect(subs(x=x-t,d),x,Normalizer)
end:
# Return -a if a looks negative, otherwise return a.
abs_value := proc(a)
local i;
i := traperror(sign(a));
if i=lasterror then
i := traperror(sign(Re(evalf[10](a))))
fi;
if i=-1 then -a else a fi
end:
# Input: differential operator L, extensions ext, name T
# Output:
#1) a list of singularities S (seperate by regular singularities and irregular singularities) such that
# for each singularity s of L at p, S contains [p,t,d,pol,deg], where
# - t is local parameter, ie x-p or 1/x
# - d is exponent difference in terms of T, if D is exponent differnce
# -- d = D for unramified case
# -- d = [D, aT^2=b]
# - pol is polynomial over K with zero p
# - deg = degree(pol)
# 2) mayB, a boolean value indicate whether Bessel case is possible.
# 3) caseB, possible Bessel case list:
# -0: easy case;
# -1: lograthemic case;
# -2: rational case;
# -3: irrational case;
# 4) caseW,
# -0: no Whittaker solutions
# -1: when mu is rational
# -2: when mu is irrational but in K
# -3: when mu is not in K
singInfo := proc(L,ext,T)
#option trace;
local Dx,x,p,S,Sirr,Sreg,mayB,mayK,caseB,caseW, muD, A, q, g, t, d,c, R, extp, ae,Sa;
global KUMMER; # Used to see if we should use Kummer functions.
mayB := true; # true means L may have Bessel solutions
Sirr := NULL;
Sreg := NULL;
Sa := NULL;
caseB, caseW, muD, KUMMER := {}, {}, 0, 0;
Dx,x := op(_Envdiffopdomain);
A := evala(Primpart(L,Dx)); A := [seq(coeff(A,Dx,i), i=0..2)];
p := A[3];
userinfo(3,dsolveBessel,"singularities are zeros of",p);
# Each finite non-apparant singularity is a root of this p:
#
# p := evala(Gcd(p, denom(Normalizer(
# DEtools[LCLM](L,Dx-rand(10..100)()) )) ));
#
# The following line computes such p much faster:
#
p := seq(`if`(A[2]=0 or q[2]>1, q[1], Normalizer(q[1] /
gcd( gcd(q[1], A[2] + diff(A[3],x)),
diff(A[1],x)*A[2]-A[1]*diff(A[2],x)-A[1]^2)
)), q=sqrfree(A[3],x)[2]);
p := mul(q, q=[p]);
S := [infinity, seq(i[1],i=evala(Factors(p,ext))[2])];
# Every non-apparant singularity is in S
for q in S do
R := {};
if q=infinity then
p := infinity
else
if not has(q,x) then next fi;
p := RootOf(q,x)
fi;
userinfo(3,dsolveBessel,"processing singularity",p);
extp := indets(p,RootOf) union ext;
g := DEtools[gen_exp](L,[Dx,x],T,x=p);
#
# The above line works OK, however, kovacicsols has
# a gen_exp implementation optimized for order 2.
# So we'll use that instead:
#
# g := [`DEtools/kovacicsols/gen_exp`(A,T,x,p,extp)];
userinfo(3,dsolveBessel,"point and gen_exp",[p,g]);
t := `if`(p=infinity, 1/x, x-p); # t = local parameter at p
if nops(g)=1 and nops(g[1]) = 2 then
# gen_exp involves an algebraic extension
if lhs(g[1,-1])<>T then
d := add(coeff(g[1,1],T,i) * 2 * mods(i,2) * T^i, i=ldegree(g[1,1],T)..0);
d := [d, g[1,2]]
else
d := evala(Trace(g[1, 1], ext, ext), x)-2*g[1, 1];
if has(d,t) then
ae := true;
R := indets(d,RootOf) minus extp;
if nops(R)>1 then
error "should not happen";
fi;
else
ae := false;
fi;
fi;
else
d := g[1,1] - `if`(nops(g)=2, g[2,1], g[1,2]);
ae := false;
fi;
d := collect(d, T, evala);
p := [p,t,d,op(`if`(q=infinity, [1,1], [q,degree(q,x)]))];
userinfo(2,dsolveBessel,"gen_exp-array", p);
if has(d,T) then
if type(d,list) then
caseW := {0,1};
#squareroot case, no Whittaker solutions.
elif ae then #has algebraic extenion but not ramified
t := coeff(d,T,0);
if t=0 then
# No Whittaker solutions:
caseW := {0,1}
else
mayB := false;
# 0: no Whittaker solutions
# 1: when mu is rational
# 2: when mu is irrational but in K
# 3: when mu is not in K
caseW := caseW union {3}
fi
else
t := coeff(d,T,0);
mayB := mayB and type(t,integer);
if type(t,rational) then
muD := igcd(muD, denom(t)*ldegree(d,T));
# denom(2*mu) divides muD, so if muD=1
# then 2*mu\in Z, which is Bessel case
caseW := caseW union {1,`if`(muD=1,0,1)}
elif indets(t,RootOf) minus ext = {} then
caseW := caseW union {2}
else
caseW := caseW union {3}
fi;
# if generalized exponents look like Kummer
# after change of variable, then increase the
# Kummer score; if it looks like whittaker
# then decrease the Kummer score:
c := {seq(min(0,ldegree(t[1],T)),t=g)};
KUMMER := KUMMER + p[5]*
`if`(nops(c)=1, op(c), abs(c[1]-c[2]))
fi;
if not mayB then
userinfo(1,dsolveBessel,
"There are no Bessel solutions")
fi;
if nops(caseW)>1 then
userinfo(1,dsolveBessel,
"There are no Whittaker solutions");
fi;
Sirr := Sirr, p
elif R <> {} then
caseB := caseB union {3};
Sreg := Sreg, p;
elif not type(d,rational) then
caseB := caseB union {2};
Sreg := Sreg, p;
elif not type(d,integer) then
caseB := caseB union {1};
Sreg := Sreg, p;
elif d=0 or ( DEtools['formal_sol'](L,`has logarithm?`,x=p[1]) ) then
# d is an integer and has a logarithm iff
# there is a gauge operation such that d=0
caseB :=caseB union {0};
Sreg := Sreg,p;
elif abs(d)>2 then
# Apparant singularities with large d
Sa := Sa,p
fi;
if nops(caseB)>1 or (nops(caseW)>1 and not mayB) then
error NO_SOL
fi;
od;
if Sirr = NULL then
error NO_SOL
elif caseB={} then
caseB:={1};
#Sreg is empty set, it is rational case.
fi;
[Sirr], [Sreg], [Sa], mayB, op(caseB), `if`(nops(caseW)>1, 0, op(caseW))
end:
#input: the operataor L and information of exponents difference
#output: the possible list of pairs (f,nu), f represents the
# change of variable and nu is Bessel parameter.
findBessel := proc(L,oldSirr,oldSreg,Sa, caseB, oldext,x,T)
#option trace;
local Sirr, Sreg, easy, dA, B, vflist, vf,c,ai,needext,extp,s,temp, uf, ext;
Sirr, Sreg,B,dA, easy := singSeries(oldSirr,oldSreg, ext,x,T);
#find the new information we need to find the possible pairs
ext := oldext;
needext := {op(map(s -> s[5], Sirr))};
needext := min(op(needext));
if needext>1 and (not easy) and caseB=1 then
_Env_original_field := ext;
for s in Sirr while s[5]>needext do od;
extp := indets(s[1],RootOf) union ext;
ext := extp;
temp := singInfo(L,extp,T); # return Sirr, Sreg, Sa,mayB, caseB, caseW
temp := singSeries(temp[1], temp[2],extp,x, T); #return Sirr, Sreg,B,dA, easy
Sirr := temp[1]; Sreg:=temp[2];
fi;
vflist := NULL;
if easy then
vflist:= sqrtEasy(Sirr, Sreg,caseB,B,dA,ext,x);
if (indets(vflist, {name}) minus {x}<>{}) then vflist :=NULL; fi;
fi;
if (vflist=NULL) then
if caseB=0 then
vflist := sqrtLog(Sirr, Sreg,B,dA,ext,x);
elif caseB=1 then
vflist := sqrtRat(L, Sirr, Sreg, Sa,B,dA,ext,x,T);
elif caseB>1 then
vflist := sqrtIrrat(Sirr, Sreg,B,dA,ext,x);
fi;
fi;
for vf in vflist do
userinfo(2,dsolveBessel,"testing",vf);
ai := testAiry(vf[2],vf[1],ext);
if ai[1] then
B := Airyequiv(L,ai[2]);
if B<>0 then
return SimplifyAnswer(B)
fi;
fi;
c := sign_value(lcoeff(numer(vf[2]),x));
B:=BesselSqrtequiv(L,oldext,c,op(vf));
if B<>0 then
return SimplifyAnswer(B)
fi
od;
return 0;
end:
# Input: differential operator L, a rational function f and a parameter v
# Output: M, y
# y is the general bessel solution B(v,f)
# M is a differential operator such that M(y) is a solution of L
# if such a solution is not found by the equiv-program, then 0 is
# returned
BesselSqrtequiv := proc(L,ext, cpos,v,f)
#option trace;
local Dx,x,eq,t,L2,Y,g,modifiedbessel;
Dx,x := op(_Envdiffopdomain);
modifiedbessel := evalb(cpos = 1);
# The BesselI equation after change of variables x --> sqrt(x)
eq := (-(1/4)*x-(1/4)*v^2)*Y(x)+x*(diff(Y(x), x))+x^2*(diff(diff(Y(x), x), x));
eq := PDEtools['dchange']({x=subs(x=t,f)},eq,[t],
params=(indets([args],{name,symbol}) minus {x}));
L2 := DEtools['de2diffop'](eq,Y(t)):
L2 := s_equiv(L2,L);
#if L2 <> 0 then
t:=0;
if type(v,rational) and nargs=5 and L2[2]<>1 and denom(v)>2 then
g := length(L2[2]);
t := procname(L, ext,cpos,abs(v+1-2*frac(v)), f, g);
fi;
if L2=0 then
if t<>0 then
return t;
else
return 0;
fi;
else
if length(t[2])0 then
userinfo(2,dsolveBessel,"found smaller answer");
return t;
fi;
g := compute_sqrt(collect(cpos*f,x,evala),x,ext);
if modifiedbessel then
L2, [BesselI(v,g),BesselK(v,g)]
else
L2, [BesselJ(v,g),BesselY(v,g)]
fi;
#else
#
fi;
end:
sign_value := proc(a)
local i;
i := traperror(sign(a));
if i=lasterror then
i := traperror(sign(Re(evalf[10](a))))
fi;
i
end:
compute_sqrt := proc(f,x,ext)
local v,p1,p2,i,w,f1;
#option trace;
v := evala(Normal(f,expanded));
v := evala(Sqrfree(v,x));
p1 := 1;
p2 := 1;
for i in v[2] do
if i[2] mod 2 =0 then
p1 := p1*evala(Factor(i[1],ext))^(i[2]/2);
else
p2 := p2*evala(Factor(i[1],ext))^i[2];
fi;
od;
p2 := p2^(1/2);
if has(p1,x) then
sqrt(v[1])*p1*p2;
else
if type(v[1],`rational`) then return sqrt(v[1])*p1 * p2 fi;
try
w:=evala(Factors(x^2-v[1],{op(ext)}));
catch :
w:=evala(Factors(numer(normal(x^2-v[1])),{op(ext)}))
end try;
w:=select(has,w[2],x);
if nops(w)=1 then
sqrt(v[1]) * p1*p2
else
-coeff(w[1][1],x,0)/coeff(w[1][1],x,1) *p1* p2
fi
fi;
end:
testAiry:=proc(f,v,ext)
#option trace;
local fas,fa,T, g;
if (not type(v,rational)) or denom(v)<> 3 then
return [false,0];
fi;
fas := evala(Factors(numer(f), ext))[2];
g:=1;
for fa in fas do
if irem(fa[2],3) <> 0 then
return [false,0];
fi;
g:=g*fa[1]^(fa[2]/3);
od;
fas := evala(Factors(denom(f), ext))[2];
for fa in fas do
if irem(fa[2],3) <> 0 then
return [false,0];
fi;
g:=g/(fa[1]^(fa[2]/3));
od;
[true, g];
end:
Airyequiv := proc(L,f)
local eq,t,L2,Y;
# Airy equation
eq := diff(Y(x),x,x) -x*Y(x);
eq := PDEtools['dchange']({x=subs(x=t,f)},eq,[t],
params=(indets([args],{name,symbol}) minus {x}) );
L2 := DEtools['de2diffop'](eq,Y(t)):
L2 := s_equiv(L2,L);
if L2 <> 0 then
L2, [AiryAi(f), AiryBi(f)]
else
0
fi
end:
# Apply operator exp(Int(d,x)) * B1 on the functions in B2.
SimplifyAnswer := proc(d, B1, B2)
local c, c2, C, Dx, x, i, de, Y, f, sol, p;
global _C1, _C2;
userinfo(1,dsolveBessel,"Operator and functions",exp(Int(d,x))*B1, B2);
Dx, x := op(_Envdiffopdomain);
de := DEtools['diffop2de'](B1,Y(x));
f := eval(de, Y(x) = B2[1]);
i := select(has, indets(f,function), op(0,B2[1]));
# f := collect(primpart(f, i, 'c'), i, factor);
#
# Bug, when f := A( 3^(1/2)*(2*x+4) ); i := {f};
# then primpart(f, i, 'c'); returns 1 instead of f.
#
# Fix:
p := proc(f,v) local w,i,j;
w := {seq([v[i]=w[i],w[i]=v[i],w[i]], i=1..nops(v))};
w := seq(map(i -> i[j], w), j=1..3);
i := primpart(eval(f,w[1]), w[3], 'j');
eval(collect(i,w[3],factor),w[2]), j
end:
f, c := p(f, i);
C := EXP_INT(Normalizer(d + diff(c,x)/c),x);
sol := C * f;
f := eval(de, Y(x) = B2[2]);
i := select(has, indets(f,function), op(0,B2[2]));
# f := collect(primpart(f, i, 'c2'), i, factor);
# Same bug as above, fix:
f, c2 := p(f, i);
sol := [sol, Normalizer(c2/c) * C * f];
if _EnvListOutput = true then
sol
else
_C1 * sol[1] + _C2 * sol[2]
fi
end:
singSeries:=proc(oldSirr, OldSreg,ext,x, T)
#option trace;
local Sirr, Sreg, dA, B, neq, i,p, d, j,l, acc, temp,easy;
Sirr := NULL;
Sreg, easy, dA, B := OldSreg, false, 0,1;
#here we also check if it is easy case.
neq := add(i[5], i in Sreg); # use neq to track the possible equations we can get.
for p in oldSirr do
if type(p[3], list) then
d:=p[3][1];
acc := -ldegree(d,T);
if p[1] <> infinity then
B := B*p[4]^acc;
else
dA := acc;
fi;
acc := ceil((1+acc)/2);
d := collect(d,T,evala);
if type(d, `+`) then l := [op(d)] else l := [d] fi;
d := add(i/ldegree(i, T), i = l);
d := collect(d^2, T, evala);
temp := evala(rhs(p[3,2])/coeff(lhs(p[3,2]),T,2));
if type(d, `+`) then l := [op(d)] else l := [d] fi;
d := add(i*T^(-(1/2)*ldegree(i, T)), i = l);
if type(d, `+`) then l := [op(d)] else l := [d] fi;
j := (1/2)*ldegree(d, T);
if j=FAIL then error NO_SOL fi;
d := 0;
for i in l do if degree(i, T) < j then d := d+i fi od;
d := subs(T = temp, d);
# addjust coefficient and take squre to get series after squre.
neq := neq + acc*p[5];
Sirr := Sirr, [p[1],p[2],[d,acc], p[4],p[5]]
else
d:=p[3];
acc:=-ldegree(d,T);
if p[1] <> infinity then
B := B*p[4]^(2*acc);
else
dA := 2*acc;
fi;
d := d-coeff(d,T,0);
d := collect(d,T,evala);
if type(d, `+`) then l := [op(d)] else l := [d] fi;
d := add(i/(2*ldegree(i, T)), i = l);
d := d^2;
d := evala(subs(T = p[2], d));
neq := neq +acc*p[5];
Sirr := Sirr, [p[1],p[2],[d,acc], p[4],p[5]]
fi;
od;
dA := degree(B, x)+dA;
if neq >dA then easy:=true fi;
# if has(Sreg, infinity) then dA := dA-1 fi; #adjust degree for infinity to reduce some computation.
[Sirr],Sreg,B,dA, easy;
end:
sqrtEasy:=proc(Sirr, Sreg, caseB,B,dA, ext, x)
#option trace;
local a,f,fn,p,i,j,t,linearr,temp,res,d,d1,nonpole,und;
userinfo(3,myname,`Now it is the easy case`);
f := add(a[i]*x^i, i = 0 .. dA)/B;
und := indets(f) minus {x} ;
if has(Sreg, infinity) then
f:=subs(a[dA]=0,f);
fi;
fn := numer(f);
for p in Sreg do
fn := fn/p[4];
od;
linearr := evala(Rem(numer(fn), denom(fn), x));
linearr := coeffs(linearr, x);
temp := SolveTools:-Linear({linearr},und);
f := subs(op(temp), f);
#each p in Sreg should be a zero.
if has(Sreg, infinity) then
f:=subs(a[dA]=0,f);
fi;
d := 0;
j := 0; # use j indicate that if infinity is irregularsigularity
for p in Sirr do
if p[1] = infinity then
d := d+ p[3][1];
j := p[3][2];
else
d := d+evala(Trace(p[3][1], ext, ext), x);
fi;
od;
nonpole:=1;
for p in Sirr do
nonpole:= nonpole*p[4]^p[3][2];
od;
nonpole := Normalizer((f-d)*nonpole);
temp := evala(Rem(numer(nonpole), denom(nonpole), x));
linearr := coeffs(temp, x);
#each irregular singularity represents pole part of f.
if j<>0 then #the equations at infinity
temp := evala(Quo(numer(nonpole), denom(nonpole),x));
d1:=degree(temp, x);
temp := seq(coeff(temp, x, i), i = d1-j+1 .. d1);
linearr := linearr, temp;
fi;
temp := SolveTools:-Linear({linearr},und);
temp := op(temp);
if temp <> NULL then
f := subs(temp, f);
f := op(simplef({f}));
else
f := NULL;
fi;
if caseB =0 then
return findnuLog(f,Sreg,x);
elif caseB =1 then
return findnueasyrat(f,Sreg,B,x);
else
return findnueasyIrrat(f,Sreg,x);
fi;
end:
findnueasyIrrat:= proc(f,Sreg,x)
#option trace;
local mult,h,res,i,s, smin, multmin;
mult := 0;
h := numer(f);
res := {};
multmin := infinity;
for s in Sreg do
if s[1] = infinity then
mult := degree(denom(f),x)-degree(h,x);
else
while evala(Rem(h, s[4], x, 'h')) = 0 do mult := mult+1 od
fi;
if mult0 then #the equations at infinity
temp := evala(Quo(numer(nonpole), denom(nonpole),x));
temp := collect(temp, x, Normalizer);
d1:=degree(temp, x);
temp := seq(coeff(temp, x, i), i = d1-j+1 .. d1);
linearr := linearr, temp;
fi;
temp := SolveTools:-Linear({linearr},{c});
if temp <> NULL then f := f union {subs(temp, c*kn/B)} fi;
od;
if f<>{} then
f := simplef(f);
fi;
for g in f do
res:= res union findnuLog(g,Sreg,x);
od;
end:
findnuLog := proc(g,Sreg,x)
local nu,j;
nu := max(degree(numer(g),x),degree(denom(g),x));
nu := ceil(add(j[3]*j[5], j=Sreg)/nu);
{[abs(nu),g]}
end:
searchKnLog :=proc(Sreg,d)
#option trace;
local res,temp,s,deg,t,k;
res:=NULL;
if nops(Sreg)=1 then
if d mod Sreg[1][5] =0 then
if Sreg[1][1]= infinity then
res:=res,1;
else
res:= res, Sreg[1][4]^(d/Sreg[1][5]);
fi;
fi;
else
deg := Sreg[1][5];
for k from 1 to iquo(d,deg) do
temp := searchKnLog([seq(Sreg[i],i=2..nops(Sreg))],d-k*deg);
if temp<>NULL then
if Sreg[1][1]<>infinity then
for t in temp do
res:= res,Sreg[1][4]^k*t;
od;
else
for t in tmep do
res:=res,t;
od;
fi;
fi;
od;
fi;
[res];
end:
# Input: A monic polynomial f\in K[x] and a positive integer p\in\N
# Output: g\in K[x], s.t.
# if there exists a solution to y^p=f, then y=g is such a solution
ispower := proc(f,x,p)
local d,n,A,Ap,a,i,b;
if p=1 or degree(f,x)=0 then return f fi;
d:=degree(f,x);
n:=d/p;
if not type(n,integer) then return NULL fi;
A:=add(a[i]*x^i,i=0..n-1)+x^n;
Ap:=collect(A^p,x);
for i from 1 to n do
# Solve a linear equation in one unknown:
b[n-i]:=solve(subs({seq(a[n-j]=b[n-j],j=1..i-1)},coeff(Ap,x,d-i))
-coeff(f,x,d-i),a[n-i] );
od;
Ap:=subs({seq(a[n-j]=b[n-j],j=1..n)},Ap);
subs({seq(a[n-j]=b[n-j],j=1..i-1)},A)
end:
sqrtIrrat := proc(Sirr, Sreg,B,dA,ext,x)
#option trace;
local ira, A, c, sum, i,s,d, mult, acc, f,vf,j, p, nonpole, temp, linearr, d1,IND, smin,multmin, minpo, r, a,b;
userinfo(3,myname,`Now it is the irrational case`);
f := NULL;
vf := {};
ira := indets(Sreg[1][3],{RootOf, name})[1];
if (ira in ext) then
IND := [op(indets([args],{RootOf,name}))];
A :=c;
sum := add(sign(i[3],IND)*i[3]*i[5], i=Sreg);
sum := collect(sum, ira, evala);
sum := lcoeff(sum, ira);
multmin := infinity;
for s in Sreg do
mult := collect(sign(s[3],IND)*s[3], ira, evala);
mult := lcoeff(mult, ira);
mult := mult/sum*dA;
if type(mult, `integer`) then
A := A*s[4]^mult;
if mult2 then return NULL fi;
od;
a:=coeff(minpo[1],x,0);
r[1]:=1;
multmin:=1;
smin:=Sreg[1];
for i from 2 to nops(Sreg) do
b:=coeff(minpo[i],x,0);
r[i] := sqrt(Normalizer(b/a));
if not type(r[i],rational) then return NULL fi;
if r[i]0 then #the equations at infinity
temp := evala(Quo(numer(nonpole), denom(nonpole),x));
d1:=degree(temp, x);
temp := seq(coeff(temp, x, i), i = d1-j+1 .. d1);
linearr := linearr, temp;
fi;
temp := SolveTools:-Linear({linearr},{c});
if temp <> NULL then
f := subs(temp, A/B);
f := op(simplef({f}));
for i from 0 to floor(multmin/2) do
vf:= vf union {[(smin[3]+i)/multmin,f]};
od
fi;
vf
end:
poly_to_powerseries := proc(P, x, p, T, acc)
# option trace;
local d, l, i, S;
if P=0 then error "only treat non-zero powseries" fi;
if p = infinity then
d, l:= degree(P,x), lcoeff(P,x);
[l, -d,
add(evala(coeff(P,x,d-i)/l)*T^i, i=0..acc-1), acc]
else
S := collect(eval(P, x = T + p), T, evala);
d, l := ldegree(S,T), tcoeff(S,T);
# Note: the evala in the line S := .. is
# needed to ensure that d is correct.
[l, d,
add(evala(coeff(S,T,d+i)/l)*T^i, i=0..acc-1), acc]
fi
end:
dthroot_powseries := proc(L, tp, d, ext)
# option trace;
local X,v,res,i,s,l;
l:=L[1];
try
v:=evala(Factors(X^d -l,ext));
catch :
v:=evala(Factors(numer(normal(X^d - l)),{op(ext)}))
end try;
v:=select(has,v[2],X);
res := NULL;
for i in v do
if degree(i[1],X) = 1 then
res := res, -coeff(i[1],X,0)/coeff(i[1],X,1)
fi
od;
v := series(L[3]^(1/d), tp=0, L[4]);
v := collect(convert(v,polynom),tp,evala);
res :={seq([i, L[2]/d, v, L[4]], i = [res])};
res;
end:
pow_time := proc(L1,L2,tp)
local acc,f,ind;
acc := min(L1[4],L2[4]);
f := L1[3]*L2[3];
[evala(L1[1]*L2[1]),L1[2]+L2[2],
convert(series(f,tp=0,acc),polynom),
acc]
end:
pow_reciprocal := proc(L1,tp)
[Normalizer(1/L1[1]),-L1[2],
convert(series(1/L1[3],tp=0,L1[4]),polynom),
L1[4]]
end:
sqrtRat := proc(L,Sirr, Sreg,Sa,B,dA,ext,x,T)
#option trace;
local s, l, dnu, m, dv, d1,d2, res, i, knl, j, k,f,ff;
res:={};
if Sreg=[] then
dnu := {};
m := dA;
for i from 3 to floor(m/2) do
if irem(m,i)=0 then dnu:= dnu union {i};fi;
od;
dnu := dnu union {m};
else
dnu := {};
dv := denom(Sreg[1][3]);
for s in Sreg do
dv:=lcm(dv,denom(s[3]));
od;
for i from 1 to dA do
d1:=0;
d2:=0;
for s in Sreg do
d1 := d1+i*dv/denom(s[3])*s[5];
d2 := gcd(d2, i*dv/denom(s[3]));
od;
if d1<=dA and irem(dA,d2)=0 then
dnu := dnu union {i*dv};
fi;
od;
fi;
userinfo(3,myname,`The possible list of the denominator of nu is`,dnu);
for i in dnu do
knl :=searchA1k(Sreg,dA,i);
if knl <> [] then
ff := findSqrtf(L,Sirr,B,i,knl,ext,x,T);
ff := simplef(ff);
if assigned(_Env_original_field) then
f := {};
for k in ff do
if indets(k, RootOf) minus _Env_original_field ={} then
f:=f union {k};
fi;
od;
else
f:= ff;
fi;
fi;
for j in f do
res := res union findnuRat(j,i,Sreg,Sa,B,x);
#for k from 1 to floor(i/2) do
# if j<>0 and gcd(k,i)=1 then res := res union {[k/i,j]} fi;
#od;
od;
od;
res;
end:
findnuRat :=proc(f,d,Sreg,Sa,B,x)
#option trace;
local mult, vf, h, s,k,n1,n2,v,p,newh;
mult:=0;
h := numer(f);
vf :={};
if nops(Sreg) <> 0 then
s:=Sreg[1];
if s[1]=infinity then
mult := degree(denom(f),x)-degree(numer(f),x);
else
while evala(Rem(h, s[4], x, 'newh')) = 0 do
mult := mult + 1;
h := newh
od;
fi;
else
for p in Sa do
if p[1]=infinity then
mult := degree(denom(f),x)-degree(numer(f),x);
else
while evala(Rem(h, p[4], x, 'h'))=0 do mult := mult+1 od;
fi;
if mult<>0 then
s := p;
break;
fi;
od;
fi;
for k from 1 to floor(d/2) do
if gcd(k,d)=1 then
v := k/d;
if mult=0 then
vf:=vf union {[v,f]};
else
n1 := solve( (v+N)*mult = s[3], N);
if type(n1,integer) then
vf := vf union {[abs(v+n1),f]};
break;
fi;
n2 := solve( (N-v)*mult = s[3], N);
if type(n2,integer) then
vf := vf union {[abs(n2-v),f]};
break;
fi;
if n1>1 then
if abs(round(n1)-n1) <= abs(round(n2)-n2) then
vf := vf union {[abs(v+round(n1)),f]};
break;
else
vf := vf union {[abs(round(n2)-v), f]};
break;
fi;
fi;
vf:=vf union {[v,f]};
fi;
fi;
od;
vf
end:
simplef := proc(flist)
#option trace;
local f,res;
res := {};
for f in flist do
f := evala(Normal(f, expanded));
f := evala(Sqrfree(f,x));
f := f[1]*mul(i[1]^i[2],i=f[2]);
res := res union {f};
od;
res;
end:
searchA1k:=proc(Sreg,dA,d)
#option trace;
local de,s,dr,k,res,temp;
res :=NULL;
if nops(Sreg)=0 then
if irem(dA,d)<>0 then
return {};
else
return {[1,dA/d]};
fi;
fi;
for k from 0 to iquo(dA,d) do
dr :=dA-k*d;
temp :=searchA1(Sreg,dr,d);
for s in temp do res := res, [s,k] od;
od;
{res};
end:
searchA1 :=proc(Sreg,de,d)
#option trace;
local s,res,i,temp,k,t;
res :=NULL;
if nops(Sreg)=0 then
if de=0 then
return 1;
else
return NULL;
fi;
fi;
s :=Sreg[1];
i :=d/denom(s[3]);
if (de-i)<0 then return NULL; fi;
for k from 1 to iquo(de-nops(Sreg)+1,i) do
if i*k>=d then break; fi;
temp := searchA1([seq(Sreg[i],i=2..nops(Sreg))],de-k*i,d);
if temp<>NULL then
for t in temp do
res := res,s[4]^(i*k)*t;
od;
fi;
od;
[res];
end:
findSqrtf := proc(L,Sirr,B,d,knl,ext,x,T)
#option trace;
local res,kn,l,acc,ep, Bs, temp, ser,lp, uk, und,i, j, lr,f, extp, ukn,uki,flist,nlist;
userinfo(3,myname,`Now it is the rational case and the denominator of nu is`, d);
res := {};
if knl <> {} then
for kn in knl do
for i from 1 to nops(Sirr) do
acc := Sirr[i][3][2];
ep := poly_to_powerseries(kn[1],x,Sirr[i][1],T,acc);
ep := pow_reciprocal(ep,T);
Bs := poly_to_powerseries(B,x,Sirr[i][1],T,acc);
temp := pow_time(ep, Bs, T);
ep := poly_to_powerseries(Sirr[i][3][1],x,Sirr[i][1],T,acc);
ser[i] := pow_time(temp, ep,T);
lp := ser[i][1];
und := indets(lp,RootOf) minus ext;
if und={} then
if not assigned(l) then
l:=lp;
else
if length(lp) 0 and Sirr[i][1]<>infinity then error "we made an error" fi;
temp[1] := temp[1]/l;
extp := indets(Sirr[i][1],RootOf) union ext;
ep := dthroot_powseries(temp,T,d,extp);
ukn := NULL;
for uki in {uk} do
temp := poly_to_powerseries( uki,x,Sirr[i][1],T,Sirr[i][3][2]);
for j in ep do
lr := temp[3]-j[3];
lr := coeffs(lr,T);
lr := {lr,temp[1]-j[1]};
lr := map(numer,lr);
if type(Sirr[i][1],RootOf) and not member(Sirr[i][1],ext) then
lr := map(evala@Expand, lr);
lr := map(coeffs, lr, Sirr[i][1])
fi;
lr := SolveTools:-Linear(lr,und);
if lr <> NULL then
f:= subs(op(lr),uki);
ukn := ukn,f;
fi;
od;
od;
uk := ukn;
if uk = NULL then break fi;
od;
userinfo(3,myname,`Compute A_2 equal`,uk);
if uk <> NULL then res := res union {seq(evala(l*i^d*kn[1]/B),i=[uk])} fi;
od;
fi;
res;
end:
# Input:
# i) Two differential operators M1 and M2 and optional the environment d
# ii) Two differential equations M1 and M2 and the indeterminate function d
# Output: A differential operator M3 such that M3(y) is a solution
# of M2 for each solution y of M1
#
# Answer is split into 2 parts.
s_equiv := proc(M1,M2)
local Dx,x,T1,L1,T2,L2,C0,D0,S,C1,D1,d,v,v1,i,w;
if not assigned(_Envdiffopdomain) then
error "domain is not assigned"
fi;
Dx,x := op(_Envdiffopdomain);
if not type([M1,M2],list(polynom(ratpoly(anything,x),Dx))) then
error "wrong arguments or dependent variable not specified"
elif degree(M1,Dx)<>2 or degree(M2,Dx)<>2 then
error "case not handled yet"
fi;
T1:=Normalizer(coeff(M1,Dx,1)/coeff(M1,Dx,2)/2);
L1:=DEtools['symmetric_product'](M1,Dx-T1);
T2:=Normalizer(coeff(M2,Dx,1)/coeff(M2,Dx,2)/2);
L2:=DEtools['symmetric_product'](M2,Dx-T2);
C0:=Normalizer(coeff(L1,Dx,0)/coeff(L1,Dx,2));
D0:=Normalizer(coeff(L2,Dx,0)/coeff(L2,Dx,2));
if not Same_p_curvature(C0,D0,x) then
userinfo(2,dsolveBessel,"case discarded by p-curvature test");
return 0
fi;
D0:=Normalizer(D0-C0);
if D0=0 then
userinfo(1,dsolveBessel,"No gauge transformation needed");
d := 0;
S := 1
else
userinfo(3,dsolveBessel,"Computing gauge transformation");
C1:=Normalizer(diff(C0,x));
D1:=Normalizer(diff(D0,x));
d:=Normalizer(D1/D0);
# v = coeff_list of symmetric_product(L1, L2)
v:=map(Normalizer,[2*diff(C1,x)+diff(D1,x)-2*d*C1-D1*d+D0^2,
6*C1+D1-4*d*C0, 2*(D0+2*C0), -d, 1]);
v, d := RemoveAppSingOrd4(v,x);
# This _Env passes information on apparant singularities
# to the expsols code, which then runs faster:
_Env_DF_sq := table([x, denom(C0)*denom(D0)]);
# The options tell Expsols to only look at exponential
# solutions with generalized exponents in 1/2 * Z (i.e. only
# exponential solutions whose square is a rational function)
v:=EXP_SOLS(v,0,x,`use Int`,`no algext`,radical,denom,2);
if nops(v)=0 then
return 0
elif nops(v)>1 then
userinfo(1,'dsolve',"input is reducible");
return 0
fi;
v1 := `if`(type(v[1],`*`),[op(v[1])],v);
v := 1;
for i in v1 do
if not has(i,x) then next fi;
if type(i,function) and op(0,i)=exp
and type(op(i),function) and op(0,op(i))=Int
and op(2,op(i))=x then
d := d + op(1,op(i))
else
v := v * i
fi
od;
w[0] := Normalizer(v);
for i to 3 do
w[i] := Normalizer(diff(w[i-1],x) + d*w[i-1])
od;
S:=collect(w[0]*Dx +
(w[3]+(4*C0+5*D0)*w[1]+w[0]*(2*C1+D1))/2/D0-2*w[1],
Dx,Normalizer);
S := DEtools['symmetric_product'](S, Dx+T1);
S := evala(Primpart(S, Dx, 'i'));
d := d + diff(i,x)/i;
S := frontend(collect,[S,Dx,factor])
fi;
d := Normalizer(d + T1 - T2);
d, S # This represents the operator exp(Int(d,x)) * S
end:
# Try to remove some apparant singularities we may have introduced.
RemoveAppSingOrd4 := proc(v, x)
local d,i,Dx,L;
d := mul(`if`(i[2]=1,i[1],1),i=sqrfree(denom(v[-2]),x)[2]);
if not has(d,x) then return v, 0 fi;
i := denom(Normalizer(v[-2]-2*diff(d,x)/d));
d := denom(Normalizer(i/d));
if not has(d,x) then return v, 0 fi;
i := 2*v[-3] - 3*(v[-2]*diff(d,x)+2*diff(d,x,x))/d + (3*diff(d,x)/d)^2;
d := denom(Normalizer( denom(Normalizer(i))/d ));
if not has(d,x) then return v, 0 fi;
d := 1/2 * diff(d,x)/d;
L := DEtools['symmetric_product'](add(v[i+1]*Dx^i,i=0..4), Dx-d, [Dx,x]);
[seq(Normalizer(coeff(L,Dx,i)),i=0..4)], -d
end:
# Operators Dx^2 + a, Dx^2 + b are compared mod 3
# If the reduction mod 3 fails, then compare mod 5 instead.
Same_p_curvature := proc(a,b, x)
local v,i,s,s3,T;
if a=b then return true fi;
v := traperror(`mod/ReduceField`([a,b],{x},3));
if v = lasterror then
return Same_5_curvature(args)
fi;
for i to 2 do
s := Normal(v[i]) mod 3;
s3 := subs({seq(j=j^3, j=indets(s, {name,symbol,RootOf}))}, s);
s := Normal(diff(s^2,x,x)) mod 3;
T[i] := s3 + s
od;
evalb(Normal(T[1] - T[2]) mod 3 = 0)
end:
# Operators Dx^2 + a, Dx^2 + b are compared mod 5
Same_5_curvature := proc(a,b, x)
local v,i,s,s5,j,T;
v := traperror(`mod/ReduceField`([a,b],{x},5));
if v = lasterror then
return true
fi;
for i to 2 do
s := Normal(v[i]) mod 5;
s5 := subs({seq(j=j^5, j=indets(s, {name,symbol,RootOf}))}, s);
s := Normal( s * (2*s^2 + diff(s,x,x)) ) mod 5;
for j to 4 do
s := Normal( diff(s,x) ) mod 5
od;
T[i] := s + s5
od;
evalb(Normal(T[1] - T[2]) mod 5 = 0)
end: