Changes http://wiki.axiom-developer.org/RecSpad/diff
--
\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{rec.spad}
\author{Martin Rubey}
\maketitle
\begin{abstract}
The package defined in this file provide an operator for the
n\textsuperscript{th} term of a recurrence and an operator for the
coefficient of $x^n$ in a function specified by a functional equation.
\end{abstract}
\tableofcontents
\section{package RECOP RecurrenceOperator}
<<package RECOP RecurrenceOperator>>=
)abbrev package RECOP RecurrenceOperator
++ Author: Martin Rubey
++ Description:
++ This package provides an operator for the n-th term of a recurrence and an
++ operator for the coefficient of x^n in a function specified by a functional
++ equation.
RecurrenceOperator(R, F): Exports == Implementation where
R: Join(OrderedSet, IntegralDomain, ConvertibleTo InputForm)
F: Join(FunctionSpace R, AbelianMonoid, RetractableTo Integer, _
RetractableTo Symbol, PartialDifferentialRing Symbol)
--RecurrenceOperator(F): Exports == Implementation where
-- F: Join(ExpressionSpace, AbelianMonoid, RetractableTo Integer,
-- RetractableTo Symbol, PartialDifferentialRing Symbol)
Exports == with
evalRec: (BasicOperator, Symbol, F, F, F, List F) -> F
++ \spad{evalRec(u, dummy, n, n0, eq, values)} creates an expression that
++ stands for u(n0), where u(n) is given by the equation eq. However, for
++ technical reasons the variable n has to be replaced by a dummy
++ variable dummy in eq. The argument values specifies the initial values
++ of the recurrence u(0), u(1),...
++ For the moment we don't allow recursions that contain u inside of
++ another operator.
evalADE: (BasicOperator, Symbol, F, F, F, List F) -> F
++ \spad{evalADE(f, dummy, x, n, eq, values)} creates an expression that
++ stands for the coefficient of x^n in f(x), where f(x) is given by the
++ functional equation eq. However, for technical reasons the variable x
++ has to be replaced by a dummy variable dummy in eq. The argument
++ values specifies f(0), f'(0), f''(0),...
-- should be local
numberOfValuesNeeded: (Integer, BasicOperator, Symbol, F) -> Integer
-- should be local
if R has Ring then
getShiftRec: (BasicOperator, Kernel F, Symbol) -> Union(Integer, "failed")
shiftInfoRec: (BasicOperator, Symbol, F) ->
Record(max: Union(Integer, "failed"),
ord: Union(Integer, "failed"),
ker: Kernel F)
Implementation == add
oprecur := operator("rootOfRec"::Symbol)$BasicOperator
opADE := operator("rootOfADE"::Symbol)$BasicOperator
setProperty(oprecur, "%dummyVar", 2 pretend None)
setProperty(opADE, "%dummyVar", 2 pretend None)
-- this implies that the second and third arguments of oprecur are dummy
-- variables and affects tower$ES:
-- the second argument will not appear in tower$ES, if it does not appear in
-- any argument but the first and second.
-- the third argument will not appear in tower$ES, if it does not appear in any
-- other argument. (%defsum is a good example)
-- The arguments of these operators are as follows:
-- 1. eq, i.e. the vanishing expression
eqAsF: List F -> F
eqAsF l == l.1
-- 2. dummy
dummy: List F -> Symbol
dummy l == retract(l.2)@Symbol
dummyAsF: List F -> F
dummyAsF l == l.2
-- 3. variable, i.e., for display
displayVariable: List F -> F
displayVariable l == l.3
-- 4. operatorname(argument)
operatorName: List F -> BasicOperator
operatorName l == operator(kernels(l.4).1)
operatorNameAsF: List F -> F
operatorNameAsF l == l.4
operatorArgument: List F -> F
operatorArgument l == argument(kernels(l.4).1).1
-- rootOfADE: note that although we have arg as argument of the operator,
-- it is intended to indicate the coefficient, not the argument
-- of the power series
-- 5.- values in reversed order
-- rootOfRec:
-- maybe "values" should be preceded by the index of the first given
-- value. Currently, the last value is interpreted as f(0)
-- rootOfADE:
-- values are the first few coefficients of the power series expansion in order
initialValues: List F -> List F
initialValues l == rest(l, 4)
if R has Ring then
getShiftRec(op: BasicOperator, f: Kernel F, n: Symbol)
: Union(Integer, "failed") ==
a := argument f
if every?(freeOf?(#1, n::F), a) then return 0
if #a ~= 1 then error "RECOP: operator should have only one argument"
p := univariate(a.1, retract(n::F)@Kernel(F))
if denominator p ~= 1 then return "failed"
num := numer p
if degree num = 1 and coefficient(num, 1) = 1
and every?(freeOf?(#1, n::F), coefficients num)
then return retractIfCan(coefficient(num, 0))
else return "failed"
shiftInfoRec(op: BasicOperator, argsym: Symbol, eq: F):
Record(max: Union(Integer, "failed"),
ord: Union(Integer, "failed"),
ker: Kernel F) ==
-- ord and ker are valid only if all shifts are Integers
-- ker is the kernel of the maximal shift.
maxShift: Integer
minShift: Integer
nextKernel: Kernel F
-- We consider only those kernels that have op as operator. If there is non, we
-- raise an error. For the moment we don't allow recursions that contain op
-- inside of another operator.
error? := true
for f in kernels eq repeat
if is?(f, op) then
shift := getShiftRec(op, f, argsym)
if error? then
error? := false
nextKernel := f
if shift case Integer then
maxShift := shift
minShift := shift
else return ["failed", "failed", nextKernel]
else
if shift case Integer then
if maxShift < shift then
maxShift := shift
nextKernel := f
if minShift > shift then
minShift := shift
else return ["failed", "failed", nextKernel]
if error? then error "evalRec: equation does not contain operator"
[maxShift, maxShift - minShift, nextKernel]
-- try to evaluate:
evalRec(op, argsym, argdisp, arg, eq, values) ==
if ((n := retractIfCan(arg)@Union(Integer, "failed")) case "failed")
or (n < 0)
then
shiftInfo := shiftInfoRec(op, argsym, eq)
if (shiftInfo.ord case "failed") or ((shiftInfo.ord)::Integer > 0)
then
kernel(oprecur,
append([eq, argsym::F, argdisp, op(arg)], values))
else
p := univariate(eq, shiftInfo.ker)
num := numer p
if degree num = 1 then
eval(-coefficient(num, 0)/coefficient(num, 1), argsym::F, arg::F)
else
kernel(oprecur,
append([eq, argsym::F, argdisp, op(arg)], values))
else
len: Integer := #values
if n < len
then values.(len-n)
else
shiftInfo := shiftInfoRec(op, argsym, eq)
if shiftInfo.max case Integer then
p := univariate(eq, shiftInfo.ker)
num := numer p
if denom p = 1 and degree num = 1 then
next := -coefficient(num, 0)/coefficient(num, 1)
nextval := eval(next, argsym::F,
(len+1-(shiftInfo.max)::Integer)::F)
newval := eval(nextval, op,
evalRec(op, argsym, argdisp, #1, eq, values))
evalRec(op, argsym, argdisp, arg, eq, cons(newval, values))
else
kernel(oprecur,
append([eq, argsym::F, argdisp, op(arg)], values))
else
kernel(oprecur,
append([eq, argsym::F, argdisp, op(arg)], values))
numberOfValuesNeeded(numberOfValues: Integer,
op: BasicOperator, argsym: Symbol, eq: F): Integer ==
order := shiftInfoRec(op, argsym, eq).ord
if order case Integer
then min(numberOfValues, retract(order)@Integer)
else numberOfValues
else
evalRec(op, argsym, argdisp, arg, eq, values) ==
kernel(oprecur,
append([eq, argsym::F, argdisp, op(arg)], values))
numberOfValuesNeeded(numberOfValues: Integer,
op: BasicOperator, argsym: Symbol, eq: F): Integer ==
numberOfValues
irecur: List F -> F
-- This is just a wrapper that allows us to write a recurrence relation as an
-- operator.
irecur l ==
evalRec(operatorName l,
dummy l, displayVariable l,
operatorArgument l, eqAsF l, initialValues l)
evaluate(oprecur, irecur)$BasicOperatorFunctions1(F)
getOrder(op: BasicOperator, f: Kernel F): NonNegativeInteger ==
res: NonNegativeInteger := 0
g := f
while is?(g, %diff) repeat
g := kernels(argument(g).1).1
res := res+1
if is?(g, op) then res else 0
-- try to evaluate:
evalADE(op, argsym, argdisp, arg, eq, values) ==
if not freeOf?(eq, retract(argdisp)@Symbol)
then error "RECOP: The argument should not be used in the equation of the_
ADE"
if ((n := retractIfCan(arg)@Union(Integer, "failed")) case "failed")
then kernel(opADE,
append([eq, argsym::F, argdisp, op(arg)], values))
else
if n < 0
then 0
else
keq := kernels eq
order := getOrder(op, keq.1)
for k in rest keq repeat order := max(order, getOrder(op, k))
p: Fraction SparseUnivariatePolynomial F
:= univariate(eq, kernels(D(op(argsym::F), argsym, order)).1)$F
if degree numer p > 1
then
kernel(opADE,
append([eq, argsym::F, argdisp, op(arg)], values))
else
cen: F := 0$F
s := seriesSolve(eq, op, argsym::F=cen, first(values, order))
$ExpressionSpaceODESolver(R, F)
r := retract(s)
$AnyFunctions1(UnivariateTaylorSeries(F, argsym, cen))
elt(r, n::Integer::NonNegativeInteger)
iADE: List F -> F
-- This is just a wrapper that allows us to write a recurrence relation as an
-- operator.
iADE l ==
evalADE(operatorName l,
dummy l, displayVariable l,
operatorArgument l, eqAsF l, initialValues l)
evaluate(opADE, iADE)$BasicOperatorFunctions1(F)
ddrec: List F -> OutputForm
ddrec l ==
op := operatorName l
values := reverse l
eq := eqAsF l
numberOfValues := numberOfValuesNeeded(#values-4, op, dummy l, eq)
vals: List OutputForm
:= cons(eval(eq, dummyAsF l, displayVariable l)::OutputForm = _
0::OutputForm,
[elt(op::OutputForm, [(i-1)::OutputForm]) = _
(values.i)::OutputForm
for i in 1..numberOfValues])
bracket(hconcat([(operatorNameAsF l)::OutputForm,
": ",
commaSeparate vals]))
setProperty(oprecur, "%specialDisp",
ddrec@(List F -> OutputForm) pretend None)
ddADE: List F -> OutputForm
ddADE l ==
op := operatorName l
values := reverse l
vals: List OutputForm
:= cons(eval(eqAsF l, dummyAsF l, displayVariable l)::OutputForm = _
0::OutputForm,
[eval(D(op(dummyAsF l), dummy l, i), _
dummyAsF l=0)::OutputForm = (values.(i+1))::OutputForm
for i in 0..min(4,#values-5)])
bracket(hconcat([bracket((displayVariable l)::OutputForm ** _
(operatorArgument l)::OutputForm),
(op(displayVariable l))::OutputForm, ": ",
commaSeparate vals]))
setProperty(opADE, "%specialDisp",
ddADE@(List F -> OutputForm) pretend None)
@
%$
\section{License}
<<license>>=
-- Copyright (C) 2006 Martin Rubey <[EMAIL PROTECTED]>
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation; either version 2 of
-- the License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied
-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-- PURPOSE. See the GNU General Public License for more details.
@
<<*>>=
<<license>>
<<package RECOP RecurrenceOperator>>
@
\end{document}
--
forwarded from http://wiki.axiom-developer.org/[EMAIL PROTECTED]