1 ;;; -*- Mode: Lisp; Package: Maxima; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; The data in this file contains enhancments. ;;;;;
5 ;;; Copyright (c) 1984,1987 by William Schelter,University of Texas ;;;;;
6 ;;; All rights reserved ;;;;;
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ;;; (c) Copyright 1982 Massachusetts Institute of Technology ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13 (macsyma-module series
)
15 (declare-top (special var
*n
*a
*m
*c
*index $cauchysum
*gcd
*
16 nn
* dn
* $ratsimpexpons
*infsumsimp
*roots
*failures
17 *ratexp
*var usexp $verbose ans
*trigred
18 *form indl
*noexpand $ratexpand
))
20 (load-macsyma-macros rzmac
)
22 ;;******************************************************************************
24 ;;******************************************************************************
26 (defmfun $powerseries
(expr var pt
)
27 (when (and (atom var
) (not (symbolp var
)))
28 (improper-arg-err var
'$powerseries
))
29 (powerseries expr var pt
))
31 ;; An error that can be raised deep in the bowels of POWERSERIES that we'll
32 ;; catch and return a noun form.
33 (define-condition powerseries-expansion-error
(error)
34 ((message :initarg
:message
:initform nil
)))
36 (defun powerseries-expansion-error (&optional message
&rest arguments
)
37 (error 'powerseries-expansion-error
38 :message
(when message
(cons message arguments
))))
40 ;; The top-level routine for $powerseries, which calls this function after
41 ;; spotting invalid arguments.
42 (defun powerseries (expr var pt
)
44 (if (and (signp e pt
) (symbolp var
))
48 (sbstpt expr
'x var var
))
50 (sbstpt expr
(m// 1 'x
) var
(div* 1 var
)))
52 (sbstpt expr
(m- (m// 1 'x
)) var
(m- (div* 1 var
))))
54 (sbstpt expr
(m+ 'x pt
) var
(simplifya (m- var pt
) nil
)))))
56 ;; If expansion fails, print a message in verbose mode but then return a
58 (powerseries-expansion-error (e)
60 (mtell (intl:gettext
"Failed to expand ~M with respect to ~M at ~M.~%")
62 (with-slots (message) e
66 (apply #'mtell message
))))
67 `((%powerseries
) ,expr
,var
,pt
))))
69 ;; Return a list of the terms in the expression that are used as integration or
70 ;; differentiation variables.
71 (defun intdiff-vars-in-expr (expr)
75 ;; Arguably, if this is a definite integral, the variable isn't free so we
76 ;; shouldn't return it. But maxima-substitute doesn't know about bound
77 ;; variables and this is a helper function to avoid silly substitutions.
78 ((eq (caar expr
) '%integrate
) (list (caddr expr
)))
80 ((eq (caar expr
) '%derivative
)
81 (loop for x in
(cddr expr
) by
#'cddr collecting x
))
84 (reduce #'union
(cdr expr
) :key
#'intdiff-vars-in-expr
))))
86 ;; Series expand EXP after substituting in SEXP for VAR1, the main
87 ;; variable. SEXP should be an expression in 'X and 'X will be replaced by a
88 ;; gensym for the calculation. When the expansion is done, substitute USEXP for
90 (defun sbstpt (exp sexp var1 usexp
)
92 (sub-exp (maxima-substitute (subst var
'x sexp
) var1 exp
))
93 (expanded (seriesexpand* sub-exp
)))
94 ;; If we've ended up with diff(foo, var) then we give up (we can't
95 ;; substitute arbitrary expressions for the differentiation / integration
97 (if (memq var
(intdiff-vars-in-expr expanded
))
98 (powerseries-expansion-error
100 Couldn't make substitution to evaluate at the given point because the~%~
101 power series expansion contained the expansion variable as an~%~
102 integration / differentiation variable."))
103 (maxima-substitute usexp var expanded
))))
105 (defun seriesexpand* (x)
106 (let ((*index
(gensumindex)) *n
*a
*m
*c
107 ($cauchysum t
) ($ratsimpexpons t
)
108 $ratexpand
*infsumsimp
*ratexp
*trigred
*noexpand
)
109 (meval `(($declare
) ,*index $integer
))
111 (sp2expand (seriespass1 x
))))
114 (let ((e (cond ((and (boundp '*var
) *var
)
115 (subst (list '(mexpt) *var
*gcd
*) var e
))
117 (var (cond ((and (boundp '*var
) *var
)) (t var
))))
118 (cond ((and (boundp 'usexp
) usexp
)
124 (list '(mlabel) nil
(out-of e
))))
126 (defun seriespass1 (e)
127 (let ((w (sratsimp (sp1 e
))))
131 (mtell (intl:gettext
"powerseries: first simplification returned ~%"))
136 ;;*****************************************************************************
137 ;; pass two expansion phase
138 ;;*****************************************************************************
141 (defun sp2expand (exp)
142 (cond ((or (free exp var
) (atom exp
)) exp
)
143 ((mbagp exp
) (cons (car exp
) (mapcar #'sp2expand
(cdr exp
))))
144 ((eq (caar exp
) 'mplus
) (m+l
(mapcar #'sp2expand
(cdr exp
)))) ;was below 'mtimes test--fixes powerseries(1+x^n,x,0)
145 ((sratp exp var
) (ratexp exp
))
146 ((eq (caar exp
) 'mexpt
) (sp2expt exp
))
147 ((zl-get (caar exp
) 'sp2
) (sp2sub (sp2trig exp
) (cadr exp
)))
148 ((eq (caar exp
) 'mtimes
) (m*l
(mapcar #'sp2expand
(cdr exp
))))
149 ((eq (caar exp
) '%log
) (sp2log (cadr exp
)))
150 ((eq (caar exp
) '%derivative
) (sp2diff (cadr exp
) (cddr exp
)))
151 ((eq (caar exp
) '%integrate
)
152 (sp2integ (cadr exp
) (caddr exp
) (cdddr exp
)))
153 ((member (caar exp
) '(%product %sum
) :test
#'eq
)
154 (list* (car exp
) (sp2expand (cadr exp
)) (cddr exp
)))
157 (m^
(list '(mfactorial) *index
) -
1)
158 (list '(%at
) (list '(%derivative
) exp var
*index
)
159 (list '(mequal) var
0)))
162 (defun sp2sub (s exp
)
163 (unless (smono exp var
)
164 (powerseries-expansion-error
165 (intl:gettext
"Can't substitute the value~%~M~%~
166 into another expansion because it isn't a monomial in the~
167 expansion variable.")
169 (maxima-substitute exp
'sp2var
(simplify s
)))
174 (mtell (intl:gettext
"powerseries: attempt rational function expansion of~%~M")
175 (list '(mlabel) nil exp
)))
176 (multiple-value-call #'sratexpnd
(numden exp
))))
178 (defun sratexpnd (n d
)
179 (let ((ans (list nil
))
181 ;; A pattern that matches cc*(c*x^m + a)^n
183 '((mtimes) ((coefftt) (cc not-zero-free var
))
184 ((mexpt) ((mplus) ((coeffpt)
185 (w m1
((mexpt) (x equal var
)
186 (m not-zero-free var
)))
188 ((coeffpp) (a freevar
)))
189 (n not-zero-free var
)))))
191 (declare (special *splist
*))
192 (cond ((and (not (equal n
1)) (smono n var
))
193 (m* n
(sratexpnd 1 d
)))
198 (m// (srbinexpnd (cdr ans
)) d
))
200 (powerseries-expansion-error))))
203 (m+l
(mapcar #'(lambda (q) (div* q d
)) (cdr n
))))
205 ((not (equal 1 (setq *gcd
* (ggcd (nconc (exlist n
) (exlist d
))))))
206 (sratsubst *gcd
* n d
))
208 ;; Rational expansion theorem for distinct roots. The main point of
209 ;; the POLY? test here is that it guarantees $HIPOW will give the
210 ;; correct degrees (in particular, it only allows known non-negative
211 ;; integer exponents)
214 (> ($hipow d var
) ($hipow n var
))
215 (has-distinct-nonzero-roots-p d var
))
216 (expand-distinct-roots n d
))
218 ;; The SRBINEXPND call above dealt with expressions of the form
219 ;; cc*(c*x^m+a)^n. Here, we deal with b/(cc*(c*x^m+a)^n). If you
220 ;; explicitly write a polynomial like that, the simplifier will
221 ;; rewrite it as (..)^(-n) before it gets here, but things like
222 ;; 1/sqrt(x+1) won't have been rewritten successfully, so we catch
225 (prog2 (setq d
(let (($ratfac t
))
226 (ratdisrep ($rat
(factor d
) var
))))
228 ;; We had num/den and LINPAT matched den. We need to replace cc
229 ;; with num/cc and n with -n.
230 (setf (cdr (assoc 'n ans
)) (m- (cdr (assoc 'n ans
)))
231 (cdr (assoc 'cc ans
)) (m// n
(cdr (assoc 'cc ans
))))
232 (srbinexpnd (cdr ans
)))
235 ;; *RATEXP is set by RATEXPAND1, which we call to do the general
236 ;; expansion below. This check makes sure we can't end up recursing
239 (powerseries-expansion-error))
241 ;; Look for a power of var (a zero root) as a term. We already know
242 ;; that d isn't a monomial, so we can only have a zero root if d is
243 ;; a product. We also know that d is not atomic because otherwise
244 ;; we'd have taken the (free d var) clause above.
246 (and (eq (caar d
) 'mtimes
)
247 (find-if (lambda (factor)
250 (eq (cadr factor
) var
))))
254 (let ((other-factors (remove zero-root
(cdr d
)
255 :test
#'eq
:count
1)))
256 (m* (sratexpnd n
(m*l other-factors
))
257 `((mexpt) ,zero-root -
1)))))))))
259 ; is a sum with index and bounds from psp2form
260 (defun psp2formp (exp)
263 (eq (caar exp
) '%sum
)
264 (eq (caddr exp
) *index
)
266 (eq (cadr (cdddr exp
)) '$inf
)))
268 ;; A stripped down version of (sumcontract (intosum EXP)). Coalesce occurrences
269 ;; of ((%SUM) <FOO> *INDEX 0 '$INF), which are allowed to start with
270 ;; multiplicative constants. So something like
272 ;; sum(a(i), i, 0, inf) + 2*sum(b(i), i, 0, inf) + c
276 ;; sum(a(i) + 2*b(i), i, 0, inf) + c
278 ;; This is good enough to collect up the multiple infinite sums that you get
279 ;; when expanding a power series by partial fractions. It's (obviously) not
280 ;; clever enough to rename index variables or adjust ranges.
282 (defun psp2foldsum (exp)
284 (mtell (intl:gettext
"powerseries: preparing to fold sums~%"))
287 (multiple-value-bind (sums others
)
288 (partition-by (lambda (e)
291 (psp2formp (caddr e
)))))
294 ;; If there were no sums, just return the original expression.
296 ;; Otherwise contract the sums
299 (m+l
(mapcar (lambda (e)
300 (if (eq (caar e
) 'mtimes
)
301 (m* (cadr e
) (cadr (caddr e
)))
307 (m+l
(cons contracted others
)))))))
309 ; solve returns a list: (soln mult soln mult ...)
310 ; distinct-nonzero-roots-p returns true if every
311 ; soln is not nonzero and every mult is 1
312 (defun distinct-nonzero-roots-p (roots)
315 ;; Check all roots have multiplicity one and are nonzero.
317 for
(root mult
) on roots by
#'cddr
318 always
(and (eql 1 mult
)
319 (eq ($sign
`((mabs) ,(caddr root
))) '$pos
)))
321 ;; Now check that the roots are genuinely different from one another
322 ;; (eg. if I have two symbolic roots, A and B, then I don't need to know
323 ;; the values of A and B, but I do need to know that they aren't equal)
325 for rest on roots by
#'cddr
327 for b in
(cddr rest
) by
#'cddr
330 ($sign
`((mabs) ,(m- (caddr b
) (caddr a
))))))))))
332 ; returns t if polynomial poly in variable var has all distinct roots
333 (defun has-distinct-nonzero-roots-p (poly var
)
338 (distinct-nonzero-roots-p *roots
))))
340 ; Rational Expansion Theorem for Distinct Roots
341 ; Graham, Knuth, Patashnik, "Concrete Math" 2nd ed, p 340
343 ; If R(z) = P(z)/Q(z), where Q(z) = q0*(1-r_1*z)*...*(1-r_l*z) and the
344 ; numbers (r_1 ... r_l) are distinct, and if P(z) is a polynomial of degree less
346 ; [z^n]R(z) = a_1*r_1^n + ... + a_l*r_l^n, where a_k = -r_k*P(1/r_k)/Q'(1/r_k)
347 (defun expand-distinct-roots (num den
)
351 (cond (*failures
(error "EXPAND-DISTINCT-ROOTS: failed to solve for roots."))
352 ((distinct-nonzero-roots-p *roots
)
353 (psp2form (m+l
(mapcar #'(lambda (r)
356 (mtell (intl:gettext
"powerseries: for root:~%"))
358 (mtell (intl:gettext
"powerseries: numerator at root =~%"))
359 (show-exp (maxima-substitute r var num
))
360 (mtell (intl:gettext
"powerseries: first derivative of denominator at root =~%"))
361 (show-exp (maxima-substitute r var
($diff den var
)))))
364 (maxima-substitute r var num
)
365 (m// 1 (maxima-substitute r var
($diff den var
)))
366 (m^
(m// 1 r
) *index
)))
367 (mapcar #'caddr
(remove-mult *roots
))))
369 (t (error "EXPAND-DISTINCT-ROOTS: roots are not distinct.~%")))))
371 ;; Try to expand N/D as a power series in VAR about 0.
373 ;; Can safely assume that D has no zero roots (we remove them in SRATEXPND
374 ;; before calling this function).
375 (defun ratexpand1 (n d
)
378 "powerseries: attempt partial fraction expansion of "))
379 (show-exp (list '(mquotient) n d
))
383 ;; *RATEXP serves as a recursion guard: if SRATEXPND is about to call us
384 ;; *recursively, the flag will be set and it gives up instead.
386 (fractions ($partfrac
(div* n d
) var
)))
388 ;; If $PARTFRAC succeeds, it will return a sum of terms. In that case,
389 ;; expand each one (which we assume is going to be easier than what we
390 ;; started with) and try to fold the result into a single power series sum
394 (mtell (intl:gettext
"which is ~%"))
395 (show-exp fractions
))
396 (psp2foldsum (m+l
(mapcar #'ratexp
(cdr fractions
)))))
398 ;; If that didn't work, maybe it's because the numerator messed things
399 ;; up. If we're really lucky, the numerator is actually a polynomial
400 ;; though. In that case, factor it out and do an expansion of 1/d on its
405 "powerseries: partial fraction expansion failed, ~
406 expanding denominator only.~%")))
407 (m* n
(ratexp (m// 1 d
))))
409 ;; If n is complicated, there's not really much we can do to make further
410 ;; progress, so give up and return a noun form.
411 (t (powerseries-expansion-error
412 (intl:gettext
"Partial fraction expansion failed"))))))
414 (defun sratsubst (gcd num den
)
416 (prog2 (mtell (intl:gettext
"powerseries: substituting for the occurrences of"))
417 (show-exp (list '(mexpt) var gcd
))
418 (mtell (intl:gettext
"in"))
419 (show-exp (list '(mquotient) num den
))
422 (funcall #'(lambda (var* *var
)
423 (setq num
(maxima-substitute (m^ var
* (m^ gcd -
1)) *var num
)
424 den
(maxima-substitute (m^ var
* (m^ gcd -
1)) *var den
))
425 (maxima-substitute (m^
*var gcd
) var
*
426 (funcall #'(lambda (var) (sratexpnd num den
)) var
*)))
431 ((null (cdr l
)) (car l
))
432 ((equal 1 (car l
)) 1)
433 (t ($gcd
(ggcd (cdr l
)) (car l
)))))
436 (cond ((null exp
) nil
)
438 (and (eq exp var
) (ncons 1)))
439 ((and (not (atom (car exp
))) (eq (caar exp
) 'mplus
))
441 ((smono (car exp
) var
)
442 (cond ((equal *n
0) (exlist (cdr exp
)))
443 (t (cons *n
(exlist (cdr exp
))))))
444 (t (exlist (cdr exp
)))))
446 ;; Perform a binomial expansion of (c x^m + a)^n.
448 ;; If n isn't known to be an integer, we'd like to write a sum of terms
450 ;; x^(mk) c^k a^(n-k) / ((n+1) beta(n-k+1, k+1))
452 ;; But we have to be a bit careful: it only works if c, x and a are
453 ;; nonzero. (Otherwise the simplifier quite reasonably simplifies each of the
454 ;; terms to zero, because the only nonzero term has the undefined term 0^0 in
457 ;; If we're not sure whether cx is zero, we can just split out the first term
458 ;; from the sum. If a is definitely zero, we can just write down a single
459 ;; term. If we're not sure about a, it's a bit more difficult: if we know that n
460 ;; is a positive integer, the sum is finite (and has at least two terms) and we
461 ;; can just split off the last term. Otherwise, give up.
462 (defun srbinexpnd (ans)
463 (alist-bind (n a m c cc x
) ans
465 (if (and (integerp n
) (minusp n
))
466 (srintegexpd (neg n
) a m c
)
467 (let ((sgn-a ($sign
(list '(mabs) a
)))
468 (sgn-cx ($sign
`((mabs) ((mtimes) ,c
,x
))))
471 (m// (m* (m^ var
(m* m
*index
))
473 (m^ a
(m- n
*index
)))
474 (m* (list '($beta
) (m- n
(m1- *index
)) (m1+ *index
))
477 ((eq sgn-n
'$zero
) 1)
478 ((eq sgn-cx
'$zero
) (m^ a n
))
479 ((eq sgn-a
'$zero
) (m* (m^ c n
) (m^ x
(m* m n
))))
481 ((and (eq sgn-a
'$pos
) (eq sgn-cx
'$pos
))
482 (if (and ($featurep n
'$integer
)
483 (memq sgn-n
'($pos $pz
)))
484 `((%sum
) ,general-term
,*index
0 ,n
)
485 `((%sum
) ,general-term
,*index
0 $inf
)))
488 (m+ (m^ a n
) `((%sum
) ,general-term
,*index
1 $inf
)))
490 ((and ($featurep n
'$integer
) (eq sgn-n
'$pos
))
491 (m+ `((%sum
) ,general-term
,*index
0 ,(m1- n
))
492 (m* (m^ c n
) (m^ x
(m* n m
)))))
495 (powerseries-expansion-error
497 "Couldn't expand binomial~%~M~%~
498 as we didn't know which terms were nonzero.")))))))))
500 (defun psp2form (coeff exp bas
)
501 (list '(%sum
) (m* coeff
(m^ var exp
)) *index bas
'$inf
))
503 (defun srintegexpd (n a m c
)
505 (prog2 (mtell (intl:gettext
"powerseries: apply rule for expressions of ~%"))
506 (show-exp '((mexpt) ((mplus) $a
((mtimes) $c
((mexpt) $var $m
)))
508 (mtell (intl:gettext
"powerseries: here we have"))
509 (show-exp (list '(mlist) (list '(mequal) '$n n
) (list '(mequal) '$a a
)
510 (list '(mequal) '$c c
) (list '(mequal) '$m m
)))))
513 (m* (m^ a
(m* -
1 (m+ 1 *index
)))
514 (m^
(m* -
1 c
) *index
))
515 (if (equal m
1) *index
(m* *index m
))
518 (psp2form (m* (m+ 1 *index
)
519 (m^ a
(m* -
1 (m+ 2 *index
)))
520 (m^
(m* -
1 c
) *index
))
521 (if (equal m
1) *index
(m* *index m
))
523 (t (psp2form (m* (do ((nn (1- n
) (1- nn
))
524 (l nil
(cons (list '(mplus) *index nn
) l
)))
526 (m*l
(cons (m// 1 (factorial (1- n
))) l
))))
527 (m^
(m* -
1 c
(m^ a -
1)) *index
)
529 (if (equal m
1) *index
(m* *index m
))
534 ((member (caar a
) '(mplus mtimes
) :test
#'eq
) (sandmap (cdr a
)))
535 ((eq (caar a
) 'mexpt
)
536 (cond ((free (cadr a
) var
) (free (caddr a
) var
))
538 ((and (free (caddr a
) var
) (sratp (cadr a
) var
)))))
539 (t (and (free (mop a
) var
) (every #'(lambda (s) (free s var
)) (margs a
))))))
541 (defun sandmap (l) (or (null l
) (and (sratp (car l
) var
) (sandmap (cdr l
)))))
543 (defun sp2trig (exp) (subst *index
'*index
(zl-get (caar exp
) 'sp2
)))
545 ;; Take an expression, EXPR, and try to write it as a + b*VAR^c. On success,
546 ;; returns (VALUES A B C). On failure, raise a powerseries-expansion-error.
548 ;; One way to do this would be to call $EXPAND and look at the results, but that
549 ;; might be rather slow - if the expression is something like (1+x)^10, expand
550 ;; will take ages and won't help very much. Another approach would be to put it
551 ;; into CRE form, but that would be a mistake if the input was something like
554 ;; Instead, we're a bit stupider: we walk through the expression tree. If we see
555 ;; anything other than +,* and ^, we give up. If we find we have more than one
556 ;; different exponent in our terms, we give up.
558 ;; Obviously, there are always examples where this won't work, but $EXPAND will
559 ;; (something like (x+1)^2 - x^2, for example), but I'm assuming that this won't
560 ;; be something we encounter in practice.
561 (defun split-two-term-poly (expr)
571 ((eq (caar expr
) 'mplus
)
573 (dolist (arg (cdr expr
))
574 (multiple-value-bind (aa bb cc
) (split-two-term-poly arg
)
578 ;; Is this the first nonzero power we've seen?
581 ;; Is this another term with the same power as one we've seen
585 ;; Otherwise, give up.
587 (powerseries-expansion-error))))))
590 ((eq (caar expr
) 'mtimes
)
591 (let ((prod 1) a b c
)
592 (dolist (arg (cdr expr
))
593 (multiple-value-bind (aa bb cc
) (split-two-term-poly arg
)
596 (setf prod
(m* prod aa
)))
598 (setf a aa b bb c cc
))
599 ;; This is the second term (a + b*x^c), so we know we'll end up
600 ;; with mixed terms. (We don't try to spot e.g. (1-x)*(1+x))
602 (powerseries-expansion-error)))))
605 (values (m* a prod
) (m* b prod
) c
)
608 ((eq (caar expr
) 'mexpt
)
609 ;; We know that EXPR isn't free of VAR. Check that VAR isn't in the
611 (unless (freeof var
(caddr expr
))
612 (powerseries-expansion-error))
613 (multiple-value-bind (a b c
) (split-two-term-poly (cadr expr
))
616 (values 0 (m^ b
(caddr expr
)) (m* c
(caddr expr
))))
618 (values (m^ a
(caddr expr
)) 0 1))
620 (powerseries-expansion-error)))))
623 (powerseries-expansion-error))))
625 ;; Try to expand log(e) using the series for log(1+x).
627 ;; The basic idea is that if a is nonzero then
629 ;; log(a + b*x^k) = log(a*(1 + b/a*x^k))
630 ;; = log(a) + log(1 + b/a*x^k)
632 ;; and we know a series for that.
635 ((or (atom e
) (free e var
))
638 (multiple-value-bind (a b k
)
639 (split-two-term-poly (specdisrep e
))
640 ;; If a is zero, we can't expand
641 (unless (eq '$positive
642 (asksign (list '(mabs) a
)))
643 (powerseries-expansion-error))
645 (let* ((coeff (m* b
(m^ a -
1)))
646 (coeff-sign ($sign coeff
))
648 ;; If we know that the coefficient is not positive, switch the series
649 ;; around (which gets rid of some ugly minus signs). If we're not sure,
650 ;; but the cofficient "looks negative" (so is something like -7*k),
651 ;; switch it around too.
653 ((member coeff-sign
'($neg $nz
))
654 (setf negate-coeff-p t
656 ((and (not (member coeff-sign
'($pos $pz
)))
658 (numberp (cadr coeff
))
659 (minusp (cadr coeff
)))
660 (setq negate-coeff-p t
662 (list* (car coeff
) (- (cadr coeff
)) (cddr coeff
))
666 (m^
(if negate-coeff-p
667 (m* coeff
(m^ var k
))
668 (m* -
1 coeff
(m^ var k
)))
674 (let ((base (cadr exp
))
677 ;; Do (a^b)^c = a^(bc) when c is a number
678 ((and (numberp power
) (mexptp base
))
679 (sp2expt (m^
(cadr base
)
680 (m* power
(caddr base
)))))
682 ;; If a positive power which is free of the expansion variable and less
683 ;; than the maximum expansion power then expand the base and do the
684 ;; multiplication explicitly.
685 ((and (free power var
)
688 (m*l
(dup (sp2expand base
) power
)))
690 ;; If the base is free of VAR, rewrite as a^b = e^(b log(a)) if necessary
691 ;; and then use the tabulated expansion of exp(x). If POWER is a sum then
692 ;; do the rewrite a^(b+c) = a^b a^c
695 (subst *index
'*index
698 (subst `((mtimes) ((mlog) ,base
) sp2var
) 'sp2var
699 (get 'mexpt
'sp2
))))))
701 ((not (mplusp power
))
702 (sp2sub expansion power
))
705 (m*l
(mapcar (lambda (summand) (sp2sub expansion summand
))
708 (t (powerseries-expansion-error)))))
713 (cons x
(dup x
(1- %n
)))))
715 (defun sp2diff (exp l
)
718 (sp3form (sp2expand exp
) (cons '(%derivative
) l
)))
720 ;; If the expression isn't free of VAR, we know how to expand the result if
721 ;; the orders of differentiation are all explicit non-negative
722 ;; integers. Otherwise, give up.
723 (let ((indl) (remaining-derivs))
725 for
(y order
) on l by
#'cddr
728 ((not (typep order
'(integer 0)))
729 (powerseries-expansion-error))
732 (setf remaining-derivs
(list* order y remaining-derivs
)))
737 (setf exp
(sp2diff1 (sp2expand exp
) nil nil
))))))
740 (sp3form exp
`((%derivative
)
741 ,@(nreverse remaining-derivs
)))
744 (defun sp2diff1 (exp ind lol
) ;ind is a list of the indices
745 ;lol is a list of the lower limits
746 (cond ((atom exp
) (sdiff exp var
))
747 ((eq (caar exp
) 'mplus
)
749 (mapcar #'(lambda (q) (sp2diff1 q ind lol
))
751 ((eq (caar exp
) '%sum
)
752 (setq indl
(cons (copy-list (cddr exp
)) indl
))
754 (cons (caddr exp
) ind
)
755 (cons (cadddr exp
) lol
)))
756 (t (sp2diff2 exp ind lol
))))
758 (defun sp2diff2 (exp ind lol
)
760 (setq e
(m2 exp
'((mtimes) ((coefftt) (fr freevar
))
761 ((coefftt) (e true
))))
762 fr
(cdr (assoc 'fr e
:test
#'eq
))
763 e
(cdr (assoc 'e e
:test
#'eq
)))
765 (cond ((and (mexptp e
) (eq (cadr e
) var
))
766 (cond ((equal 0 (mbinding (ind lol
)
767 (meval (m* fr
(caddr e
)))))
768 (m* (sp3substp1 ind ind
(m* fr
(caddr e
))) e
))
769 ((mgrp 1 (mbinding (ind lol
)
770 (simplify (mevalatoms (caddr e
)))))
771 (m* fr
(caddr e
) (m^
(cadr e
) (m- (caddr e
) 1))))
772 (t (sdiff exp var
))))
773 (t (sdiff exp var
))))))
775 (defun sp2integ (exp v l
)
778 (sp2integ1 ($expand
(sp2expand exp
)))
779 (sp3form (sp2expand exp
) (list '(%integrate
) v
)))
780 (sp2integ2 exp v
(car l
) (cadr l
))))
782 (defun sp2integ1 (exp)
784 (cond ((ratp exp var
) (ratint exp var
))
785 ((eq (caar exp
) 'mplus
)
786 (cons '(mplus) (mapcar #'sp2integ1
(cdr exp
))))
787 ((and (eq (caar exp
) 'mtimes
)
788 (prog2 (setq pair
(partition exp var
1))
789 (not (equal (car pair
) 1))))
790 (mul2* (car pair
) (sp2integ1 (cdr pair
))))
791 ((and (eq (caar exp
) 'mtimes
)
792 (prog2 (setq exp
($intosum exp
)) nil
)))
793 ((or (not (eq (caar exp
) '%sum
)) (not (isinop (cadr exp
) '%sum
)))
795 (t (let ((indl (ncons (cddr exp
))))
796 (sp2integ12 (cadr exp
) (ncons (caddr exp
)) (ncons (cadddr exp
))))))))
798 (defun sp2integ12 (exp ind lol
)
800 (sp3reconst (ratint exp var
)))
801 ((eq (caar exp
) 'mplus
)
803 (m+l
(mapcar #'(lambda (q) (sp2integ13 q ind lol
))
805 ((eq (caar exp
) '%sum
)
806 (setq indl
(cons (cddr exp
) indl
))
807 (sp2integ12 (cadr exp
)
808 (cons (caddr exp
) ind
)
809 (cons (cadddr exp
) lol
)))
810 (t (sp3reconst (sp2integ13 exp ind lol
)))))
812 (defun sp2integ13 (exp ind lol
)
814 (setq e
(m2 exp
'((mtimes) ((coefftt) (fr freevar
))
815 ((coefftt) (e true
))))
816 fr
(cdr (assoc 'fr e
:test
#'eq
))
817 e
(cdr (assoc 'e e
:test
#'eq
)))
818 (cond ((and (mexptp e
) (eq (cadr e
) var
))
819 (cond ((mgrp -
1 (mbinding (ind lol
)
821 (m* (sp3substpn ind ind
(m* fr
(caddr e
)) -
1) e
))
822 (t (sinint exp var
))))
823 (t (sinint exp var
)))))
825 ;; Substitute NEW for OLD in the expression tree EXPRESSION. This is a bit like
826 ;; MAXIMA-SUBSTITUTE, except it assumes OLD is a symbol. But the important bit
827 ;; is that it is bright enough to avoid substituting for bound variables in
828 ;; %INTEGRATE and %AT forms: even though the symbol might have the same name,
829 ;; it's thought of as a logically different variable.
831 ;; The function returns two values: the new expression and a flag that says
832 ;; whether that expression has changed. If the flag is true on a recursive call,
833 ;; int-diff-substitute resimplifies the result.
834 (defun int-diff-substitute (new old expression
)
836 ((eq expression old
) (values new t
))
837 ((atom expression
) (values expression nil
))
839 (let ((op (caar expression
))
840 (args (cdr expression
)))
841 (if (or (and (eq op
'%integrate
) (eq old
(second args
)))
843 (not (atom (second args
)))
844 (if (eq (caar (second args
)) 'mlist
)
845 ;; (second args) looks like ((mlist) ((mequal) ...) ...)
846 (memq old
(mapcar #'second
(rest (second args
))))
847 ;; (second args) looks like ((mequal) ...)
848 (eq old
(second (second args
))))))
849 (values expression nil
)
850 (let* ((some-changed-p nil
)
853 (multiple-value-bind (new-val changed-p
)
854 (int-diff-substitute new old x
)
856 (setf some-changed-p t
))
859 (if (not some-changed-p
)
860 (values expression nil
)
862 (simplifya (cons (list op
) new-args
) nil
) t
))))))))
864 ;; Expand a definite integral, integrate(exp, v, lo, hi).
865 (defun sp2integ2 (exp v lo hi
)
866 ;; Deal with the case where the integration variable is also the expansion
867 ;; variable. Something's a bit odd if we're doing this, but we assume that the
868 ;; aliasing was accidental and that the (bound) integration variable should be
869 ;; called something else.
872 exp
(subst v var exp
)))
874 (when (boundp '*sp2integ-recursion-guard
*)
875 (powerseries-expansion-error
876 (intl:gettext
"Recursion when trying to expand the definite integral: ~M")
877 (out-of (symbol-value '*sp2integ-recursion-guard
*))))
880 ((not (and (free lo var
) (free hi var
)))
881 ;; Suppose one or both of the endpoints involves VAR. We'll give up unless
882 ;; they are monomials in VAR (because substituting a non-monomial into a
883 ;; power series is more difficult). If they *are* monomials in VAR, then we
884 ;; try to expand the integral as a power series and find an antiderivative.
885 (let ((lo-exp (sp2expand lo
))
886 (hi-exp (sp2expand hi
))
887 (*sp2integ-recursion-guard
* exp
))
888 (declare (special *sp2integ-recursion-guard
*))
890 (unless (and (smono lo-exp var
) (smono hi-exp var
))
891 (powerseries-expansion-error
892 (intl:gettext
"Endpoints of definite integral ~M are not monomials in ~
893 the expansion variable.") (out-of exp
)))
895 ;; Since this is a formal powerseries calculation, we needn't concern
896 ;; ourselves with radii of convergence here. Just expand the integrand
897 ;; about zero. (Is there something cleverer we could do?)
898 (let ((anti-derivative (sinint ($powerseries exp v
0) v
)))
899 ;; If the expansion had failed, we'd have thrown an error to top-level,
900 ;; so we can assume that ANTI-DERIVATIVE is a sum of monomials in
901 ;; V. Substituting in LO-EXP and HI-EXP, we'll get two sums of
902 ;; monomials in VAR. The difference of the two expressions isn't quite
903 ;; of the right form, but hopefully it's close enough for any other
904 ;; code that uses it.
905 (m- (int-diff-substitute hi-exp v anti-derivative
)
906 (int-diff-substitute lo-exp v anti-derivative
)))))
909 (list '(%integrate
) (subst var v exp
) var lo hi
))
912 (sp3form (sp2expand exp
)
913 (list '(%integrate
) v lo hi
)))))
915 ;;************************************************************************************
916 ;; phase three miscellaneous garbage and final simplification
917 ;;************************************************************************************
919 (defun sp3reconst (e)
920 (do ((l indl
(cdr l
)) (e e
(list* '(%sum
) e
(car l
))))
923 (defun sp3substpn (vars vals exp n
)
924 (sp3subst vars
(mapcar #'(lambda (q) (add2* q n
)) vals
) exp
))
926 (defun sp3substp1 (vars vals exp
) (sp3substpn vars vals exp
1))
928 (defun sp3subst (vars vals exp
)
929 (simplify (sublis (mapcar #'cons
(cdr vars
) (cdr vals
))
930 (subst (car vals
) (car vars
) exp
))))
932 (defun sp3form (e *form
) (sp3form1 e
))
935 (cond ((atom e
) (list* (car *form
) e
(cdr *form
)))
936 ((eq (caar e
) 'mplus
)
937 (cons '(mplus) (mapcar #'sp3form1
(cdr e
))))
939 (list* '(%sum
) (sp3form1 (cadr e
)) (cddr e
)))
940 (t (list* (car *form
) e
(cdr *form
)))))
942 ;; These are the series expansions for circular functions
947 ((mexpt) ((mfactorial) ((mplus) ((mtimes) 2 *index
) 1)) -
1)
948 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) 1)))
953 ((%sum
) ((mtimes) ((mexpt) -
1 *index
)
954 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
955 ((mexpt) sp2var
((mtimes) 2 *index
)))
960 ((%sum
) ((mtimes) ((mexpt) -
1 ((mplus) *index -
1))
961 ((mexpt) 2 ((mtimes) 2 *index
))
962 ((mplus) ((mexpt) 2 ((mtimes) 2 *index
)) -
1)
963 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
964 (($bern
) ((mtimes) 2 *index
))
965 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1)))
971 ((mexpt) -
1 ((mplus) *index -
1))
972 ((mplus) ((mexpt) 2 ((mplus) ((mtimes) 2 *index
) -
1)) -
1)
973 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
974 (($bern
) ((mtimes) 2 *index
))
975 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1)))
982 ((mexpt) 2 ((mtimes) 2 *index
))
983 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
984 (($bern
) ((mtimes) 2 *index
))
985 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1)))
990 ((%sum
) ((mtimes) ((mexpt) -
1 *index
)
991 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
992 (($euler
) ((mtimes) 2 *index
))
993 ((mexpt) sp2var
((mtimes) 2 *index
)))
997 ;; These are the series definitions of exponential functions.
1001 ((mtimes) ((mexpt) ((mfactorial) *index
) -
1) ((mexpt) sp2var
*index
))
1007 ((mexpt) ((mfactorial) ((mplus) ((mtimes) 2 *index
) 1)) -
1)
1008 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) 1)))
1014 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
1015 ((mexpt) sp2var
((mtimes) 2 *index
)))
1021 ((mtimes) ((mexpt) 4 *index
)
1022 ((mplus) ((mexpt) 4 *index
) -
1)
1023 (($bern
) ((mtimes) 2 *index
))
1024 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1))
1026 ((mfactorial) ((mtimes) 2 *index
))
1033 ((mtimes) ((mexpt) 4 *index
)
1034 (($bern
) ((mtimes) 2 *index
))
1035 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
1036 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1)))
1042 ((mtimes) (($euler
) ((mtimes) 2 *index
))
1043 ((mexpt) ((mfactorial) ((mtimes) 2 *index
)) -
1)
1044 ((mexpt) sp2var
((mtimes) 2 *index
)))
1050 ((mtimes) -
2 ((mplus) ((mexpt) 2 ((mplus) ((mtimes) 2 *index
) -
1)) -
1)
1051 ((mexpt) ((mfactorial) ((mtimes) *index
2)) -
1)
1052 (($bern
) ((mtimes) 2 *index
))
1053 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) -
1)))
1057 ;;arc trigonometric function expansions
1061 ((mtimes) ((%genfact
) ((mplus) ((mtimes) 2 *index
) -
1) *index
2)
1062 ((mexpt) ((%genfact
) ((mtimes) 2 *index
) *index
2) -
1)
1063 ((mexpt) ((mplus) ((mtimes) 2 *index
) 1) -
1)
1064 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) 1)))
1070 ((mtimes) ((mexpt) -
1 *index
)
1071 ((mexpt) ((mplus) ((mtimes) 2 *index
) 1) -
1)
1072 ((mexpt) sp2var
((mplus) ((mtimes) 2 *index
) 1)))
1076 ;; Lambert W function principal branch
1079 ((mtimes) ((mexpt) -
1 *index
)
1081 ((mexpt) *index
( (mplus) *index -
1))
1082 ((mexpt) ((mfactorial) *index
) -
1) ((mexpt) sp2var
*index
))