/usr/share/axiom-20170501/src/algebra/NODE1.spad is in axiom-source 20170501-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | )abbrev package NODE1 NonLinearFirstOrderODESolver
++ Author: Manuel Bronstein
++ Date Created: 2 September 1991
++ Date Last Updated: 14 October 1994
++ Description:
++ NonLinearFirstOrderODESolver provides a function
++ for finding closed form first integrals of nonlinear ordinary
++ differential equations of order 1.
NonLinearFirstOrderODESolver(R, F) : SIG == CODE where
R: Join(OrderedSet, EuclideanDomain, RetractableTo Integer,
LinearlyExplicitRingOver Integer, CharacteristicZero)
F: Join(AlgebraicallyClosedFunctionSpace R, TranscendentalFunctionCategory,
PrimitiveFunctionCategory)
N ==> NonNegativeInteger
Q ==> Fraction Integer
UQ ==> Union(Q, "failed")
OP ==> BasicOperator
SY ==> Symbol
K ==> Kernel F
U ==> Union(F, "failed")
P ==> SparseMultivariatePolynomial(R, K)
REC ==> Record(coef:Q, logand:F)
SOL ==> Record(particular: F,basis: List F)
BER ==> Record(coef1:F, coefn:F, exponent:N)
SIG ==> with
solve : (F, F, OP, SY) -> U
++ solve(M(x,y), N(x,y), y, x) returns \spad{F(x,y)} such that
++ \spad{F(x,y) = c} for a constant \spad{c} is a first integral
++ of the equation \spad{M(x,y) dx + N(x,y) dy = 0}, or
++ "failed" if no first-integral can be found.
CODE ==> add
import ODEIntegration(R, F)
import ElementaryFunctionODESolver(R, F) -- recursive dependency!
checkBernoulli : (F, F, K) -> Union(BER, "failed")
solveBernoulli : (BER, OP, SY, F) -> Union(F, "failed")
checkRiccati : (F, F, K) -> Union(List F, "failed")
solveRiccati : (List F, OP, SY, F) -> Union(F, "failed")
partSolRiccati : (List F, OP, SY, F) -> Union(F, "failed")
integratingFactor: (F, F, SY, SY) -> U
unk := new()$SY
kunk:K := kernel unk
solve(m, n, y, x) ==
-- first replace the operator y(x) by a new symbol z in m(x,y) and n(x,y)
lk:List(K) := [retract(yx := y(x::F))@K]
lv:List(F) := [kunk::F]
mm := eval(m, lk, lv)
nn := eval(n, lk, lv)
-- put over a common denominator (to balance m and n)
d := lcm(denom mm, denom nn)::F
mm := d * mm
nn := d * nn
-- look for an integrating factor mu
(u := integratingFactor(mm, nn, unk, x)) case F =>
mu := u::F
mm := mm * mu
nn := nn * mu
eval(int(mm,x) + int(nn-int(differentiate(mm,unk),x), unk),[kunk],[yx])
-- check for Bernoulli equation
(w := checkBernoulli(m, n, k1 := first lk)) case BER =>
solveBernoulli(w::BER, y, x, yx)
-- check for Riccati equation
(v := checkRiccati(m, n, k1)) case List(F) =>
solveRiccati(v::List(F), y, x, yx)
"failed"
-- look for an integrating factor
integratingFactor(m, n, y, x) ==
-- check first for exactness
zero?(d := differentiate(m, y) - differentiate(n, x)) => 1
-- look for an integrating factor involving x only
not member?(y, variables(f := d / n)) => expint(f, x)
-- look for an integrating factor involving y only
not member?(x, variables(f := - d / m)) => expint(f, y)
-- room for more techniques later on (for example Prelle-Singer)
"failed"
-- check whether the equation is of the form
-- dy/dx + p(x)y + q(x)y^N = 0 with N > 1
-- whether m/n is of the form p(x) y + q(x) y^N
-- returns [p, q, N] if the equation is in that form
checkBernoulli(m, n, ky) ==
r := denom(f := m / n)::F
(not freeOf?(r, y := ky::F))
or (d := degree(p := univariate(numer f, ky))) < 2
or degree(pp := reductum p) ^= 1 or reductum(pp) ^= 0
or (not freeOf?(a := (leadingCoefficient(pp)::F), y))
or (not freeOf?(b := (leadingCoefficient(p)::F), y)) => "failed"
[a / r, b / r, d]
-- solves the equation dy/dx + rec.coef1 y + rec.coefn y^rec.exponent = 0
-- the change of variable v = y^{1-n} transforms the above equation to
-- dv/dx + (1 - n) p v + (1 - n) q = 0
solveBernoulli(rec, y, x, yx) ==
n1 := 1 - rec.exponent::Integer
deq := differentiate(yx, x) + n1 * rec.coef1 * yx + n1 * rec.coefn
sol := solve(deq, y, x)::SOL -- can always solve for order 1
-- if v = vp + c v0 is the general solution of the linear equation, then
-- the general first integral for the Bernoulli equation is
-- (y^{1-n} - vp) / v0 = c for any constant c
(yx**n1 - sol.particular) / first(sol.basis)
-- check whether the equation is of the form
-- dy/dx + q0(x) + q1(x)y + q2(x)y^2 = 0
-- whether m/n is a quadratic polynomial in y.
-- returns the list [q0, q1, q2] if the equation is in that form
checkRiccati(m, n, ky) ==
q := denom(f := m / n)::F
(not freeOf?(q, y := ky::F)) or degree(p := univariate(numer f, ky)) > 2
or (not freeOf?(a0 := (coefficient(p, 0)::F), y))
or (not freeOf?(a1 := (coefficient(p, 1)::F), y))
or (not freeOf?(a2 := (coefficient(p, 2)::F), y)) => "failed"
[a0 / q, a1 / q, a2 / q]
-- solves the equation dy/dx + l.1 + l.2 y + l.3 y^2 = 0
solveRiccati(l, y, x, yx) ==
-- get first a particular solution
(u := partSolRiccati(l, y, x, yx)) case "failed" => "failed"
-- once a particular solution yp is known, the general solution is of the
-- form y = yp + 1/v where v satisfies the linear 1st order equation
-- v' - (l.2 + 2 l.3 yp) v = l.3
deq := differentiate(yx, x) - (l.2 + 2 * l.3 * u::F) * yx - l.3
gsol := solve(deq, y, x)::SOL -- can always solve for order 1
-- if v = vp + c v0 is the general solution of the above equation, then
-- the general first integral for the Riccati equation is
-- (1/(y - yp) - vp) / v0 = c for any constant c
(inv(yx - u::F) - gsol.particular) / first(gsol.basis)
-- looks for a particular solution of dy/dx + l.1 + l.2 y + l.3 y^2 = 0
partSolRiccati(l, y, x, yx) ==
-- we first do the change of variable y = z / l.3, which transforms
-- the equation into dz/dx + l.1 l.3 + (l.2 - l.3'/l.3) z + z^2 = 0
q0 := l.1 * (l3 := l.3)
q1 := l.2 - differentiate(l3, x) / l3
-- the equation dz/dx + q0 + q1 z + z^2 = 0 is transformed by the change
-- of variable z = w'/w into the linear equation w'' + q1 w' + q0 w = 0
lineq := differentiate(yx, x, 2) + q1 * differentiate(yx, x) + q0 * yx
-- should be made faster by requesting a particular nonzero solution only
(not((gsol := solve(lineq, y, x)) case SOL))
or empty?(bas := (gsol::SOL).basis) => "failed"
differentiate(first bas, x) / (l3 * first bas)
|