123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- module TayIntrf;
- %*****************************************************************
- %
- % The interface to the REDUCE simplificator
- %
- %*****************************************************************
- exports simptaylor, simpTaylor!*, taylorexpand$
- imports
- % from the REDUCE kernel:
- !*f2q, aconc!*, denr, depends, diffsq, eqcar, kernp, lastpair,
- leq, lprim, mkquote, mksq, multsq, mvar, neq, nth, numr, over,
- prepsq, revlis, reversip, simp, simp!*, subs2, subsq, typerr,
- % from the header module:
- !*tay2q, get!-degree, has!-Taylor!*, has!-TayVars,
- make!-Taylor!*, multintocoefflist, resimptaylor, TayCfPl,
- TayCfSq, TayCoeffList, TayFlags, TayMakeCoeff, TayOrig,
- TayTemplate, TayTpElOrder, TayTpElPoint,
- Taylor!-kernel!-sq!-p, taymincoeff,
- % from module Tayintro:
- replace!-nth, Taylor!-error, var!-is!-nth,
- % from module TayExpnd:
- taylorexpand,
- % from module Tayutils:
- delete!-superfluous!-coeffs,
- % from module taybasic:
- invtaylor1, quottaylor1,
- % from module Tayconv:
- prepTaylor!*;
- fluid '(!*backtrace !*precise !*taylorkeeporiginal !*taylorautocombine
- frlis!* subfg!*);
- global '(mul!*);
- comment The following statement forces all expressions to be
- re-simplified if the switch `taylorautocombine' is set to on,
- unfortunately, this is not always sufficient;
- put ('taylorautocombine, 'simpfg, '((t (rmsubs))));
- symbolic procedure simptaylor u;
- %
- % (PrefixForm) -> s.q.
- %
- % This procedure is called directly by the simplifier.
- % Its argument list must be of the form
- % (exp, [var, var0, deg, ...]),
- % where [...] indicate one or more occurences.
- % This means that exp is to be expanded w.r.t var about var0
- % up to degree deg.
- % var may also be a list of variables, which means that the
- % the expansion takes place in a homogeneous way.
- % If var0 is the special atom infinity var is replaced by 1/var
- % and the result expanded about 0.
- %
- % This procedure returns the input if it cannot expand the expression.
- %
- if remainder(length u,3) neq 1
- then Taylor!-error('wrong!-no!-args,'taylor)
- else if null subfg!* then mksq('taylor . u,1)
- else begin scalar !*precise,arglist,degree,f,ll,result,var,var0;
- %
- % Allow automatic combination of Taylor kernels.
- %
- if !*taylorautocombine and not ('taysimpsq memq mul!*)
- then mul!* := aconc!*(mul!*,'taysimpsq);
- %
- % First of all, call the simplifier on exp (i.e. car u),
- %
- f := simp!* car u;
- u := revlis cdr u; % reval instead of simp!* to handle lists
- arglist := u;
- %
- % then scan the rest of the argument list.
- %
- while not null arglist do
- << var := car arglist;
- var := if eqcar(var,'list) then cdr var else {var};
- % In principle one should use !*a2k
- % but typerr (maprin) does not correctly print the atom nil
- for each el in var collect begin
- el := simp!* el;
- if kernp el then return mvar numr el
- else typerr(prepsq el,'kernel)
- end;
- var0 := cadr arglist;
- degree := caddr arglist;
- if not fixp degree
- then typerr(degree,"order of Taylor expansion");
- arglist := cdddr arglist;
- ll := {var,var0,degree,degree + 1} . ll>>;
- %
- % Now ll is a Taylor template, i.e. of the form
- % ((var_1 var0_1 deg1 next_1) (var_2 var0_2 deg_2 next_2) ...)
- %
- result := taylorexpand(f,reversip ll);
- %
- % If the result does not contain a Taylor kernel, return the input.
- %
- return if has!-Taylor!* result then result
- else mksq('taylor . prepsq f . u,1)
- end;
- put('taylor,'simpfn,'simptaylor)$
- %symbolic procedure taylorexpand (f, ll);
- % %
- % % f is a s.q. that is expanded according to the list ll
- % % which has the form
- % % ((var_1 var0_1 deg1) (var_2 var0_2 deg_2) ...)
- % %
- % begin scalar result;
- % result := f;
- % for each el in ll do
- % %
- % % taylor1 is the function that does the real work
- % %
- % result := !*tay2q taylor1 (result, car el, cadr el, caddr el);
- % if !*taylorkeeporiginal then set!-TayOrig (mvar numr result, f);
- % return result
- % end$
- symbolic procedure taylor1 (f, varlis, var0, n);
- %
- % Taylor expands s.q. f w.r.t. varlis about var0 up to degree n.
- % var is a list of kernels, which means that the expansion
- % takes place in a homogeneous way if there is more than one
- % kernel.
- % If var0 is the special atom infinity all kernels in varlis are
- % replaced by 1/kernel. The result is then expanded about 0.
- %
- taylor1sq (if var0 eq 'infinity
- then subsq (f,
- for each krnl in varlis collect
- (krnl . list ('quotient, 1, krnl)))
- else f,
- varlis, var0, n)$
- symbolic procedure taylor1sq (f, varlis, var0, n);
- %
- % f is a standard quotient, value is a Taylor kernel.
- %
- % First check if it is a Taylor kernel
- %
- if Taylor!-kernel!-sq!-p f
- then if has!-TayVars(mvar numr f,varlis)
- %
- % special case: f has already been expanded w.r.t. var.
- %
- then taylorsamevar (mvar numr f, varlis, var0, n)
- else begin scalar y, z;
- f := mvar numr f;
- %
- % taylor2 returns a list of the form
- % ((deg1 . coeff1) (deg2 . coeff2) ... )
- % apply this to every coefficient in f.
- % car cc is the degree list of this coefficient,
- % cdr cc the coefficient itself.
- % Finally collect the new pairs
- % (degreelist . coefficient)
- %
- z :=
- for each cc in TayCoeffList f join
- for each cc2 in taylor2 (TayCfSq cc, varlis, var0, n)
- collect
- TayMakeCoeff (append (TayCfPl cc, TayCfPl cc2),
- TayCfSq cc2);
- %
- % Append the new list to the Taylor template and return.
- %
- y := append(TayTemplate f,list {varlis,var0,n,n+1});
- return make!-Taylor!* (z, y, TayOrig f, TayFlags f)
- end
- %
- % Last possible case: f is not a Taylor expression.
- % Expand it.
- %
- else make!-Taylor!* (
- taylor2 (f, varlis, var0, n),
- list {varlis,var0,n,n+1},
- if !*taylorkeeporiginal then f else nil,
- nil)$
- symbolic procedure taylor2 (f, varlis, var0, n);
- begin scalar result,oldklist;
- oldklist := get('Taylor!*,'klist);
- result := errorset (list ('taylor2e,
- mkquote f,
- mkquote varlis,
- mkquote var0,
- mkquote n),
- nil, !*backtrace);
- put('Taylor!*,'klist,oldklist);
- if atom result
- then Taylor!-error ('expansion, "(possible singularity!)")
- else return car result
- end$
- symbolic procedure taylor2e (f, varlis, var0, n);
- %
- % taylor2e expands s.q. f w.r.t. varlis about var0 up to degree n.
- % var is a list of kernels, which means that the expansion takes
- % place in a homogeneous way if there is more than one kernel.
- % The case that var0 is the special atom infinity has to be taken
- % care of by the calling function(s).
- % Expansion is carried out separately for numerator and
- % denominator. This approach has the advantage of not producing
- % complicated s.q.'s which usually appear if an s.q. is
- % differentiated. The procedure is (roughly) as follows:
- % if the denominator of f is free of var
- % then expand the numerator and divide,
- % else if the numerator is free of var expand the denominator,
- % take the reciprocal of the Taylor series and multiply,
- % else expand both and divide the two series.
- % This fails if there are nontrivial dependencies, e.g.,
- % if a variable is declared to depend on a kernel in varlis.
- % It is of course necessary that the denominator yields a unit
- % in the ring of Taylor series. If not, an error will be
- % signalled by invtaylor or quottaylor, resp.
- %
- if cdr varlis then taylor2hom (f, varlis, var0, n)
- else if denr f = 1 then taylor2f (numr f, car varlis, var0, n, t)
- else if not depends (denr f, car varlis)
- then multintocoefflist (taylor2f (numr f, car varlis, var0, n, t),
- 1 ./ denr f)
- else if numr f = 1
- then delete!-superfluous!-coeffs
- (invtaylor1 ({varlis,var0,n,n+1},
- taylor2f (denr f, car varlis, var0, n, nil)),
- 1, n)
- else if not depends (numr f, car varlis)
- then multintocoefflist
- (invtaylor1 ({varlis,var0,n,n+1},
- taylor2f (denr f, car varlis, var0, n, nil)),
- !*f2q numr f)
- else begin scalar denom; integer n1;
- denom := taylor2f (denr f, car varlis, var0, n, nil);
- n1 := n + taymincoeff denom;
- return
- delete!-superfluous!-coeffs
- (quottaylor1 ({varlis,var0,n1,n1+1},
- taylor2f (numr f, car varlis, var0, n1, t),
- denom),
- 1, n)
- end$
- symbolic procedure taylor2f (f, var, var0, n, flg);
- %
- % taylor2f is the procedure that does the actual expansion
- % of the s.f. f.
- % It returns a list of the form
- % ((deglis1 . coeff1) (deglis2 . coeff2) ... )
- % For the use of the parameter `flg' see below.
- %
- begin scalar x, y, z; integer k;
- %
- % Calculate once what is needed several times.
- % var0 eq 'infinity is a special case that has already been taken
- % care of by the calling functions by replacing var by 1/var.
- %
- if var0 eq 'infinity then var0 := 0;
- x := list (var . var0);
- y := simp list ('difference, var, var0);
- %
- % The following is a first attempt to treat expressions
- % that have poles at the expansion point.
- % Currently nothing more than factorizable poles, i.e.
- % factors in the denominator are handled.
- % We use the following trick to calculate enough terms: If the
- % first l coefficients of the Taylor series are zero we replace n
- % by n + 2l. This is necessary since we separately expand
- % numerator and denominator of an expression. If the expansion of
- % both parts starts with, say, the quadratic term we have to
- % expand them up to order (n+2) to get the correct result up to
- % order n. However, if the numerator starts with a constant term
- % instead, we have to expand up to order (n+4). It is important,
- % however, to drop the additional coefficients as soon as they are
- % no longer needed. The parameter `flg' is used here to control
- % this behaviour. The calling function must pass the value `t' if
- % it wants to inhibit the calculation of additional coefficients.
- % This is currently the case if the s.q. f has a denominator that
- % may contain the expansion variable. Otherwise `flg' is used to
- % remember if we already encountered a non-zero coefficient.
- %
- f := !*f2q f;
- z := subs2 subsq (f, x);
- if null numr z and not flg then n := n + 1 else flg := t;
- y := list TayMakeCoeff ((list list 0), z);
- k := 1;
- while k <= n and not null numr f do
- << f := multsq (diffsq (f, var), 1 ./ k);
- % k is always > 0!
- % subs2 added to simplify expressions involving roots
- z := subs2 subsq (f, x);
- if null numr z and not flg then n := n + 2 else flg := t;
- if not null numr z then y := TayMakeCoeff(list list k, z) . y;
- k := k + 1 >>;
- return reversip y
- end;
- symbolic procedure taylor2hom (f, varlis, var0, n);
- %
- % Homogeneous expansion of s.q. f wrt the variables in varlis,
- % i.e. the sum of the degrees of the kernels is varlis is <= n
- %
- if null cdr varlis then taylor2e (f, list car varlis, var0, n)
- else for each u in taylor2e (f, list car varlis, var0, n) join
- for each v in taylor2hom (cdr u,
- cdr varlis,
- var0,
- n - get!-degree TayCfPl car u)
- collect list (car TayCfPl car u . TayCfPl car v) . cdr v$
- symbolic procedure taylorsamevar (u, varlis, var0, n);
- begin scalar tp; integer mdeg, pos;
- if cdr varlis
- then Taylor!-error ('not!-implemented,
- "(homogeneous expansion in TAYLORSAMEVAR)");
- tp := TayTemplate u;
- pos := car var!-is!-nth (tp, car varlis);
- tp := nth (tp, pos);
- if TayTpElPoint tp neq var0
- then return taylor1 (if not null TayOrig u then TayOrig u
- else simp!* prepTaylor!* u,
- varlis, var0, n);
- mdeg := TayTpElOrder tp;
- if n=mdeg then return u
- else if n > mdeg
- %
- % further expansion required
- %
- then << lprim "Cannot expand further... truncated.";
- return u >> ;
- return make!-Taylor!* (
- for each cc in TayCoeffList u join
- if nth (nth (TayCfPl cc, pos), 1) > n
- then nil
- else list cc,
- replace!-nth(TayTemplate u,pos,
- {varlis,TayTpElPoint tp,n,n+1}),
- TayOrig u, TayFlags u)
- end$
- comment The `FULL' flag causes the whole term (including the
- Taylor!* symbol) to be passed to SIMPTAYLOR!* ;
- symbolic procedure simpTaylor!* u;
- if TayCoefflist u memq frlis!* or eqcar(TayCoefflist u,'!~)
- then !*tay2q u
- else <<
- %
- % Allow automatic combination of Taylor kernels.
- %
- if !*taylorautocombine and not ('taysimpsq memq mul!*)
- then mul!* := aconc!* (mul!*, 'taysimpsq);
- !*tay2q resimptaylor u >>$
- flag ('(Taylor!*), 'full)$
- put ('Taylor!*, 'simpfn, 'simpTaylor!*)$
- comment The following is necessary to properly process Taylor kernels
- in substitutions;
- flag ('(Taylor!*), 'simp0fn);
- endmodule;
- end;
|