aboutsummaryrefslogtreecommitdiff
path: root/src/algebra/fs2ups.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/fs2ups.spad.pamphlet
downloadopen-axiom-ab8cc85adde879fb963c94d15675783f2cf4b183.tar.gz
Initial population.
Diffstat (limited to 'src/algebra/fs2ups.spad.pamphlet')
-rw-r--r--src/algebra/fs2ups.spad.pamphlet812
1 files changed, 812 insertions, 0 deletions
diff --git a/src/algebra/fs2ups.spad.pamphlet b/src/algebra/fs2ups.spad.pamphlet
new file mode 100644
index 00000000..9751dd40
--- /dev/null
+++ b/src/algebra/fs2ups.spad.pamphlet
@@ -0,0 +1,812 @@
+\documentclass{article}
+\usepackage{axiom}
+\begin{document}
+\title{\$SPAD/src/algebra fs2ups.spad}
+\author{Clifton J. Williamson}
+\maketitle
+\begin{abstract}
+\end{abstract}
+\eject
+\tableofcontents
+\eject
+\section{package FS2UPS FunctionSpaceToUnivariatePowerSeries}
+<<package FS2UPS FunctionSpaceToUnivariatePowerSeries>>=
+)abbrev package FS2UPS FunctionSpaceToUnivariatePowerSeries
+++ Author: Clifton J. Williamson
+++ Date Created: 21 March 1989
+++ Date Last Updated: 2 December 1994
+++ Basic Operations:
+++ Related Domains:
+++ Also See:
+++ AMS Classifications:
+++ Keywords: elementary function, power series
+++ Examples:
+++ References:
+++ Description:
+++ This package converts expressions in some function space to power
+++ series in a variable x with coefficients in that function space.
+++ The function \spadfun{exprToUPS} converts expressions to power series
+++ whose coefficients do not contain the variable x. The function
+++ \spadfun{exprToGenUPS} converts functional expressions to power series
+++ whose coefficients may involve functions of \spad{log(x)}.
+FunctionSpaceToUnivariatePowerSeries(R,FE,Expon,UPS,TRAN,x):_
+ Exports == Implementation where
+ R : Join(GcdDomain,OrderedSet,RetractableTo Integer,_
+ LinearlyExplicitRingOver Integer)
+ FE : Join(AlgebraicallyClosedField,TranscendentalFunctionCategory,_
+ FunctionSpace R)
+ with
+ coerce: Expon -> %
+ ++ coerce(e) converts an 'exponent' e to an 'expression'
+ Expon : OrderedRing
+ UPS : Join(UnivariatePowerSeriesCategory(FE,Expon),Field,_
+ TranscendentalFunctionCategory)
+ with
+ differentiate: % -> %
+ ++ differentiate(x) returns the derivative of x since we
+ ++ need to be able to differentiate a power series
+ integrate: % -> %
+ ++ integrate(x) returns the integral of x since
+ ++ we need to be able to integrate a power series
+ TRAN : PartialTranscendentalFunctions UPS
+ x : Symbol
+ B ==> Boolean
+ BOP ==> BasicOperator
+ I ==> Integer
+ NNI ==> NonNegativeInteger
+ K ==> Kernel FE
+ L ==> List
+ RN ==> Fraction Integer
+ S ==> String
+ SY ==> Symbol
+ PCL ==> PolynomialCategoryLifting(IndexedExponents K,K,R,SMP,FE)
+ POL ==> Polynomial R
+ SMP ==> SparseMultivariatePolynomial(R,K)
+ SUP ==> SparseUnivariatePolynomial Polynomial R
+ Problem ==> Record(func:String,prob:String)
+ Result ==> Union(%series:UPS,%problem:Problem)
+ SIGNEF ==> ElementaryFunctionSign(R,FE)
+
+ Exports ==> with
+ exprToUPS : (FE,B,S) -> Result
+ ++ exprToUPS(fcn,posCheck?,atanFlag) converts the expression
+ ++ \spad{fcn} to a power series. If \spad{posCheck?} is true,
+ ++ log's of negative numbers are not allowed nor are nth roots of
+ ++ negative numbers with n even. If \spad{posCheck?} is false,
+ ++ these are allowed. \spad{atanFlag} determines how the case
+ ++ \spad{atan(f(x))}, where \spad{f(x)} has a pole, will be treated.
+ ++ The possible values of \spad{atanFlag} are \spad{"complex"},
+ ++ \spad{"real: two sides"}, \spad{"real: left side"},
+ ++ \spad{"real: right side"}, and \spad{"just do it"}.
+ ++ If \spad{atanFlag} is \spad{"complex"}, then no series expansion
+ ++ will be computed because, viewed as a function of a complex
+ ++ variable, \spad{atan(f(x))} has an essential singularity.
+ ++ Otherwise, the sign of the leading coefficient of the series
+ ++ expansion of \spad{f(x)} determines the constant coefficient
+ ++ in the series expansion of \spad{atan(f(x))}. If this sign cannot
+ ++ be determined, a series expansion is computed only when
+ ++ \spad{atanFlag} is \spad{"just do it"}. When the leading term
+ ++ in the series expansion of \spad{f(x)} is of odd degree (or is a
+ ++ rational degree with odd numerator), then the constant coefficient
+ ++ in the series expansion of \spad{atan(f(x))} for values to the
+ ++ left differs from that for values to the right. If \spad{atanFlag}
+ ++ is \spad{"real: two sides"}, no series expansion will be computed.
+ ++ If \spad{atanFlag} is \spad{"real: left side"} the constant
+ ++ coefficient for values to the left will be used and if \spad{atanFlag}
+ ++ \spad{"real: right side"} the constant coefficient for values to the
+ ++ right will be used.
+ ++ If there is a problem in converting the function to a power series,
+ ++ a record containing the name of the function that caused the problem
+ ++ and a brief description of the problem is returned.
+ ++ When expanding the expression into a series it is assumed that
+ ++ the series is centered at 0. For a series centered at a, the
+ ++ user should perform the substitution \spad{x -> x + a} before calling
+ ++ this function.
+
+ exprToGenUPS : (FE,B,S) -> Result
+ ++ exprToGenUPS(fcn,posCheck?,atanFlag) converts the expression
+ ++ \spad{fcn} to a generalized power series. If \spad{posCheck?}
+ ++ is true, log's of negative numbers are not allowed nor are nth roots
+ ++ of negative numbers with n even. If \spad{posCheck?} is false,
+ ++ these are allowed. \spad{atanFlag} determines how the case
+ ++ \spad{atan(f(x))}, where \spad{f(x)} has a pole, will be treated.
+ ++ The possible values of \spad{atanFlag} are \spad{"complex"},
+ ++ \spad{"real: two sides"}, \spad{"real: left side"},
+ ++ \spad{"real: right side"}, and \spad{"just do it"}.
+ ++ If \spad{atanFlag} is \spad{"complex"}, then no series expansion
+ ++ will be computed because, viewed as a function of a complex
+ ++ variable, \spad{atan(f(x))} has an essential singularity.
+ ++ Otherwise, the sign of the leading coefficient of the series
+ ++ expansion of \spad{f(x)} determines the constant coefficient
+ ++ in the series expansion of \spad{atan(f(x))}. If this sign cannot
+ ++ be determined, a series expansion is computed only when
+ ++ \spad{atanFlag} is \spad{"just do it"}. When the leading term
+ ++ in the series expansion of \spad{f(x)} is of odd degree (or is a
+ ++ rational degree with odd numerator), then the constant coefficient
+ ++ in the series expansion of \spad{atan(f(x))} for values to the
+ ++ left differs from that for values to the right. If \spad{atanFlag}
+ ++ is \spad{"real: two sides"}, no series expansion will be computed.
+ ++ If \spad{atanFlag} is \spad{"real: left side"} the constant
+ ++ coefficient for values to the left will be used and if \spad{atanFlag}
+ ++ \spad{"real: right side"} the constant coefficient for values to the
+ ++ right will be used.
+ ++ If there is a problem in converting the function to a power
+ ++ series, we return a record containing the name of the function
+ ++ that caused the problem and a brief description of the problem.
+ ++ When expanding the expression into a series it is assumed that
+ ++ the series is centered at 0. For a series centered at a, the
+ ++ user should perform the substitution \spad{x -> x + a} before calling
+ ++ this function.
+ localAbs: FE -> FE
+ ++ localAbs(fcn) = \spad{abs(fcn)} or \spad{sqrt(fcn**2)} depending
+ ++ on whether or not FE has a function \spad{abs}. This should be
+ ++ a local function, but the compiler won't allow it.
+
+ Implementation ==> add
+
+ ratIfCan : FE -> Union(RN,"failed")
+ carefulNthRootIfCan : (UPS,NNI,B,B) -> Result
+ stateProblem : (S,S) -> Result
+ polyToUPS : SUP -> UPS
+ listToUPS : (L FE,(FE,B,S) -> Result,B,S,UPS,(UPS,UPS) -> UPS)_
+ -> Result
+ isNonTrivPower : FE -> Union(Record(val:FE,exponent:I),"failed")
+ powerToUPS : (FE,I,B,S) -> Result
+ kernelToUPS : (K,B,S) -> Result
+ nthRootToUPS : (FE,NNI,B,S) -> Result
+ logToUPS : (FE,B,S) -> Result
+ atancotToUPS : (FE,B,S,I) -> Result
+ applyIfCan : (UPS -> Union(UPS,"failed"),FE,S,B,S) -> Result
+ tranToUPS : (K,FE,B,S) -> Result
+ powToUPS : (L FE,B,S) -> Result
+ newElem : FE -> FE
+ smpElem : SMP -> FE
+ k2Elem : K -> FE
+ contOnReals? : S -> B
+ bddOnReals? : S -> B
+ iExprToGenUPS : (FE,B,S) -> Result
+ opsInvolvingX : FE -> L BOP
+ opInOpList? : (SY,L BOP) -> B
+ exponential? : FE -> B
+ productOfNonZeroes? : FE -> B
+ powerToGenUPS : (FE,I,B,S) -> Result
+ kernelToGenUPS : (K,B,S) -> Result
+ nthRootToGenUPS : (FE,NNI,B,S) -> Result
+ logToGenUPS : (FE,B,S) -> Result
+ expToGenUPS : (FE,B,S) -> Result
+ expGenUPS : (UPS,B,S) -> Result
+ atancotToGenUPS : (FE,FE,B,S,I) -> Result
+ genUPSApplyIfCan : (UPS -> Union(UPS,"failed"),FE,S,B,S) -> Result
+ applyBddIfCan : (FE,UPS -> Union(UPS,"failed"),FE,S,B,S) -> Result
+ tranToGenUPS : (K,FE,B,S) -> Result
+ powToGenUPS : (L FE,B,S) -> Result
+
+ ZEROCOUNT : I := 1000
+ -- number of zeroes to be removed when taking logs or nth roots
+
+ ratIfCan fcn == retractIfCan(fcn)@Union(RN,"failed")
+
+ carefulNthRootIfCan(ups,n,posCheck?,rightOnly?) ==
+ -- similar to 'nthRootIfCan', but it is fussy about the series
+ -- it takes as an argument. If 'n' is EVEN and 'posCheck?'
+ -- is truem then the leading coefficient of the series must
+ -- be POSITIVE. In this case, if 'rightOnly?' is false, the
+ -- order of the series must be zero. The idea is that the
+ -- series represents a real function of a real variable, and
+ -- we want a unique real nth root defined on a neighborhood
+ -- of zero.
+ n < 1 => error "nthRoot: n must be positive"
+ deg := degree ups
+ if (coef := coefficient(ups,deg)) = 0 then
+ deg := order(ups,deg + ZEROCOUNT :: Expon)
+ (coef := coefficient(ups,deg)) = 0 =>
+ error "log of series with many leading zero coefficients"
+ -- if 'posCheck?' is true, we do not allow nth roots of negative
+ -- numbers when n in even
+ if even?(n :: I) then
+ if posCheck? and ((signum := sign(coef)$SIGNEF) case I) then
+ (signum :: I) = -1 =>
+ return stateProblem("nth root","negative leading coefficient")
+ not rightOnly? and not zero? deg => -- nth root not unique
+ return stateProblem("nth root","series of non-zero order")
+ (ans := nthRootIfCan(ups,n)) case "failed" =>
+ stateProblem("nth root","no nth root")
+ [ans :: UPS]
+
+ stateProblem(function,problem) ==
+ -- records the problem which occured in converting an expression
+ -- to a power series
+ [[function,problem]]
+
+ exprToUPS(fcn,posCheck?,atanFlag) ==
+ -- converts a functional expression to a power series
+ --!! The following line is commented out so that expressions of
+ --!! the form a**b will be normalized to exp(b * log(a)) even if
+ --!! 'a' and 'b' do not involve the limiting variable 'x'.
+ --!! - cjw 1 Dec 94
+ --not member?(x,variables fcn) => [monomial(fcn,0)]
+ (poly := retractIfCan(fcn)@Union(POL,"failed")) case POL =>
+ [polyToUPS univariate(poly :: POL,x)]
+ (sum := isPlus fcn) case L(FE) =>
+ listToUPS(sum :: L(FE),exprToUPS,posCheck?,atanFlag,0,#1 + #2)
+ (prod := isTimes fcn) case L(FE) =>
+ listToUPS(prod :: L(FE),exprToUPS,posCheck?,atanFlag,1,#1 * #2)
+ (expt := isNonTrivPower fcn) case Record(val:FE,exponent:I) =>
+ power := expt :: Record(val:FE,exponent:I)
+ powerToUPS(power.val,power.exponent,posCheck?,atanFlag)
+ (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
+ kernelToUPS(ker :: K,posCheck?,atanFlag)
+ error "exprToUPS: neither a sum, product, power, nor kernel"
+
+ polyToUPS poly ==
+ -- converts a polynomial to a power series
+ zero? poly => 0
+ -- we don't start with 'ans := 0' as this may lead to an
+ -- enormous number of leading zeroes in the power series
+ deg := degree poly
+ coef := leadingCoefficient(poly) :: FE
+ ans := monomial(coef,deg :: Expon)$UPS
+ poly := reductum poly
+ while not zero? poly repeat
+ deg := degree poly
+ coef := leadingCoefficient(poly) :: FE
+ ans := ans + monomial(coef,deg :: Expon)$UPS
+ poly := reductum poly
+ ans
+
+ listToUPS(list,feToUPS,posCheck?,atanFlag,ans,op) ==
+ -- converts each element of a list of expressions to a power
+ -- series and returns the sum of these series, when 'op' is +
+ -- and 'ans' is 0, or the product of these series, when 'op' is *
+ -- and 'ans' is 1
+ while not null list repeat
+ (term := feToUPS(first list,posCheck?,atanFlag)) case %problem =>
+ return term
+ ans := op(ans,term.%series)
+ list := rest list
+ [ans]
+
+ isNonTrivPower fcn ==
+ -- is the function a power with exponent other than 0 or 1?
+ (expt := isPower fcn) case "failed" => "failed"
+ power := expt :: Record(val:FE,exponent:I)
+-- one? power.exponent => "failed"
+ (power.exponent = 1) => "failed"
+ power
+
+ powerToUPS(fcn,n,posCheck?,atanFlag) ==
+ -- converts an integral power to a power series
+ (b := exprToUPS(fcn,posCheck?,atanFlag)) case %problem => b
+ n > 0 => [(b.%series) ** n]
+ -- check lowest order coefficient when n < 0
+ ups := b.%series; deg := degree ups
+ if (coef := coefficient(ups,deg)) = 0 then
+ deg := order(ups,deg + ZEROCOUNT :: Expon)
+ (coef := coefficient(ups,deg)) = 0 =>
+ error "inverse of series with many leading zero coefficients"
+ [ups ** n]
+
+ kernelToUPS(ker,posCheck?,atanFlag) ==
+ -- converts a kernel to a power series
+ (sym := symbolIfCan(ker)) case Symbol =>
+ (sym :: Symbol) = x => [monomial(1,1)]
+ [monomial(ker :: FE,0)]
+ empty?(args := argument ker) => [monomial(ker :: FE,0)]
+ not member?(x, variables(ker :: FE)) => [monomial(ker :: FE,0)]
+ empty? rest args =>
+ arg := first args
+ is?(ker,"abs" :: Symbol) =>
+ nthRootToUPS(arg*arg,2,posCheck?,atanFlag)
+ is?(ker,"%paren" :: Symbol) => exprToUPS(arg,posCheck?,atanFlag)
+ is?(ker,"log" :: Symbol) => logToUPS(arg,posCheck?,atanFlag)
+ is?(ker,"exp" :: Symbol) =>
+ applyIfCan(expIfCan,arg,"exp",posCheck?,atanFlag)
+ tranToUPS(ker,arg,posCheck?,atanFlag)
+ is?(ker,"%power" :: Symbol) => powToUPS(args,posCheck?,atanFlag)
+ is?(ker,"nthRoot" :: Symbol) =>
+ n := retract(second args)@I
+ nthRootToUPS(first args,n :: NNI,posCheck?,atanFlag)
+ stateProblem(string name ker,"unknown kernel")
+
+ nthRootToUPS(arg,n,posCheck?,atanFlag) ==
+ -- converts an nth root to a power series
+ -- this is not used in the limit package, so the series may
+ -- have non-zero order, in which case nth roots may not be unique
+ (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
+ ans := carefulNthRootIfCan(result.%series,n,posCheck?,false)
+ ans case %problem => ans
+ [ans.%series]
+
+ logToUPS(arg,posCheck?,atanFlag) ==
+ -- converts a logarithm log(f(x)) to a power series
+ -- f(x) must have order 0 and if 'posCheck?' is true,
+ -- then f(x) must have a non-negative leading coefficient
+ (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
+ ups := result.%series
+ not zero? order(ups,1) =>
+ stateProblem("log","series of non-zero order")
+ coef := coefficient(ups,0)
+ -- if 'posCheck?' is true, we do not allow logs of negative numbers
+ if posCheck? then
+ if ((signum := sign(coef)$SIGNEF) case I) then
+ (signum :: I) = -1 =>
+ return stateProblem("log","negative leading coefficient")
+ [logIfCan(ups) :: UPS]
+
+ if FE has abs: FE -> FE then
+ localAbs fcn == abs fcn
+ else
+ localAbs fcn == sqrt(fcn * fcn)
+
+ signOfExpression: FE -> FE
+ signOfExpression arg == localAbs(arg)/arg
+
+ atancotToUPS(arg,posCheck?,atanFlag,plusMinus) ==
+ -- converts atan(f(x)) to a power series
+ (result := exprToUPS(arg,posCheck?,atanFlag)) case %problem => result
+ ups := result.%series; coef := coefficient(ups,0)
+ (ord := order(ups,0)) = 0 and coef * coef = -1 =>
+ -- series involves complex numbers
+ return stateProblem("atan","logarithmic singularity")
+ cc : FE :=
+ ord < 0 =>
+ atanFlag = "complex" =>
+ return stateProblem("atan","essential singularity")
+ (rn := ratIfCan(ord :: FE)) case "failed" =>
+ -- this condition usually won't occur because exponents will
+ -- be integers or rational numbers
+ return stateProblem("atan","branch problem")
+ if (atanFlag = "real: two sides") and (odd? numer(rn :: RN)) then
+ -- expansions to the left and right of zero have different
+ -- constant coefficients
+ return stateProblem("atan","branch problem")
+ lc := coefficient(ups,ord)
+ (signum := sign(lc)$SIGNEF) case "failed" =>
+ -- can't determine sign
+ atanFlag = "just do it" =>
+ plusMinus = 1 => pi()/(2 :: FE)
+ 0
+ posNegPi2 := signOfExpression(lc) * pi()/(2 :: FE)
+ plusMinus = 1 => posNegPi2
+ pi()/(2 :: FE) - posNegPi2
+ --return stateProblem("atan","branch problem")
+ left? : B := atanFlag = "real: left side"; n := signum :: Integer
+ (left? and n = 1) or (not left? and n = -1) =>
+ plusMinus = 1 => -pi()/(2 :: FE)
+ pi()
+ plusMinus = 1 => pi()/(2 :: FE)
+ 0
+ atan coef
+ [(cc :: UPS) + integrate(plusMinus * differentiate(ups)/(1 + ups*ups))]
+
+ applyIfCan(fcn,arg,fcnName,posCheck?,atanFlag) ==
+ -- converts fcn(arg) to a power series
+ (ups := exprToUPS(arg,posCheck?,atanFlag)) case %problem => ups
+ ans := fcn(ups.%series)
+ ans case "failed" => stateProblem(fcnName,"essential singularity")
+ [ans :: UPS]
+
+ tranToUPS(ker,arg,posCheck?,atanFlag) ==
+ -- converts ker to a power series for certain functions
+ -- in trig or hyperbolic trig categories
+ is?(ker,"sin" :: SY) =>
+ applyIfCan(sinIfCan,arg,"sin",posCheck?,atanFlag)
+ is?(ker,"cos" :: SY) =>
+ applyIfCan(cosIfCan,arg,"cos",posCheck?,atanFlag)
+ is?(ker,"tan" :: SY) =>
+ applyIfCan(tanIfCan,arg,"tan",posCheck?,atanFlag)
+ is?(ker,"cot" :: SY) =>
+ applyIfCan(cotIfCan,arg,"cot",posCheck?,atanFlag)
+ is?(ker,"sec" :: SY) =>
+ applyIfCan(secIfCan,arg,"sec",posCheck?,atanFlag)
+ is?(ker,"csc" :: SY) =>
+ applyIfCan(cscIfCan,arg,"csc",posCheck?,atanFlag)
+ is?(ker,"asin" :: SY) =>
+ applyIfCan(asinIfCan,arg,"asin",posCheck?,atanFlag)
+ is?(ker,"acos" :: SY) =>
+ applyIfCan(acosIfCan,arg,"acos",posCheck?,atanFlag)
+ is?(ker,"atan" :: SY) => atancotToUPS(arg,posCheck?,atanFlag,1)
+ is?(ker,"acot" :: SY) => atancotToUPS(arg,posCheck?,atanFlag,-1)
+ is?(ker,"asec" :: SY) =>
+ applyIfCan(asecIfCan,arg,"asec",posCheck?,atanFlag)
+ is?(ker,"acsc" :: SY) =>
+ applyIfCan(acscIfCan,arg,"acsc",posCheck?,atanFlag)
+ is?(ker,"sinh" :: SY) =>
+ applyIfCan(sinhIfCan,arg,"sinh",posCheck?,atanFlag)
+ is?(ker,"cosh" :: SY) =>
+ applyIfCan(coshIfCan,arg,"cosh",posCheck?,atanFlag)
+ is?(ker,"tanh" :: SY) =>
+ applyIfCan(tanhIfCan,arg,"tanh",posCheck?,atanFlag)
+ is?(ker,"coth" :: SY) =>
+ applyIfCan(cothIfCan,arg,"coth",posCheck?,atanFlag)
+ is?(ker,"sech" :: SY) =>
+ applyIfCan(sechIfCan,arg,"sech",posCheck?,atanFlag)
+ is?(ker,"csch" :: SY) =>
+ applyIfCan(cschIfCan,arg,"csch",posCheck?,atanFlag)
+ is?(ker,"asinh" :: SY) =>
+ applyIfCan(asinhIfCan,arg,"asinh",posCheck?,atanFlag)
+ is?(ker,"acosh" :: SY) =>
+ applyIfCan(acoshIfCan,arg,"acosh",posCheck?,atanFlag)
+ is?(ker,"atanh" :: SY) =>
+ applyIfCan(atanhIfCan,arg,"atanh",posCheck?,atanFlag)
+ is?(ker,"acoth" :: SY) =>
+ applyIfCan(acothIfCan,arg,"acoth",posCheck?,atanFlag)
+ is?(ker,"asech" :: SY) =>
+ applyIfCan(asechIfCan,arg,"asech",posCheck?,atanFlag)
+ is?(ker,"acsch" :: SY) =>
+ applyIfCan(acschIfCan,arg,"acsch",posCheck?,atanFlag)
+ stateProblem(string name ker,"unknown kernel")
+
+ powToUPS(args,posCheck?,atanFlag) ==
+ -- converts a power f(x) ** g(x) to a power series
+ (logBase := logToUPS(first args,posCheck?,atanFlag)) case %problem =>
+ logBase
+ (expon := exprToUPS(second args,posCheck?,atanFlag)) case %problem =>
+ expon
+ ans := expIfCan((expon.%series) * (logBase.%series))
+ ans case "failed" => stateProblem("exp","essential singularity")
+ [ans :: UPS]
+
+-- Generalized power series: power series in x, where log(x) and
+-- bounded functions of x are allowed to appear in the coefficients
+-- of the series. Used for evaluating REAL limits at x = 0.
+
+ newElem f ==
+ -- rewrites a functional expression; all trig functions are
+ -- expressed in terms of sin and cos; all hyperbolic trig
+ -- functions are expressed in terms of exp
+ smpElem(numer f) / smpElem(denom f)
+
+ smpElem p == map(k2Elem,#1::FE,p)$PCL
+
+ k2Elem k ==
+ -- rewrites a kernel; all trig functions are
+ -- expressed in terms of sin and cos; all hyperbolic trig
+ -- functions are expressed in terms of exp
+ null(args := [newElem a for a in argument k]) => k::FE
+ iez := inv(ez := exp(z := first args))
+ sinz := sin z; cosz := cos z
+ is?(k,"tan" :: Symbol) => sinz / cosz
+ is?(k,"cot" :: Symbol) => cosz / sinz
+ is?(k,"sec" :: Symbol) => inv cosz
+ is?(k,"csc" :: Symbol) => inv sinz
+ is?(k,"sinh" :: Symbol) => (ez - iez) / (2 :: FE)
+ is?(k,"cosh" :: Symbol) => (ez + iez) / (2 :: FE)
+ is?(k,"tanh" :: Symbol) => (ez - iez) / (ez + iez)
+ is?(k,"coth" :: Symbol) => (ez + iez) / (ez - iez)
+ is?(k,"sech" :: Symbol) => 2 * inv(ez + iez)
+ is?(k,"csch" :: Symbol) => 2 * inv(ez - iez)
+ (operator k) args
+
+ CONTFCNS : L S := ["sin","cos","atan","acot","exp","asinh"]
+ -- functions which are defined and continuous at all real numbers
+
+ BDDFCNS : L S := ["sin","cos","atan","acot"]
+ -- functions which are bounded on the reals
+
+ contOnReals? fcn == member?(fcn,CONTFCNS)
+ bddOnReals? fcn == member?(fcn,BDDFCNS)
+
+ exprToGenUPS(fcn,posCheck?,atanFlag) ==
+ -- converts a functional expression to a generalized power
+ -- series; "generalized" means that log(x) and bounded functions
+ -- of x are allowed to appear in the coefficients of the series
+ iExprToGenUPS(newElem fcn,posCheck?,atanFlag)
+
+ iExprToGenUPS(fcn,posCheck?,atanFlag) ==
+ -- converts a functional expression to a generalized power
+ -- series without first normalizing the expression
+ --!! The following line is commented out so that expressions of
+ --!! the form a**b will be normalized to exp(b * log(a)) even if
+ --!! 'a' and 'b' do not involve the limiting variable 'x'.
+ --!! - cjw 1 Dec 94
+ --not member?(x,variables fcn) => [monomial(fcn,0)]
+ (poly := retractIfCan(fcn)@Union(POL,"failed")) case POL =>
+ [polyToUPS univariate(poly :: POL,x)]
+ (sum := isPlus fcn) case L(FE) =>
+ listToUPS(sum :: L(FE),iExprToGenUPS,posCheck?,atanFlag,0,#1 + #2)
+ (prod := isTimes fcn) case L(FE) =>
+ listToUPS(prod :: L(FE),iExprToGenUPS,posCheck?,atanFlag,1,#1 * #2)
+ (expt := isNonTrivPower fcn) case Record(val:FE,exponent:I) =>
+ power := expt :: Record(val:FE,exponent:I)
+ powerToGenUPS(power.val,power.exponent,posCheck?,atanFlag)
+ (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
+ kernelToGenUPS(ker :: K,posCheck?,atanFlag)
+ error "exprToGenUPS: neither a sum, product, power, nor kernel"
+
+ opsInvolvingX fcn ==
+ opList := [op for k in tower fcn | unary?(op := operator k) _
+ and member?(x,variables first argument k)]
+ removeDuplicates opList
+
+ opInOpList?(name,opList) ==
+ for op in opList repeat
+ is?(op,name) => return true
+ false
+
+ exponential? fcn ==
+ -- is 'fcn' of the form exp(f)?
+ (ker := retractIfCan(fcn)@Union(K,"failed")) case K =>
+ is?(ker :: K,"exp" :: Symbol)
+ false
+
+ productOfNonZeroes? fcn ==
+ -- is 'fcn' a product of non-zero terms, where 'non-zero'
+ -- means an exponential or a function not involving x
+ exponential? fcn => true
+ (prod := isTimes fcn) case "failed" => false
+ for term in (prod :: L(FE)) repeat
+ (not exponential? term) and member?(x,variables term) =>
+ return false
+ true
+
+ powerToGenUPS(fcn,n,posCheck?,atanFlag) ==
+ -- converts an integral power to a generalized power series
+ -- if n < 0 and the lowest order coefficient of the series
+ -- involves x, we are careful about inverting this coefficient
+ -- the coefficient is inverted only if
+ -- (a) the only function involving x is 'log', or
+ -- (b) the lowest order coefficient is a product of exponentials
+ -- and functions not involving x
+ (b := exprToGenUPS(fcn,posCheck?,atanFlag)) case %problem => b
+ n > 0 => [(b.%series) ** n]
+ -- check lowest order coefficient when n < 0
+ ups := b.%series; deg := degree ups
+ if (coef := coefficient(ups,deg)) = 0 then
+ deg := order(ups,deg + ZEROCOUNT :: Expon)
+ (coef := coefficient(ups,deg)) = 0 =>
+ error "inverse of series with many leading zero coefficients"
+ xOpList := opsInvolvingX coef
+ -- only function involving x is 'log'
+ (null xOpList) => [ups ** n]
+ (null rest xOpList and is?(first xOpList,"log" :: SY)) =>
+ [ups ** n]
+ -- lowest order coefficient is a product of exponentials and
+ -- functions not involving x
+ productOfNonZeroes? coef => [ups ** n]
+ stateProblem("inv","lowest order coefficient involves x")
+
+ kernelToGenUPS(ker,posCheck?,atanFlag) ==
+ -- converts a kernel to a generalized power series
+ (sym := symbolIfCan(ker)) case Symbol =>
+ (sym :: Symbol) = x => [monomial(1,1)]
+ [monomial(ker :: FE,0)]
+ empty?(args := argument ker) => [monomial(ker :: FE,0)]
+ empty? rest args =>
+ arg := first args
+ is?(ker,"abs" :: Symbol) =>
+ nthRootToGenUPS(arg*arg,2,posCheck?,atanFlag)
+ is?(ker,"%paren" :: Symbol) => iExprToGenUPS(arg,posCheck?,atanFlag)
+ is?(ker,"log" :: Symbol) => logToGenUPS(arg,posCheck?,atanFlag)
+ is?(ker,"exp" :: Symbol) => expToGenUPS(arg,posCheck?,atanFlag)
+ tranToGenUPS(ker,arg,posCheck?,atanFlag)
+ is?(ker,"%power" :: Symbol) => powToGenUPS(args,posCheck?,atanFlag)
+ is?(ker,"nthRoot" :: Symbol) =>
+ n := retract(second args)@I
+ nthRootToGenUPS(first args,n :: NNI,posCheck?,atanFlag)
+ stateProblem(string name ker,"unknown kernel")
+
+ nthRootToGenUPS(arg,n,posCheck?,atanFlag) ==
+ -- convert an nth root to a power series
+ -- used for computing right hand limits, so the series may have
+ -- non-zero order, but may not have a negative leading coefficient
+ -- when n is even
+ (result := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
+ result
+ ans := carefulNthRootIfCan(result.%series,n,posCheck?,true)
+ ans case %problem => ans
+ [ans.%series]
+
+ logToGenUPS(arg,posCheck?,atanFlag) ==
+ -- converts a logarithm log(f(x)) to a generalized power series
+ (result := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
+ result
+ ups := result.%series; deg := degree ups
+ if (coef := coefficient(ups,deg)) = 0 then
+ deg := order(ups,deg + ZEROCOUNT :: Expon)
+ (coef := coefficient(ups,deg)) = 0 =>
+ error "log of series with many leading zero coefficients"
+ -- if 'posCheck?' is true, we do not allow logs of negative numbers
+ if posCheck? then
+ if ((signum := sign(coef)$SIGNEF) case I) then
+ (signum :: I) = -1 =>
+ return stateProblem("log","negative leading coefficient")
+ -- create logarithmic term, avoiding log's of negative rationals
+ lt := monomial(coef,deg)$UPS; cen := center lt
+ -- check to see if lowest order coefficient is a negative rational
+ negRat? : Boolean :=
+ ((rat := ratIfCan coef) case RN) =>
+ (rat :: RN) < 0 => true
+ false
+ false
+ logTerm : FE :=
+ mon : FE := (x :: FE) - (cen :: FE)
+ pow : FE := mon ** (deg :: FE)
+ negRat? => log(coef * pow)
+ term1 : FE := (deg :: FE) * log(mon)
+ log(coef) + term1
+ [monomial(logTerm,0) + log(ups/lt)]
+
+ expToGenUPS(arg,posCheck?,atanFlag) ==
+ -- converts an exponential exp(f(x)) to a generalized
+ -- power series
+ (ups := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem => ups
+ expGenUPS(ups.%series,posCheck?,atanFlag)
+
+ expGenUPS(ups,posCheck?,atanFlag) ==
+ -- computes the exponential of a generalized power series.
+ -- If the series has order zero and the constant term a0 of the
+ -- series involves x, the function tries to expand exp(a0) as
+ -- a power series.
+ (deg := order(ups,1)) < 0 =>
+ stateProblem("exp","essential singularity")
+ deg > 0 => [exp ups]
+ lc := coefficient(ups,0); xOpList := opsInvolvingX lc
+ not opInOpList?("log" :: SY,xOpList) => [exp ups]
+ -- try to fix exp(lc) if necessary
+ expCoef :=
+ normalize(exp lc,x)$ElementaryFunctionStructurePackage(R,FE)
+ opInOpList?("log" :: SY,opsInvolvingX expCoef) =>
+ stateProblem("exp","logs in constant coefficient")
+ result := exprToGenUPS(expCoef,posCheck?,atanFlag)
+ result case %problem => result
+ [(result.%series) * exp(ups - monomial(lc,0))]
+
+ atancotToGenUPS(fe,arg,posCheck?,atanFlag,plusMinus) ==
+ -- converts atan(f(x)) to a generalized power series
+ (result := exprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
+ trouble := result.%problem
+ trouble.prob = "essential singularity" => [monomial(fe,0)$UPS]
+ result
+ ups := result.%series; coef := coefficient(ups,0)
+ -- series involves complex numbers
+ (ord := order(ups,0)) = 0 and coef * coef = -1 =>
+ y := differentiate(ups)/(1 + ups*ups)
+ yCoef := coefficient(y,-1)
+ [monomial(log yCoef,0) + integrate(y - monomial(yCoef,-1)$UPS)]
+ cc : FE :=
+ ord < 0 =>
+ atanFlag = "complex" =>
+ return stateProblem("atan","essential singularity")
+ (rn := ratIfCan(ord :: FE)) case "failed" =>
+ -- this condition usually won't occur because exponents will
+ -- be integers or rational numbers
+ return stateProblem("atan","branch problem")
+ if (atanFlag = "real: two sides") and (odd? numer(rn :: RN)) then
+ -- expansions to the left and right of zero have different
+ -- constant coefficients
+ return stateProblem("atan","branch problem")
+ lc := coefficient(ups,ord)
+ (signum := sign(lc)$SIGNEF) case "failed" =>
+ -- can't determine sign
+ atanFlag = "just do it" =>
+ plusMinus = 1 => pi()/(2 :: FE)
+ 0
+ posNegPi2 := signOfExpression(lc) * pi()/(2 :: FE)
+ plusMinus = 1 => posNegPi2
+ pi()/(2 :: FE) - posNegPi2
+ --return stateProblem("atan","branch problem")
+ left? : B := atanFlag = "real: left side"; n := signum :: Integer
+ (left? and n = 1) or (not left? and n = -1) =>
+ plusMinus = 1 => -pi()/(2 :: FE)
+ pi()
+ plusMinus = 1 => pi()/(2 :: FE)
+ 0
+ atan coef
+ [(cc :: UPS) + integrate(differentiate(ups)/(1 + ups*ups))]
+
+ genUPSApplyIfCan(fcn,arg,fcnName,posCheck?,atanFlag) ==
+ -- converts fcn(arg) to a generalized power series
+ (series := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
+ series
+ ups := series.%series
+ (deg := order(ups,1)) < 0 =>
+ stateProblem(fcnName,"essential singularity")
+ deg > 0 => [fcn(ups) :: UPS]
+ lc := coefficient(ups,0); xOpList := opsInvolvingX lc
+ null xOpList => [fcn(ups) :: UPS]
+ opInOpList?("log" :: SY,xOpList) =>
+ stateProblem(fcnName,"logs in constant coefficient")
+ contOnReals? fcnName => [fcn(ups) :: UPS]
+ stateProblem(fcnName,"x in constant coefficient")
+
+ applyBddIfCan(fe,fcn,arg,fcnName,posCheck?,atanFlag) ==
+ -- converts fcn(arg) to a generalized power series, where the
+ -- function fcn is bounded for real values
+ -- if fcn(arg) has an essential singularity as a complex
+ -- function, we return fcn(arg) as a monomial of degree 0
+ (ups := iExprToGenUPS(arg,posCheck?,atanFlag)) case %problem =>
+ trouble := ups.%problem
+ trouble.prob = "essential singularity" => [monomial(fe,0)$UPS]
+ ups
+ (ans := fcn(ups.%series)) case "failed" => [monomial(fe,0)$UPS]
+ [ans :: UPS]
+
+ tranToGenUPS(ker,arg,posCheck?,atanFlag) ==
+ -- converts op(arg) to a power series for certain functions
+ -- op in trig or hyperbolic trig categories
+ -- N.B. when this function is called, 'k2elem' will have been
+ -- applied, so the following functions cannot appear:
+ -- tan, cot, sec, csc, sinh, cosh, tanh, coth, sech, csch
+ is?(ker,"sin" :: SY) =>
+ applyBddIfCan(ker :: FE,sinIfCan,arg,"sin",posCheck?,atanFlag)
+ is?(ker,"cos" :: SY) =>
+ applyBddIfCan(ker :: FE,cosIfCan,arg,"cos",posCheck?,atanFlag)
+ is?(ker,"asin" :: SY) =>
+ genUPSApplyIfCan(asinIfCan,arg,"asin",posCheck?,atanFlag)
+ is?(ker,"acos" :: SY) =>
+ genUPSApplyIfCan(acosIfCan,arg,"acos",posCheck?,atanFlag)
+ is?(ker,"atan" :: SY) =>
+ atancotToGenUPS(ker :: FE,arg,posCheck?,atanFlag,1)
+ is?(ker,"acot" :: SY) =>
+ atancotToGenUPS(ker :: FE,arg,posCheck?,atanFlag,-1)
+ is?(ker,"asec" :: SY) =>
+ genUPSApplyIfCan(asecIfCan,arg,"asec",posCheck?,atanFlag)
+ is?(ker,"acsc" :: SY) =>
+ genUPSApplyIfCan(acscIfCan,arg,"acsc",posCheck?,atanFlag)
+ is?(ker,"asinh" :: SY) =>
+ genUPSApplyIfCan(asinhIfCan,arg,"asinh",posCheck?,atanFlag)
+ is?(ker,"acosh" :: SY) =>
+ genUPSApplyIfCan(acoshIfCan,arg,"acosh",posCheck?,atanFlag)
+ is?(ker,"atanh" :: SY) =>
+ genUPSApplyIfCan(atanhIfCan,arg,"atanh",posCheck?,atanFlag)
+ is?(ker,"acoth" :: SY) =>
+ genUPSApplyIfCan(acothIfCan,arg,"acoth",posCheck?,atanFlag)
+ is?(ker,"asech" :: SY) =>
+ genUPSApplyIfCan(asechIfCan,arg,"asech",posCheck?,atanFlag)
+ is?(ker,"acsch" :: SY) =>
+ genUPSApplyIfCan(acschIfCan,arg,"acsch",posCheck?,atanFlag)
+ stateProblem(string name ker,"unknown kernel")
+
+ powToGenUPS(args,posCheck?,atanFlag) ==
+ -- converts a power f(x) ** g(x) to a generalized power series
+ (logBase := logToGenUPS(first args,posCheck?,atanFlag)) case %problem =>
+ logBase
+ expon := iExprToGenUPS(second args,posCheck?,atanFlag)
+ expon case %problem => expon
+ expGenUPS((expon.%series) * (logBase.%series),posCheck?,atanFlag)
+
+@
+\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>>
+
+<<package FS2UPS FunctionSpaceToUnivariatePowerSeries>>
+@
+\eject
+\begin{thebibliography}{99}
+\bibitem{1} nothing
+\end{thebibliography}
+\end{document}