Lisp

An dieser Stelle soll etwas ausführlicher an aktuellen Beispielen die Programmierung von Lisp Funktionen erläutert werden.

Neue Lisp Funktion combine-factors

Das Problem

Wir haben die zwei Fehlerberichte Bug ID: 2976378 - Solver doesn't finish und Bug ID: 2944431 - 0.0007s^1.874. Eine Analyse zeigt, dass Maxima ein Problem hat, die Lösung für eine Gleichung a^x * b^x = c zu finden, wobei x die Unbekannte ist, nach der die Gleichung aufgelöst werden soll. Das ist erstaunlich, da Maxima kein Problem mit der Gleichung (a * b)^x = c hat.
Hier sind die Ergebnisse von Maxima für die genannten Beispiele:

       (%i1) solve(a^x*b^x=c, x);              
                                     x   c      
       (%o1)                       [b  = --]    
                                          x     
                                         a      
       (%i2) solve((a*b)^x=c, x);              
                                      log(c)    
       (%o2)                    [x = --------]  
                                     log(a b)   
       
Maxima hat für Gleichungen der Form a * f(x)^n + b einen speziellen Algorithmus. Dieser wird für eine Gleichung der Form (a * b)^x = c angewendet. Maxima erkennt jedoch nicht, dass es sich bei der Gleichung a^x * b^x = c um ein äquivalentes Problem handelt.

Die Idee

Wenn ein Ausdruck der Form a^x * b^x in einen Ausdruck (a * b)^x transformiert wird, sollte Maxima das Problem lösen können. Die Transformation sollte möglichst Allgemein formuliert werden.

Die Lisp Funktion

Die folgende Lisp Funktion leistet die gewünschte Transformation.

(defun combine-factors (expr var1 &aux w)
  (cond ((atom expr) expr)
        ((not (eq (caar expr) 'mtimes))
         ;; Not a mtimes expression, call combine-factors recusively
         (cons (car expr)
               (mapcar #'(lambda (u) (combine-factors u var1)) (cdr expr))))
        (t
         ;; At this point we have a mtimes expression.
         (let ((factors nil) (rest nil))
           (dolist (ll (cdr expr))
             (when *debug-solve*
               (format t "~& ll = ~A~%" ll))
             (cond ((and (mexptp ll)
                         (free (cadr ll) var1)
                         (eq (caddr ll) var1))
                    (push (cadr ll) factors))
                   ((and (mexptp ll)
                         (free (cadr ll) var1)
                         (mtimesp (caddr ll))
                         (eq -1 (cadr (caddr ll)))
                         (eq var1 (caddr (caddr ll))))
                    (push (list '(mexpt simp) (cadr ll) -1) factors))
                   (t
                    ;; Not an expression of the form a^x or a^-x 
                    (push (combine-factors ll var1) rest))))
           (mul (muln rest t)
                (if (equal 1 (setq w (muln factors t)))
                    1
                    (list '(mexpt simp) w var1)))))))
       

Test der neuen Funktion

Die neue Funktion kann von einer Maxima-Kommandozeile getestet werden. Hier sind die Ergebnisse für zwei Beispiele. Die Funktion funktioniert für beliebig verschachtelte mathematische Ausdrücke.

       (%i1) ?combine\-factors(a^x*b^x,x);
                                         x
       (%o1)                        (a b)
       (%i2) ?combine\-factors(a^x*b^x/c^x+sin(a^x/b^x),x);
                               a b x        a x
       (%o2)                  (---)  + sin((-) )
                                c           b
       
Maxima findet für einen transformierten Ausdruck die gewünschte Lösung.
       (%i3) solve(?combine\-factors(a^x*b^x=c,x),x);
                                      log(c)
       (%o3)                    [x = --------]
                                     log(a b)
       

Implementation der neuen Funktion

Zuletzt wird die neue Funktion an einer geeigneten Stelle in den Algorithmus von Maxima zum Lösen von Gleichungen eingefügt. Der Bug sollte damit behoben sein. Hier ein Beispiel für ein neues Ergebnis:

       (%i4) solve(a^x*b^x=c, x);
                                      log(c)
       (%o4)                    [x = --------]
                                     log(a b)
       
Wird die Implementation genauer getestet, stellt sich heraus, dass weiterhin ein Problem mit ganzen Zahlen als Basis der Exponentiation besteht. Maxima transformiert z. B. wie gewünscht 2^x/3^x zu (2/3)^x, aber für eine Gleichung 2^x/3^x=c findet Maxima weiterhin keine Lösung.

An diesem Punkt gilt es tiefer in die Algorithmen von Maxima hineinzuschauen. Die vorhandenen Algorithmen müssen an einigen Stellen verallgemeinert werden, damit Maxima auch für ganze Zahlen Lösungen liefert. Am Ende kann auch dieses Problem gelöst werden:
       (%i5) solve(2^x/3^x=c, x);
                                      log(c)
       (%o5)                     [x = ------]
                                          2
                                      log(-)
                                          3
       
Der Programmcode ist noch nicht veröffentlicht.