Changes http://wiki.axiom-developer.org/FffgSpad/diff
--
\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{fffg.spad}
\author{Martin Rubey}
\maketitle
\begin{abstract}
The packages defined in this file provide fast fraction free rational
interpolation algorithms.
\end{abstract}
\tableofcontents
\section{package FAMR2 FiniteAbelianMonoidRingFunctions2}
<<package FAMR2 FiniteAbelianMonoidRingFunctions2>>=
)abbrev package FAMR2 FiniteAbelianMonoidRingFunctions2
++ Author: Martin Rubey
++ Description:
++ This package provides a mapping function for
\spadtype{FiniteAbelianMonoidRing}
FiniteAbelianMonoidRingFunctions2(E: OrderedAbelianMonoid,
R1: Ring,
A1: FiniteAbelianMonoidRing(R1, E),
R2: Ring,
A2: FiniteAbelianMonoidRing(R2, E))
: Exports == Implementation where
Exports == with
map: (R1 -> R2, A1) -> A2
Implementation == add
-- we assume that 0 is mapped onto 0
map(f: R1 -> R2, a: A1): A2 ==
if zero? a then 0$A2
else
monomial(f leadingCoefficient a, degree a)$A2 + map(f, reductum a)
@
\section{package FFFG FractionFreeFastGaussian}
<<package FFFG FractionFreeFastGaussian>>=
)abbrev package FFFG FractionFreeFastGaussian
++ Author: Martin Rubey
++ Description:
++ This package implements the interpolation algorithm proposed in Beckermann,
++ Bernhard and Labahn, George, Fraction-free computation of matrix rational
++ interpolants and matrix GCDs, SIAM Journal on Matrix Analysis and
++ Applications 22.
FractionFreeFastGaussian(D, V): Exports == Implementation where
D: Join(IntegralDomain, GcdDomain)
V: AbelianMonoidRing(D, NonNegativeInteger)
SUP ==> SparseUnivariatePolynomial
cFunction ==> (NonNegativeInteger, Vector SUP D) -> D
CoeffAction ==> (NonNegativeInteger, NonNegativeInteger, V) -> D
-- coeffAction(k, l, f) is the coefficient of x^k in z^l f(x)
Exports == with
fffg: (List D, cFunction, List NonNegativeInteger) -> Matrix SUP D
++ \spad{fffg} is the general algorithm as proposed by Beckermann and
++ Labahn.
-- c(k, M) computes $c_k(M)$ as in Equation (2). Note that the information
-- about $f$ is encoded in $c$.
interpolate: (List D, List D, NonNegativeInteger) -> Fraction SUP D
interpolate: (List Fraction D, List Fraction D, NonNegativeInteger)
-> Fraction SUP D
generalInterpolation: (List D, CoeffAction,
Vector V, List NonNegativeInteger) -> Matrix SUP D
++ \spad{generalInterpolation(l, CA, f, eta)} performs Hermite-Pade
++ approximation using the given action CA of polynomials on the elements
++ of f. The result is guaranteed to be correct up to order
++ |eta|-1. Given that eta is a "normal" point, the degrees on the
++ diagonal are given by eta. The degrees of column i are in this case
++ eta + e.i - [1,1,...,1], where the degree of zero is -1.
generalInterpolation: (List D, CoeffAction,
Vector V, NonNegativeInteger, NonNegativeInteger)
-> Stream Matrix SUP D
++ \spad{generalInterpolation(l, CA, f, totalDegree, maxDegree)} applies
++ \spad{generalInterpolation(l, CA, f, eta)} for all possible \spad{eta}
++ with maximal entry \spad{maxDegree} and sum of entries
++ \spad{totalDegree}.
generalCoefficient: (CoeffAction, Vector V,
NonNegativeInteger, Vector SUP D) -> D
++ \spad{generalCoefficient(action, f, k, p)} gives the coefficient of
++ x^k in p(z)\dot f(x), where the action of z^l on a polynomial in x is
++ given by action.
ShiftAction: (NonNegativeInteger, NonNegativeInteger, V) -> D
++ \spad{ShiftAction(k, l, g)} gives the coefficient of x^k in z^l g(x),
++ where \spad{z*(a+b*x+c*x^2+d*x^3+...) = (b*x+2*c*x^2+3*d*x^3+...)}. In
++ terms of sequences, z*u(n)=n*u(n).
ShiftC: NonNegativeInteger -> List D
++ \spad{ShiftC} gives the coefficients c_{k,k} in the expansion <x^k> z
++ g(x) = sum_{i=0}^k c_{k,i} <x^i> g(x), where z acts on g(x) by
++ shifting. In fact, the result is [0,1,2,...]
DiffAction: (NonNegativeInteger, NonNegativeInteger, V) -> D
++ \spad{DiffAction(k, l, g)} gives the coefficient of x^k in z^l g(x),
++ where z*(a+b*x+c*x^2+d*x^3+...) = (a*x+b*x^2+c*x^3+...).
DiffC: NonNegativeInteger -> List D
++ \spad{DiffC} gives the coefficients c_{k,k} in the expansion <x^k> z
++ g(x) = sum_{i=0}^k c_{k,i} <x^i> g(x), where z acts on g(x) by
++ shifting. In fact, the result is [0,0,0,...]
qShiftAction: (D, NonNegativeInteger, NonNegativeInteger, V) -> D
++ \spad{qShiftAction(q, k, l, g)} gives the coefficient of x^k in z^l
++ g(x), where z*(a+b*x+c*x^2+d*x^3+...) =
++ (a+q*b*x+q^2*c*x^2+q^3*d*x^3+...). In terms of sequences,
++ z*u(n)=q^n*u(n).
qShiftC: (D, NonNegativeInteger) -> List D
++ \spad{qShiftC} gives the coefficients c_{k,k} in the expansion <x^k> z
++ g(x) = sum_{i=0}^k c_{k,i} <x^i> g(x), where z acts on g(x) by
++ shifting. In fact, the result is [1,q,q^2,...]
Implementation ==> add
-------------------------------------------------------------------------------
-- Shift Operator
-------------------------------------------------------------------------------
-- ShiftAction(k, l, f) is the CoeffAction appropriate for the shift operator.
ShiftAction(k: NonNegativeInteger, l: NonNegativeInteger, f: V): D ==
k**l*coefficient(f, k)
ShiftC(total: NonNegativeInteger): List D ==
[i::D for i in 0..total-1]
-------------------------------------------------------------------------------
-- q-Shift Operator
-------------------------------------------------------------------------------
-- q-ShiftAction(k, l, f) is the CoeffAction appropriate for the shift operator.
qShiftAction(q: D, k: NonNegativeInteger, l: NonNegativeInteger, f: V): D ==
q**(k*l)*coefficient(f, k)
qShiftC(q: D, total: NonNegativeInteger): List D ==
[q**i for i in 0..total-1]
-------------------------------------------------------------------------------
-- Differentiation Operator
-------------------------------------------------------------------------------
-- DiffAction(k, l, f) is the CoeffAction appropriate for the differentiation
-- operator.
DiffAction(k: NonNegativeInteger, l: NonNegativeInteger, f: V): D ==
coefficient(f, (k-l)::NonNegativeInteger)
DiffC(total: NonNegativeInteger): List D ==
[0 for i in 1..total]
-------------------------------------------------------------------------------
-- general - suitable for functions f
-------------------------------------------------------------------------------
-- get the coefficient of z^k in the scalar product of p and f, the action
-- being defined by coeffAction
generalCoefficient(coeffAction: CoeffAction, f: Vector V,
k: NonNegativeInteger, p: Vector SUP D): D ==
res: D := 0
for i in 1..#f repeat
-- Defining a and b and summing only over those coefficients that might be
-- nonzero makes a huge difference in speed
a := f.i
b := p.i
for l in minimumDegree b..degree b repeat
if not zero? coefficient(b, l)
then res := res + coefficient(b, l) * coeffAction(k, l, a)
res
generalInterpolation(C: List D, coeffAction: CoeffAction,
f: Vector V,
eta: List NonNegativeInteger): Matrix SUP D ==
c: cFunction := generalCoefficient(coeffAction, f,
(#1-1)::NonNegativeInteger, #2)
fffg(C, c, eta)
-- returns the lexicographically next vector with non-negative components
-- smaller than p with the same sum as v
nextVector!(p: NonNegativeInteger, v: List NonNegativeInteger)
: Union("failed", List NonNegativeInteger) ==
n := #v
pos := position(#1 < p, v)
-- pos should never be zero, since we assume that we have at least one
-- non-maximal, non-zero element.
if pos = 1 then
sum: Integer := v.1
for i in 2..n repeat
if v.i < p and sum > 0 then
v.i := v.i + 1
sum := sum - 1
for j in 1..i-1 repeat
if sum > p then
v.j := p
sum := sum - p
else
v.j := sum::NonNegativeInteger
sum := 0
return v
else sum := sum + v.i
return "failed"
else
v.pos := v.pos + 1
v.(pos-1) := (v.(pos-1) - 1)::NonNegativeInteger
v
vectorStream(p: NonNegativeInteger, v: List NonNegativeInteger)
: Stream List NonNegativeInteger == delay
next := nextVector!(p, copy v)
(next case "failed") => empty()$Stream(List NonNegativeInteger)
cons(next, vectorStream(p, next))
vectorStream2(p: NonNegativeInteger, v: List NonNegativeInteger)
: Stream List NonNegativeInteger == delay
next := nextVector!(p, copy v)
(next case "failed") => empty()$Stream(List NonNegativeInteger)
next2 := nextVector!(p, copy next)
(next2 case "failed") => cons(next, empty())
cons(next2, vectorStream2(p, next2))
generalInterpolation(C: List D, coeffAction: CoeffAction,
f: Vector V,
totalDegree: NonNegativeInteger,
maxDegree: NonNegativeInteger)
: Stream Matrix SUP D ==
sum: Integer := totalDegree
entry: Integer
eta: List NonNegativeInteger
:= [(if sum < maxDegree _
then (entry := sum; sum := 0) _
else (entry := maxDegree; sum := sum - maxDegree); _
entry::NonNegativeInteger) for i in 1..#f]
(sum > 0) => empty()$Stream(Matrix SUP D)
if #f = 2 then
map(generalInterpolation(C, coeffAction, f, #1),
cons(eta, vectorStream2(maxDegree, eta)))
$StreamFunctions2(List NonNegativeInteger,
Matrix SUP D)
else
map(generalInterpolation(C, coeffAction, f, #1),
cons(eta, vectorStream(maxDegree, eta)))
$StreamFunctions2(List NonNegativeInteger,
Matrix SUP D)
-------------------------------------------------------------------------------
-- rational interpolation
-------------------------------------------------------------------------------
interpolate(x: List Fraction D, y: List Fraction D, d: NonNegativeInteger)
: Fraction SUP D ==
gx := splitDenominator(x)$InnerCommonDenominator(D, Fraction D, _
List D, _
List Fraction D)
gy := splitDenominator(y)$InnerCommonDenominator(D, Fraction D, _
List D, _
List Fraction D)
r := interpolate(gx.num, gy.num, d)
elt(numer r, monomial(gx.den,1))/(gy.den*elt(denom r, monomial(gx.den,1)))
interpolate(x: List D, y: List D, d: NonNegativeInteger): Fraction SUP D ==
-- berechne Interpolante mit Graden d und N-d-1
if (N := #x) ~= #y then
error "interpolate: number of points and values must match"
if N <= d then
error "interpolate: numerator degree must be smaller than number of
data points"
c: cFunction := y.#1 * elt(#2.2, x.#1) - elt(#2.1, x.#1)
eta: List NonNegativeInteger := [d, (N-d)::NonNegativeInteger]
M := fffg(x, c, eta)
if zero?(M.(2,1)) then M.(1,2)/M.(2,2)
else M.(1,1)/M.(2,1)
-- wegen Lemma 5.3 können M.1.(2,1) und M.1.(2,2) nicht beide verschwinden,
-- denn eta_sigma ist immer sigma-normal (Theorem 7.2) und damit auch
-- paranormal (Dfn 4.2)
-- wegen Lemma 5.1 ist M.1.(*,2) eine Lösung des Interpolationsproblems, wenn
-- M.1.(2,1) verschwindet.
-------------------------------------------------------------------------------
-- fffg
-------------------------------------------------------------------------------
-- a major part of the time is spent here
recurrence(M: Matrix SUP D, lambda: NonNegativeInteger, m:
NonNegativeInteger,
r: Vector D, d: D, z: SUP D, Ck: D, p: Vector D): Matrix SUP D ==
rLambda := qelt(r, lambda)
polyf := rLambda * (z - Ck::SUP D)
for i in 1..m repeat
Milambda := qelt(M, i, lambda)
newMilambda := polyf * Milambda
-- update columns ~= lambda and calculate their sum
for l in 1..m | l ~= lambda repeat
rl := qelt(r, l)
Mil := ((qelt(M, i, l) * rLambda - Milambda * rl) exquo d)::SUP D
qsetelt!(M, i, l, Mil)
pl := qelt(p, l)
newMilambda := newMilambda - pl * Mil
-- update column lambda
qsetelt!(M, i, lambda, (newMilambda exquo d)::SUP D)
M
fffg(C: List D, c: cFunction, eta: List NonNegativeInteger): Matrix SUP D ==
-- eta is the vector of degrees. We compute M with degrees eta+e_i-1, i=1..m
z: SUP D := monomial(1, 1)
m: NonNegativeInteger := #eta
M: Matrix SUP D := scalarMatrix(m, 1)
d: D := 1
K: NonNegativeInteger := reduce(_+, eta)
etak: Vector NonNegativeInteger := zero(m)
r: Vector D := zero(m)
p: Vector D := zero(m)
Lambda: List Integer
lambdaMax: Integer
lambda: NonNegativeInteger
for k in 1..K repeat
-- k = sigma+1
for l in 1..m repeat r.l := c(k, column(M, l))
Lambda := [eta.l-etak.l for l in 1..m | r.l ~= 0]
-- if Lambda is empty, then M, d and etak remain unchanged. Otherwise, we look
-- for the next closest para-normal point.
(empty? Lambda) => "iterate"
lambdaMax := reduce(max, Lambda)
lambda := 1
while eta.lambda-etak.lambda < lambdaMax or r.lambda = 0 repeat
lambda := lambda + 1
-- Calculate leading coefficients
for l in 1..m | l ~= lambda repeat
if etak.l > 0 then
p.l := coefficient(M.(l, lambda), (etak.l-1)::NonNegativeInteger)
else
p.l := 0
-- increase order and adjust degree constraints
M := recurrence(M, lambda, m, r, d, z, C.k, p)
d := r.lambda
etak.lambda := etak.lambda + 1
M
@
%$
\section{package FFFGF FractionFreeFastGaussianFractions}
<<package FFFGF FractionFreeFastGaussianFractions>>=
)abbrev package FFFGF FractionFreeFastGaussianFractions
++ Author: Martin Rubey
++ Description:
++ This package lifts the interpolation functions from
++ \spadtype{FractionFreeFastGaussian} to fractions.
FractionFreeFastGaussianFractions(D, V, VF): Exports == Implementation where
D: Join(IntegralDomain, GcdDomain)
V: FiniteAbelianMonoidRing(D, NonNegativeInteger)
VF: FiniteAbelianMonoidRing(Fraction D, NonNegativeInteger)
F ==> Fraction D
SUP ==> SparseUnivariatePolynomial
FFFG ==> FractionFreeFastGaussian
FAMR2 ==> FiniteAbelianMonoidRingFunctions2
cFunction ==> (NonNegativeInteger, Vector SUP D) -> D
CoeffAction ==> (NonNegativeInteger, NonNegativeInteger, V) -> D
-- coeffAction(k, l, f) is the coefficient of x^k in z^l f(x)
Exports == with
generalInterpolation: (List D, CoeffAction, Vector VF, List
NonNegativeInteger)
-> Matrix SUP D
++ \spad{generalInterpolation(l, CA, f, eta)} performs Hermite-Pade
++ approximation using the given action CA of polynomials on the elements
++ of f. The result is guaranteed to be correct up to order
++ |eta|-1. Given that eta is a "normal" point, the degrees on the
++ diagonal are given by eta. The degrees of column i are in this case
++ eta + e.i - [1,1,...,1], where the degree of zero is -1.
generalInterpolation: (List D, CoeffAction,
Vector VF, NonNegativeInteger, NonNegativeInteger)
-> Stream Matrix SUP D
++ \spad{generalInterpolation(l, CA, f, totalDegree, maxDegree)} applies
++ generalInterpolation(l, CA, f, eta) for all possible eta with maximal
++ entry maxDegree and sum of entries totalDegree
Implementation == add
multiplyRows!(v: Vector D, M: Matrix SUP D): Matrix SUP D ==
n := #v
for i in 1..n repeat
for j in 1..n repeat
M.(i,j) := v.i*M.(i,j)
M
generalInterpolation(C: List D, coeffAction: CoeffAction,
f: Vector VF, eta: List NonNegativeInteger): Matrix
SUP D ==
n := #f
g: Vector V := new(n, 0)
den: Vector D := new(n, 0)
for i in 1..n repeat
c := coefficients(f.i)
den.i := commonDenominator(c)$CommonDenominator(D, F, List F)
g.i := map(retract(#1*den.i)@D, f.i)
$FAMR2(NonNegativeInteger, Fraction D, VF, D, V)
M := generalInterpolation(C, coeffAction, g, eta)$FFFG(D, V)
-- The following is necessary since I'm multiplying each row with a factor, not
-- each column. Possibly I could factor out gcd den, but I'm not sure whether
-- this is efficient.
multiplyRows!(den, M)
generalInterpolation(C: List D, coeffAction: CoeffAction,
f: Vector VF,
totalDegree: NonNegativeInteger,
maxDegree: NonNegativeInteger)
: Stream Matrix SUP D ==
n := #f
g: Vector V := new(n, 0)
den: Vector D := new(n, 0)
for i in 1..n repeat
c := coefficients(f.i)
den.i := commonDenominator(c)$CommonDenominator(D, F, List F)
g.i := map(retract(#1*den.i)@D, f.i)
$FAMR2(NonNegativeInteger, Fraction D, VF, D, V)
c: cFunction := generalCoefficient(coeffAction, g,
(#1-1)::NonNegativeInteger,
#2)$FFFG(D, V)
MS: Stream Matrix SUP D := generalInterpolation(C, coeffAction, g,
totalDegree, maxDegree)$FFFG(D, V)
-- The following is necessary since I'm multiplying each row with a factor, not
-- each column. Possibly I could factor out gcd den, but I'm not sure whether
-- this is efficient.
map(multiplyRows!(den, #1), MS)$Stream(Matrix SUP D)
@
\section{package NEWTON NewtonInterpolation}
<<package NEWTON NewtonInterpolation>>=
)abbrev package NEWTON NewtonInterpolation
++ Description:
++ This package exports Newton interpolation for the special case where the
++ result is known to be in the original integral domain
NewtonInterpolation F: Exports == Implementation where
F: IntegralDomain
Exports == with
newton: List F -> SparseUnivariatePolynomial F
Implementation == add
differences(yl: List F): List F ==
[y2-y1 for y1 in yl for y2 in rest yl]
z:SparseUnivariatePolynomial(F) := monomial(1,1)
-- we assume x=[1,2,3,...,n]
newtonAux(k: F, fact: F, yl: List F): SparseUnivariatePolynomial(F) ==
if empty? rest yl
then ((yl.1) exquo fact)::F::SparseUnivariatePolynomial(F)
else ((yl.1) exquo fact)::F::SparseUnivariatePolynomial(F)
+ (z-k::SparseUnivariatePolynomial(F)) _
* newtonAux(k+1$F, fact*k, differences yl)
newton yl == newtonAux(1$F, 1$F, yl)
@
%$
\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 FAMR2 FiniteAbelianMonoidRingFunctions2>>
<<package FFFG FractionFreeFastGaussian>>
<<package FFFGF FractionFreeFastGaussianFractions>>
<<package NEWTON NewtonInterpolation>>
@
\end{document}
--
forwarded from http://wiki.axiom-developer.org/[EMAIL PROTECTED]