Is there a numerical ODE solver facility in Maxima
Subject: Is there a numerical ODE solver facility in Maxima
From: Michel Talon
Date: Sun, 05 Apr 2009 20:02:05 +0200
Richard Fateman wrote:
> Using a computer algebra system there is another approach, which is to
> solve for the taylor series coefficients in f(x).
> There are various packages to do this, though I don't know of one that
> is set up in Maxima.
>
> A trivial setup (but expensive to run) is to define f, using deftaylor,
> with symbolic coefficients a0, a1, ...
> and then expand f to some order, substitute in the DE, and solve for a0,
> a1, etc.
>
> RJF
The colnew program solves the differential equation by approximating the
solution by splines with indeterminate coefficients, and then imposing the
differential equation at so-called "collocation points", which are similar
to Gauss points in numerical integration. This is proved to give better
convergence. Taylor expansions are most useful at singular points of a
solution, when they exist. In this case it is a criterion for
integrability, known as the Kowalewski-Painlev? analysis. It is trying to
plug such a singular expansion in the top equation that Sophie Kowalewski
found her celebrated integrability case. This sort of thing is very
appropriate for Maxima computations. For example i have done the exercise
here (please be indulgent i don't know all Maxima tricks by far, but at
least this worked for me).
--
Michel Talon
niobe% cat kowa.max
/* The aim is to show that one can find Laurent developments for the
dynamical
* variables p, q, r, etc. of the Kowalevski top, in the cases where the
* determinant of the recursive system has sufficiently many integer roots
*/
tt: elapsed_real_time ();
/* The matrix of the recursive system */
Mat: matrix(
[(m-1)*A, -A1*r, -A1*q, 0, z, -y],
[-B1*r, (m-1)*B, -B1*p, -z, 0, x],
[-C1*q, -C1*p, (m-1)*C, y, -x, 0],
[0, h, -g, m-2, -r, q],
[-h, 0, f, r, m-2, -p],
[g, -f, 0, -q, p, m-2] );
Mat: subst([A1 = B-C, B1 = C-A, C1 = A-B], %)$
/* to get %i^2 = -1 */
algebraic: true$
/* the first Kowalevski solution */
Mat1: subst([B=A, A=2*C, y=0, z=0, h=0, q=%i*p, r=2*%i, f=-2*C/x,
g=-2*%i*C/x] , Mat)$
Det1: factor(determinant(Mat1));
/* The second Kowalevski solution */
Mat2: subst([B=A, A=2*C, y=0, z=0, q=2*%i, r=0, f=-4*C/x,
g=0, h=4*%i*C/x, p=0], Mat)$
Det2: factor(determinant(Mat2));
/* Now we need to solve the recursive equations for the Laurent series */
/* this is a column vector, the unknowns */
v: matrix([p[m]], [q[m]], [r[m]], [f[m]], [g[m]], [h[m]])$
/* note that v[1] = [p[m]] and part(v[1],1) = p[m] */
/* the right hand side of the recursion relations, starting with 0 */
w: [P[m], Q[m], R[m], F[m], G[m], H[m]]$
P[1]: 0$ Q[1]:0$ R[1]: 0$ F[1]: 0$ G[1]: 0$ H[1]: 0$
vars: makelist(part(v[j],1), j, 1, 6)$
print ( "The first Kowalevski case.")$
/* We solve the first Kowalevski case */
eqns: makelist(part((Mat1.v)[j],1) = w[j], j, 1, 6)$
/* solve for m=1 */
solve(subst(m=1, eqns), subst(m=1, vars))$
/* assign the solution to vars, :: evaluates right and left */
subst(m = 1, vars) :: map(rhs, %[1])$
print("First iteration. Vars:", %)$
/* now for m=2,3,4 */
for mm from 2 thru 4 do (
P[mm]: C*sum(q[j]*r[mm-j], j, 1, mm-1),
Q[mm]: -C*sum(p[j]*r[mm-j], j, 1, mm-1),
R[mm]: 0,
F[mm]: sum(r[j]*g[mm-j] - q[j]*h[mm-j], j, 1, mm-1),
G[mm]: sum(p[j]*h[mm-j] - r[j]*f[mm-j], j, 1, mm-1),
H[mm]: sum(q[j]*f[mm-j] - p[j]*g[mm-j], j, 1, mm-1),
solve(ev(subst(m=mm, eqns)), subst(m=mm, vars)),
/* in a block, % has to be replaced by %% */
subst(m=mm, vars) :: map(rhs, %%[1]) )$
/* Display the Laurent solution */
print ( "Laurent solution depending on 5 parameters,
p, %r1, %r2, %r3, %r4")$
[p[0], q[0], r[0], f[0], g[0], h[0]]: [p, %i*p, 2*%i, -2*C/x, -2*%i*C/x, 0]$
for j in [p, q, r] do block([z],
z: fullmap(ev, j[0]/t + j[1] + t*j[2] + t^2*j[3] + t^3*j[4]),
print(j, "(t) = ", expandwrt_factored(z, t, p)),
define(ev(j(t)), z) )$ /* this is for the check below */
for j in [f, g, h] do block([z],
z: fullmap(ev, j[0]/t^2 + j[1]/t + j[2] + t*j[3] + t^2*j[4]),
print(j, "(t) = ", expandwrt_factored(z, t, p)),
define(ev(j(t)), z) )$
print ( "Checking integrals of motion.")$
/* Check that weight has constant magnitude, this is central for Poisson */
taylor(f(t)^2 + g(t)^2 + h(t)^2, t, 0, 0);
/* Notice no terms in 1/t^4, 1/t^3, 1/t^2, 1/t !!! */
/* Check another integral of motion, in the centre of Poisson algebra */
taylor(2*C*(p(t)*f(t)+q(t)*g(t))+C*r(t)*h(t), t, 0, 1);
/* Notice no terms in 1/t^3, 1/t^2, 1/t, t !!! */
/* The phase space is thus or real dimension 4 and not 6. In the integrable
* case there are 2 integrals of motion
*/
/* Check the energy integral of motion */
taylor( C*r(t)^2+2*C*((p(t))^2+(q(t))^2) -2*x*f(t), t, 0, 2);
/* Notice no terms in 1/t^2, 1/t, t, t^2 !!! */
/* Check the new Kowalevski integral of motion */
taylor(
((p(t)+%i*q(t))^2+x*(f(t)+%i*g(t))/C)*((p(t)-%i*q(t))^2+x*(f(t)-%i*g(t))/C),
t, 0, 0);
/* Notice no terms in 1/t^4, 1/t^3, 1/t^2, 1/t !!! */
ttt: elapsed_real_time ()$ ttt -tt;
print ( "Now the second Kowalewski case." )$
/* We solve the second Kowalevski case */
/* Reset variables */
kill(allbut (Mat2, w, ttt))$
v: matrix([p[m]], [q[m]], [r[m]], [f[m]], [g[m]], [h[m]])$
w: [P[m], Q[m], R[m], F[m], G[m], H[m]]$
P[1]: 0$ Q[1]:0$ R[1]: 0$ F[1]: 0$ G[1]: 0$ H[1]: 0$
vars: makelist(part(v[j],1), j, 1, 6)$
eqns: makelist(part((Mat2.v)[j],1) = w[j], j, 1, 6)$
/* solve for m=1 */
solve(subst(m=1, eqns), subst(m=1, vars))$
/* assign the solution to vars, :: evaluates right and left */
subst(m = 1, vars) :: map(rhs, %[1])$
print("First iteration. Vars:", %)$
/* now for m=2,3,4 . Note the determinant vanishes to second order at m=2,
* better find 2 solutions here.
*/
[mm]: -C*sum(p[j]*r[mm-j], j, 1, mm-1),
R[mm]: 0,
F[mm]: sum(r[j]*g[mm-j] - q[j]*h[mm-j], j, 1, mm-1),
G[mm]: sum(p[j]*h[mm-j] - r[j]*f[mm-j], j, 1, mm-1),
H[mm]: sum(q[j]*f[mm-j] - p[j]*g[mm-j], j, 1, mm-1),
solve(ev(subst(m=mm, eqns)), subst(m=mm, vars)),
subst(m=mm, vars) :: map(rhs, %%[1]) )$
/* Display the Laurent solution */
print ( "Laurent solution depending on 4 parameters, %r5, %r6, %r7, %r8")$
[p[0], q[0], r[0], f[0], g[0], h[0]]: [0, 2*%i, 0, -4*C/x, 0, 4*%i*C/x]$
for j in [p, q, r] do block([z],
z: fullmap(ev, j[0]/t + j[1] + t*j[2] + t^2*j[3] + t^3*j[4]),
print(j, "(t) = ", expandwrt_factored(z, t, p)),
define(ev(j(t)), z) )$ /* this is for the check below */
for j in [f, g, h] do block([z],
z: fullmap(ev, j[0]/t^2 + j[1]/t + j[2] + t*j[3] + t^2*j[4]),
print(j, "(t) = ", expandwrt_factored(z, t, p)),
define(ev(j(t)), z) )$
print ( "Checking integrals of motion.")$
taylor(f(t)^2 + g(t)^2 + h(t)^2, t, 0, 0);
taylor(2*C*(p(t)*f(t)+q(t)*g(t))+C*r(t)*h(t), t, 0, 1);
taylor( C*r(t)^2+2*C*((p(t))^2+(q(t))^2) -2*x*f(t), t, 0, 2);
taylor(
((p(t)+%i*q(t))^2+x*(f(t)+%i*g(t))/C)*((p(t)-%i*q(t))^2+x*(f(t)-%i*g(t))/C),
t, 0, 0);
elapsed_real_time () - ttt;