aboutsummaryrefslogtreecommitdiff
path: root/src/algebra/odeef.spad.pamphlet
diff options
context:
space:
mode:
authordos-reis <gdr@axiomatics.org>2007-08-14 05:14:52 +0000
committerdos-reis <gdr@axiomatics.org>2007-08-14 05:14:52 +0000
commitab8cc85adde879fb963c94d15675783f2cf4b183 (patch)
treec202482327f474583b750b2c45dedfc4e4312b1d /src/algebra/odeef.spad.pamphlet
downloadopen-axiom-ab8cc85adde879fb963c94d15675783f2cf4b183.tar.gz
Initial population.
Diffstat (limited to 'src/algebra/odeef.spad.pamphlet')
-rw-r--r--src/algebra/odeef.spad.pamphlet643
1 files changed, 643 insertions, 0 deletions
diff --git a/src/algebra/odeef.spad.pamphlet b/src/algebra/odeef.spad.pamphlet
new file mode 100644
index 00000000..734344a9
--- /dev/null
+++ b/src/algebra/odeef.spad.pamphlet
@@ -0,0 +1,643 @@
+\documentclass{article}
+\usepackage{axiom}
+\begin{document}
+\title{\$SPAD/src/algebra odeef.spad}
+\author{Manuel Bronstein}
+\maketitle
+\begin{abstract}
+\end{abstract}
+\eject
+\tableofcontents
+\eject
+\section{package REDORDER ReductionOfOrder}
+<<package REDORDER ReductionOfOrder>>=
+)abbrev package REDORDER ReductionOfOrder
+++ Author: Manuel Bronstein
+++ Date Created: 4 November 1991
+++ Date Last Updated: 3 February 1994
+++ Description:
+++ \spadtype{ReductionOfOrder} provides
+++ functions for reducing the order of linear ordinary differential equations
+++ once some solutions are known.
+++ Keywords: differential equation, ODE
+ReductionOfOrder(F, L): Exports == Impl where
+ F: Field
+ L: LinearOrdinaryDifferentialOperatorCategory F
+
+ Z ==> Integer
+ A ==> PrimitiveArray F
+
+ Exports ==> with
+ ReduceOrder: (L, F) -> L
+ ++ ReduceOrder(op, s) returns \spad{op1} such that for any solution
+ ++ \spad{z} of \spad{op1 z = 0}, \spad{y = s \int z} is a solution of
+ ++ \spad{op y = 0}. \spad{s} must satisfy \spad{op s = 0}.
+ ReduceOrder: (L, List F) -> Record(eq:L, op:List F)
+ ++ ReduceOrder(op, [f1,...,fk]) returns \spad{[op1,[g1,...,gk]]} such that
+ ++ for any solution \spad{z} of \spad{op1 z = 0},
+ ++ \spad{y = gk \int(g_{k-1} \int(... \int(g1 \int z)...)} is a solution
+ ++ of \spad{op y = 0}. Each \spad{fi} must satisfy \spad{op fi = 0}.
+
+ Impl ==> add
+ ithcoef : (L, Z, A) -> F
+ locals : (A, Z, Z) -> F
+ localbinom: (Z, Z) -> Z
+
+ diff := D()$L
+
+ localbinom(j, i) == (j > i => binomial(j, i+1); 0)
+ locals(s, j, i) == (j > i => qelt(s, j - i - 1); 0)
+
+ ReduceOrder(l:L, sols:List F) ==
+ empty? sols => [l, empty()]
+ neweq := ReduceOrder(l, sol := first sols)
+ rec := ReduceOrder(neweq, [diff(s / sol) for s in rest sols])
+ [rec.eq, concat_!(rec.op, sol)]
+
+ ithcoef(eq, i, s) ==
+ ans:F := 0
+ while eq ^= 0 repeat
+ j := degree eq
+ ans := ans + localbinom(j, i) * locals(s,j,i) * leadingCoefficient eq
+ eq := reductum eq
+ ans
+
+ ReduceOrder(eq:L, sol:F) ==
+ s:A := new(n := degree eq, 0) -- will contain derivatives of sol
+ si := sol -- will run through the derivatives
+ qsetelt_!(s, 0, si)
+ for i in 1..(n-1)::NonNegativeInteger repeat
+ qsetelt_!(s, i, si := diff si)
+ ans:L := 0
+ for i in 0..(n-1)::NonNegativeInteger repeat
+ ans := ans + monomial(ithcoef(eq, i, s), i)
+ ans
+
+@
+\section{package LODEEF ElementaryFunctionLODESolver}
+<<package LODEEF ElementaryFunctionLODESolver>>=
+)abbrev package LODEEF ElementaryFunctionLODESolver
+++ Author: Manuel Bronstein
+++ Date Created: 3 February 1994
+++ Date Last Updated: 9 March 1994
+++ Description:
+++ \spad{ElementaryFunctionLODESolver} provides the top-level
+++ functions for finding closed form solutions of linear ordinary
+++ differential equations and initial value problems.
+++ Keywords: differential equation, ODE
+ElementaryFunctionLODESolver(R, F, L): Exports == Implementation where
+ R: Join(OrderedSet, EuclideanDomain, RetractableTo Integer,
+ LinearlyExplicitRingOver Integer, CharacteristicZero)
+ F: Join(AlgebraicallyClosedFunctionSpace R, TranscendentalFunctionCategory,
+ PrimitiveFunctionCategory)
+ L: LinearOrdinaryDifferentialOperatorCategory F
+
+ SY ==> Symbol
+ N ==> NonNegativeInteger
+ K ==> Kernel F
+ V ==> Vector F
+ M ==> Matrix F
+ UP ==> SparseUnivariatePolynomial F
+ RF ==> Fraction UP
+ UPUP==> SparseUnivariatePolynomial RF
+ P ==> SparseMultivariatePolynomial(R, K)
+ P2 ==> SparseMultivariatePolynomial(P, K)
+ LQ ==> LinearOrdinaryDifferentialOperator1 RF
+ REC ==> Record(particular: F, basis: List F)
+ U ==> Union(REC, "failed")
+ ALGOP ==> "%alg"
+
+ Exports ==> with
+ solve: (L, F, SY) -> U
+ ++ solve(op, g, x) returns either a solution of the ordinary differential
+ ++ equation \spad{op y = g} or "failed" if no non-trivial solution can be
+ ++ found; When found, the solution is returned in the form
+ ++ \spad{[h, [b1,...,bm]]} where \spad{h} is a particular solution and
+ ++ and \spad{[b1,...bm]} are linearly independent solutions of the
+ ++ associated homogenuous equation \spad{op y = 0}.
+ ++ A full basis for the solutions of the homogenuous equation
+ ++ is not always returned, only the solutions which were found;
+ ++ \spad{x} is the dependent variable.
+ solve: (L, F, SY, F, List F) -> Union(F, "failed")
+ ++ solve(op, g, x, a, [y0,...,ym]) returns either the solution
+ ++ of the initial value problem \spad{op y = g, y(a) = y0, y'(a) = y1,...}
+ ++ or "failed" if the solution cannot be found;
+ ++ \spad{x} is the dependent variable.
+
+ Implementation ==> add
+ import Kovacic(F, UP)
+ import ODETools(F, L)
+ import RationalLODE(F, UP)
+ import RationalRicDE(F, UP)
+ import ODEIntegration(R, F)
+ import ConstantLODE(R, F, L)
+ import IntegrationTools(R, F)
+ import ReductionOfOrder(F, L)
+ import ReductionOfOrder(RF, LQ)
+ import PureAlgebraicIntegration(R, F, L)
+ import FunctionSpacePrimitiveElement(R, F)
+ import LinearSystemMatrixPackage(F, V, V, M)
+ import SparseUnivariatePolynomialFunctions2(RF, F)
+ import FunctionSpaceUnivariatePolynomialFactor(R, F, UP)
+ import LinearOrdinaryDifferentialOperatorFactorizer(F, UP)
+ import PolynomialCategoryQuotientFunctions(IndexedExponents K,
+ K, R, P, F)
+
+ upmp : (P, List K) -> P2
+ downmp : (P2, List K, List P) -> P
+ xpart : (F, SY) -> F
+ smpxpart : (P, SY, List K, List P) -> P
+ multint : (F, List F, SY) -> F
+ ulodo : (L, K) -> LQ
+ firstOrder : (F, F, F, SY) -> REC
+ rfSolve : (L, F, K, SY) -> U
+ ratlogsol : (LQ, List RF, K, SY) -> List F
+ expsols : (LQ, K, SY) -> List F
+ homosolve : (L, LQ, List RF, K, SY) -> List F
+ homosolve1 : (L, List F, K, SY) -> List F
+ norf1 : (L, K, SY, N) -> List F
+ kovode : (LQ, K, SY) -> List F
+ doVarParams: (L, F, List F, SY) -> U
+ localmap : (F -> F, L) -> L
+ algSolve : (L, F, K, List K, SY) -> U
+ palgSolve : (L, F, K, K, SY) -> U
+ lastChance : (L, F, SY) -> U
+
+ diff := D()$L
+
+ smpxpart(p, x, l, lp) == downmp(primitivePart upmp(p, l), l, lp)
+ downmp(p, l, lp) == ground eval(p, l, lp)
+ homosolve(lf, op, sols, k, x) == homosolve1(lf, ratlogsol(op,sols,k,x),k,x)
+
+-- left hand side has algebraic (not necessarily pure) coefficients
+ algSolve(op, g, k, l, x) ==
+ symbolIfCan(kx := ksec(k, l, x)) case SY => palgSolve(op, g, kx, k, x)
+ has?(operator kx, ALGOP) =>
+ rec := primitiveElement(kx::F, k::F)
+ z := rootOf(rec.prim)
+ lk:List K := [kx, k]
+ lv:List F := [(rec.pol1) z, (rec.pol2) z]
+ (u := solve(localmap(eval(#1, lk, lv), op), eval(g, lk, lv), x))
+ case "failed" => "failed"
+ rc := u::REC
+ kz := retract(z)@K
+ [eval(rc.particular, kz, rec.primelt),
+ [eval(f, kz, rec.primelt) for f in rc.basis]]
+ lastChance(op, g, x)
+
+ doVarParams(eq, g, bas, x) ==
+ (u := particularSolution(eq, g, bas, int(#1, x))) case "failed" =>
+ lastChance(eq, g, x)
+ [u::F, bas]
+
+ lastChance(op, g, x) ==
+-- one? degree op => firstOrder(coefficient(op,0), leadingCoefficient op,g,x)
+ (degree op) = 1 => firstOrder(coefficient(op,0), leadingCoefficient op,g,x)
+ "failed"
+
+-- solves a0 y + a1 y' = g
+-- does not check whether there is a solution in the field generated by
+-- a0, a1 and g
+ firstOrder(a0, a1, g, x) ==
+ h := xpart(expint(- a0 / a1, x), x)
+ [h * int((g / h) / a1, x), [h]]
+
+-- xpart(f,x) removes any constant not involving x from f
+ xpart(f, x) ==
+ l := reverse_! varselect(tower f, x)
+ lp := [k::P for k in l]
+ smpxpart(numer f, x, l, lp) / smpxpart(denom f, x, l, lp)
+
+ upmp(p, l) ==
+ empty? l => p::P2
+ up := univariate(p, k := first l)
+ l := rest l
+ ans:P2 := 0
+ while up ^= 0 repeat
+ ans := ans + monomial(upmp(leadingCoefficient up, l), k, degree up)
+ up := reductum up
+ ans
+
+-- multint(a, [g1,...,gk], x) returns gk \int(g(k-1) \int(....g1 \int(a))...)
+ multint(a, l, x) ==
+ for g in l repeat a := g * xpart(int(a, x), x)
+ a
+
+ expsols(op, k, x) ==
+-- one? degree op =>
+ (degree op) = 1 =>
+ firstOrder(multivariate(coefficient(op, 0), k),
+ multivariate(leadingCoefficient op, k), 0, x).basis
+ [xpart(expint(multivariate(h, k), x), x) for h in ricDsolve(op, ffactor)]
+
+-- Finds solutions with rational logarithmic derivative
+ ratlogsol(oper, sols, k, x) ==
+ bas := [xpart(multivariate(h, k), x) for h in sols]
+ degree(oper) = #bas => bas -- all solutions are found already
+ rec := ReduceOrder(oper, sols)
+ le := expsols(rec.eq, k, x)
+ int:List(F) := [xpart(multivariate(h, k), x) for h in rec.op]
+ concat_!([xpart(multivariate(h, k), x) for h in sols],
+ [multint(e, int, x) for e in le])
+
+ homosolve1(oper, sols, k, x) ==
+ zero?(n := (degree(oper) - #sols)::N) => sols -- all solutions found
+ rec := ReduceOrder(oper, sols)
+ int:List(F) := [xpart(h, x) for h in rec.op]
+ concat_!(sols, [multint(e, int, x) for e in norf1(rec.eq, k, x, n::N)])
+
+-- if the coefficients are rational functions, then the equation does not
+-- not have a proper 1st-order right factor over the rational functions
+ norf1(op, k, x, n) ==
+-- one? n => firstOrder(coefficient(op, 0), leadingCoefficient op,0,x).basis
+ (n = 1) => firstOrder(coefficient(op, 0), leadingCoefficient op,0,x).basis
+-- for order > 2, we check that the coeffs are still rational functions
+ symbolIfCan(kmax vark(coefficients op, x)) case SY =>
+ eq := ulodo(op, k)
+ n = 2 => kovode(eq, k, x)
+ eq := last factor1 eq -- eq cannot have order 1
+ degree(eq) = 2 =>
+ empty?(bas := kovode(eq, k, x)) => empty()
+ homosolve1(op, bas, k, x)
+ empty()
+ empty()
+
+ kovode(op, k, x) ==
+ b := coefficient(op, 1)
+ a := coefficient(op, 2)
+ (u := kovacic(coefficient(op, 0), b, a, ffactor)) case "failed" => empty()
+ p := map(multivariate(#1, k), u::UPUP)
+ ba := multivariate(- b / a, k)
+-- if p has degree 2 (case 2), then it must be squarefree since the
+-- ode is irreducible over the rational functions, so the 2 roots of p
+-- are distinct and must yield 2 independent solutions.
+ degree(p) = 2 => [xpart(expint(ba/(2::F) + e, x), x) for e in zerosOf p]
+-- otherwise take 1 root of p and find the 2nd solution by reduction of order
+ y1 := xpart(expint(ba / (2::F) + zeroOf p, x), x)
+ [y1, y1 * xpart(int(expint(ba, x) / y1**2, x), x)]
+
+ solve(op:L, g:F, x:SY) ==
+ empty?(l := vark(coefficients op, x)) => constDsolve(op, g, x)
+ symbolIfCan(k := kmax l) case SY => rfSolve(op, g, k, x)
+ has?(operator k, ALGOP) => algSolve(op, g, k, l, x)
+ lastChance(op, g, x)
+
+ ulodo(eq, k) ==
+ op:LQ := 0
+ while eq ^= 0 repeat
+ op := op + monomial(univariate(leadingCoefficient eq, k), degree eq)
+ eq := reductum eq
+ op
+
+-- left hand side has rational coefficients
+ rfSolve(eq, g, k, x) ==
+ op := ulodo(eq, k)
+ empty? remove_!(k, varselect(kernels g, x)) => -- i.e. rhs is rational
+ rc := ratDsolve(op, univariate(g, k))
+ rc.particular case "failed" => -- this implies g ^= 0
+ doVarParams(eq, g, homosolve(eq, op, rc.basis, k, x), x)
+ [multivariate(rc.particular::RF, k), homosolve(eq, op, rc.basis, k, x)]
+ doVarParams(eq, g, homosolve(eq, op, ratDsolve(op, 0).basis, k, x), x)
+
+ solve(op, g, x, a, y0) ==
+ (u := solve(op, g, x)) case "failed" => "failed"
+ hp := h := (u::REC).particular
+ b := (u::REC).basis
+ v:V := new(n := #y0, 0)
+ kx:K := kernel x
+ for i in minIndex v .. maxIndex v for yy in y0 repeat
+ v.i := yy - eval(h, kx, a)
+ h := diff h
+ (sol := particularSolution(map_!(eval(#1,kx,a),wronskianMatrix(b,n)), v))
+ case "failed" => "failed"
+ for f in b for i in minIndex(s := sol::V) .. repeat
+ hp := hp + s.i * f
+ hp
+
+ localmap(f, op) ==
+ ans:L := 0
+ while op ^= 0 repeat
+ ans := ans + monomial(f leadingCoefficient op, degree op)
+ op := reductum op
+ ans
+
+-- left hand side has pure algebraic coefficients
+ palgSolve(op, g, kx, k, x) ==
+ rec := palgLODE(op, g, kx, k, x) -- finds solutions in the coef. field
+ rec.particular case "failed" =>
+ doVarParams(op, g, homosolve1(op, rec.basis, k, x), x)
+ [(rec.particular)::F, homosolve1(op, rec.basis, k, x)]
+
+@
+\section{package ODEEF ElementaryFunctionODESolver}
+<<package ODEEF ElementaryFunctionODESolver>>=
+)abbrev package ODEEF ElementaryFunctionODESolver
+++ Author: Manuel Bronstein
+++ Date Created: 18 March 1991
+++ Date Last Updated: 8 March 1994
+++ Description:
+++ \spad{ElementaryFunctionODESolver} provides the top-level
+++ functions for finding closed form solutions of ordinary
+++ differential equations and initial value problems.
+++ Keywords: differential equation, ODE
+ElementaryFunctionODESolver(R, F): Exports == Implementation where
+ R: Join(OrderedSet, EuclideanDomain, RetractableTo Integer,
+ LinearlyExplicitRingOver Integer, CharacteristicZero)
+ F: Join(AlgebraicallyClosedFunctionSpace R, TranscendentalFunctionCategory,
+ PrimitiveFunctionCategory)
+
+ N ==> NonNegativeInteger
+ OP ==> BasicOperator
+ SY ==> Symbol
+ K ==> Kernel F
+ EQ ==> Equation F
+ V ==> Vector F
+ M ==> Matrix F
+ UP ==> SparseUnivariatePolynomial F
+ P ==> SparseMultivariatePolynomial(R, K)
+ LEQ ==> Record(left:UP, right:F)
+ NLQ ==> Record(dx:F, dy:F)
+ REC ==> Record(particular: F, basis: List F)
+ VEC ==> Record(particular: V, basis: List V)
+ ROW ==> Record(index: Integer, row: V, rh: F)
+ SYS ==> Record(mat:M, vec: V)
+ U ==> Union(REC, F, "failed")
+ UU ==> Union(F, "failed")
+ OPDIFF ==> "%diff"::SY
+
+ Exports ==> with
+ solve: (M, V, SY) -> Union(VEC, "failed")
+ ++ solve(m, v, x) returns \spad{[v_p, [v_1,...,v_m]]} such that
+ ++ the solutions of the system \spad{D y = m y + v} are
+ ++ \spad{v_p + c_1 v_1 + ... + c_m v_m} where the \spad{c_i's} are
+ ++ constants, and the \spad{v_i's} form a basis for the solutions of
+ ++ \spad{D y = m y}.
+ ++ \spad{x} is the dependent variable.
+ solve: (M, SY) -> Union(List V, "failed")
+ ++ solve(m, x) returns a basis for the solutions of \spad{D y = m y}.
+ ++ \spad{x} is the dependent variable.
+ solve: (List EQ, List OP, SY) -> Union(VEC, "failed")
+ ++ solve([eq_1,...,eq_n], [y_1,...,y_n], x) returns either "failed"
+ ++ or, if the equations form a fist order linear system, a solution
+ ++ of the form \spad{[y_p, [b_1,...,b_n]]} where \spad{h_p} is a
+ ++ particular solution and \spad{[b_1,...b_m]} are linearly independent
+ ++ solutions of the associated homogenuous system.
+ ++ error if the equations do not form a first order linear system
+ solve: (List F, List OP, SY) -> Union(VEC, "failed")
+ ++ solve([eq_1,...,eq_n], [y_1,...,y_n], x) returns either "failed"
+ ++ or, if the equations form a fist order linear system, a solution
+ ++ of the form \spad{[y_p, [b_1,...,b_n]]} where \spad{h_p} is a
+ ++ particular solution and \spad{[b_1,...b_m]} are linearly independent
+ ++ solutions of the associated homogenuous system.
+ ++ error if the equations do not form a first order linear system
+ solve: (EQ, OP, SY) -> U
+ ++ solve(eq, y, x) returns either a solution of the ordinary differential
+ ++ equation \spad{eq} or "failed" if no non-trivial solution can be found;
+ ++ If the equation is linear ordinary, a solution is of the form
+ ++ \spad{[h, [b1,...,bm]]} where \spad{h} is a particular solution
+ ++ and \spad{[b1,...bm]} are linearly independent solutions of the
+ ++ associated homogenuous equation \spad{f(x,y) = 0};
+ ++ A full basis for the solutions of the homogenuous equation
+ ++ is not always returned, only the solutions which were found;
+ ++ If the equation is of the form {dy/dx = f(x,y)}, a solution is of
+ ++ the form \spad{h(x,y)} where \spad{h(x,y) = c} is a first integral
+ ++ of the equation for any constant \spad{c};
+ ++ error if the equation is not one of those 2 forms;
+ solve: (F, OP, SY) -> U
+ ++ solve(eq, y, x) returns either a solution of the ordinary differential
+ ++ equation \spad{eq} or "failed" if no non-trivial solution can be found;
+ ++ If the equation is linear ordinary, a solution is of the form
+ ++ \spad{[h, [b1,...,bm]]} where \spad{h} is a particular solution and
+ ++ and \spad{[b1,...bm]} are linearly independent solutions of the
+ ++ associated homogenuous equation \spad{f(x,y) = 0};
+ ++ A full basis for the solutions of the homogenuous equation
+ ++ is not always returned, only the solutions which were found;
+ ++ If the equation is of the form {dy/dx = f(x,y)}, a solution is of
+ ++ the form \spad{h(x,y)} where \spad{h(x,y) = c} is a first integral
+ ++ of the equation for any constant \spad{c};
+ solve: (EQ, OP, EQ, List F) -> UU
+ ++ solve(eq, y, x = a, [y0,...,ym]) returns either the solution
+ ++ of the initial value problem \spad{eq, y(a) = y0, y'(a) = y1,...}
+ ++ or "failed" if the solution cannot be found;
+ ++ error if the equation is not one linear ordinary or of the form
+ ++ \spad{dy/dx = f(x,y)};
+ solve: (F, OP, EQ, List F) -> UU
+ ++ solve(eq, y, x = a, [y0,...,ym]) returns either the solution
+ ++ of the initial value problem \spad{eq, y(a) = y0, y'(a) = y1,...}
+ ++ or "failed" if the solution cannot be found;
+ ++ error if the equation is not one linear ordinary or of the form
+ ++ \spad{dy/dx = f(x,y)};
+
+ Implementation ==> add
+ import ODEIntegration(R, F)
+ import IntegrationTools(R, F)
+ import NonLinearFirstOrderODESolver(R, F)
+
+ getfreelincoeff : (F, K, SY) -> F
+ getfreelincoeff1: (F, K, List F) -> F
+ getlincoeff : (F, K) -> F
+ getcoeff : (F, K) -> UU
+ parseODE : (F, OP, SY) -> Union(LEQ, NLQ)
+ parseLODE : (F, List K, UP, SY) -> LEQ
+ parseSYS : (List F, List OP, SY) -> Union(SYS, "failed")
+ parseSYSeq : (F, List K, List K, List F, SY) -> Union(ROW, "failed")
+
+ solve(diffeq:EQ, y:OP, x:SY) == solve(lhs diffeq - rhs diffeq, y, x)
+
+ solve(leq: List EQ, lop: List OP, x:SY) ==
+ solve([lhs eq - rhs eq for eq in leq], lop, x)
+
+ solve(diffeq:EQ, y:OP, center:EQ, y0:List F) ==
+ solve(lhs diffeq - rhs diffeq, y, center, y0)
+
+ solve(m:M, x:SY) ==
+ (u := solve(m, new(nrows m, 0), x)) case "failed" => "failed"
+ u.basis
+
+ solve(m:M, v:V, x:SY) ==
+ Lx := LinearOrdinaryDifferentialOperator(F, diff x)
+ uu := solve(m, v, solve(#1, #2,
+ x)$ElementaryFunctionLODESolver(R, F, Lx))$SystemODESolver(F, Lx)
+ uu case "failed" => "failed"
+ rec := uu::Record(particular: V, basis: M)
+ [rec.particular, [column(rec.basis, i) for i in 1..ncols(rec.basis)]]
+
+ solve(diffeq:F, y:OP, center:EQ, y0:List F) ==
+ a := rhs center
+ kx:K := kernel(x := retract(lhs(center))@SY)
+ (ur := parseODE(diffeq, y, x)) case NLQ =>
+-- not one?(#y0) => error "solve: more than one initial condition!"
+ not ((#y0) = 1) => error "solve: more than one initial condition!"
+ rc := ur::NLQ
+ (u := solve(rc.dx, rc.dy, y, x)) case "failed" => "failed"
+ u::F - eval(u::F, [kx, retract(y(x::F))@K], [a, first y0])
+ rec := ur::LEQ
+ p := rec.left
+ Lx := LinearOrdinaryDifferentialOperator(F, diff x)
+ op:Lx := 0
+ while p ^= 0 repeat
+ op := op + monomial(leadingCoefficient p, degree p)
+ p := reductum p
+ solve(op, rec.right, x, a, y0)$ElementaryFunctionLODESolver(R, F, Lx)
+
+ solve(leq: List F, lop: List OP, x:SY) ==
+ (u := parseSYS(leq, lop, x)) case SYS =>
+ rec := u::SYS
+ solve(rec.mat, rec.vec, x)
+ error "solve: not a first order linear system"
+
+ solve(diffeq:F, y:OP, x:SY) ==
+ (u := parseODE(diffeq, y, x)) case NLQ =>
+ rc := u::NLQ
+ (uu := solve(rc.dx, rc.dy, y, x)) case "failed" => "failed"
+ uu::F
+ rec := u::LEQ
+ p := rec.left
+ Lx := LinearOrdinaryDifferentialOperator(F, diff x)
+ op:Lx := 0
+ while p ^= 0 repeat
+ op := op + monomial(leadingCoefficient p, degree p)
+ p := reductum p
+ (uuu := solve(op, rec.right, x)$ElementaryFunctionLODESolver(R, F, Lx))
+ case "failed" => "failed"
+ uuu::REC
+
+-- returns [M, v] s.t. the equations are D x = M x + v
+ parseSYS(eqs, ly, x) ==
+ (n := #eqs) ^= #ly => "failed"
+ m:M := new(n, n, 0)
+ v:V := new(n, 0)
+ xx := x::F
+ lf := [y xx for y in ly]
+ lk0:List(K) := [retract(f)@K for f in lf]
+ lk1:List(K) := [retract(differentiate(f, x))@K for f in lf]
+ for eq in eqs repeat
+ (u := parseSYSeq(eq,lk0,lk1,lf,x)) case "failed" => return "failed"
+ rec := u::ROW
+ setRow_!(m, rec.index, rec.row)
+ v(rec.index) := rec.rh
+ [m, v]
+
+ parseSYSeq(eq, l0, l1, lf, x) ==
+ l := [k for k in varselect(kernels eq, x) | is?(k, OPDIFF)]
+ empty? l or not empty? rest l or zero?(n := position(k := first l,l1)) =>
+ "failed"
+ c := getfreelincoeff1(eq, k, lf)
+ eq := eq - c * k::F
+ v:V := new(#l0, 0)
+ for y in l0 for i in 1.. repeat
+ ci := getfreelincoeff1(eq, y, lf)
+ v.i := - ci / c
+ eq := eq - ci * y::F
+ [n, v, -eq]
+
+-- returns either [p, g] where the equation (diffeq) is of the form p(D)(y) = g
+-- or [p, q] such that the equation (diffeq) is of the form p dx + q dy = 0
+ parseODE(diffeq, y, x) ==
+ f := y(x::F)
+ l:List(K) := [retract(f)@K]
+ n:N := 2
+ for k in varselect(kernels diffeq, x) | is?(k, OPDIFF) repeat
+ if (m := height k) > n then n := m
+ n := (n - 2)::N
+-- build a list of kernels in the order [y^(n)(x),...,y''(x),y'(x),y(x)]
+ for i in 1..n repeat
+ l := concat(retract(f := differentiate(f, x))@K, l)
+ k:K -- #$^#& compiler requires this line and the next one too...
+ c:F
+ while not(empty? l) and zero?(c := getlincoeff(diffeq, k := first l))
+ repeat l := rest l
+ empty? l or empty? rest l => error "parseODE: equation has order 0"
+ diffeq := diffeq - c * (k::F)
+ ny := name y
+ l := rest l
+ height(k) > 3 => parseLODE(diffeq, l, monomial(c, #l), ny)
+ (u := getcoeff(diffeq, k := first l)) case "failed" => [diffeq, c]
+ eqrhs := (d := u::F) * (k::F) - diffeq
+ freeOf?(eqrhs, ny) and freeOf?(c, ny) and freeOf?(d, ny) =>
+ [monomial(c, 1) + d::UP, eqrhs]
+ [diffeq, c]
+
+-- returns [p, g] where the equation (diffeq) is of the form p(D)(y) = g
+ parseLODE(diffeq, l, p, y) ==
+ not freeOf?(leadingCoefficient p, y) =>
+ error "parseLODE: not a linear ordinary differential equation"
+ d := degree(p)::Integer - 1
+ for k in l repeat
+ p := p + monomial(c := getfreelincoeff(diffeq, k, y), d::N)
+ d := d - 1
+ diffeq := diffeq - c * (k::F)
+ freeOf?(diffeq, y) => [p, - diffeq]
+ error "parseLODE: not a linear ordinary differential equation"
+
+ getfreelincoeff(f, k, y) ==
+ freeOf?(c := getlincoeff(f, k), y) => c
+ error "getfreelincoeff: not a linear ordinary differential equation"
+
+ getfreelincoeff1(f, k, ly) ==
+ c := getlincoeff(f, k)
+ for y in ly repeat
+ not freeOf?(c, y) =>
+ error "getfreelincoeff: not a linear ordinary differential equation"
+ c
+
+ getlincoeff(f, k) ==
+ (u := getcoeff(f, k)) case "failed" =>
+ error "getlincoeff: not an appropriate ordinary differential equation"
+ u::F
+
+ getcoeff(f, k) ==
+ (r := retractIfCan(univariate(denom f, k))@Union(P, "failed"))
+ case "failed" or degree(p := univariate(numer f, k)) > 1 => "failed"
+ coefficient(p, 1) / (r::P)
+
+@
+\section{License}
+<<license>>=
+--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
+--All rights reserved.
+--
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are
+--met:
+--
+-- - Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+--
+-- - Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in
+-- the documentation and/or other materials provided with the
+-- distribution.
+--
+-- - Neither the name of The Numerical ALgorithms Group Ltd. nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+--
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+@
+<<*>>=
+<<license>>
+
+-- Compile order for the differential equation solver:
+-- oderf.spad odealg.spad nlode.spad nlinsol.spad riccati.spad
+-- kovacic.spad lodof.spad odeef.spad
+
+<<package REDORDER ReductionOfOrder>>
+<<package LODEEF ElementaryFunctionLODESolver>>
+<<package ODEEF ElementaryFunctionODESolver>>
+@
+\eject
+\begin{thebibliography}{99}
+\bibitem{1} nothing
+\end{thebibliography}
+\end{document}