# [Maxima] Simplifying functions in Maxima code

Stavros Macrakis macrakis at alum.mit.edu
Wed May 2 17:48:16 CDT 2007

```Until now, there have been two alternatives if you wanted to add a new
function with simplifying rules to Maxima: you could either work with the
pattern-matching system (tellsimp etc.) or you could write a simplifier in
Lisp code.

I have now written a little experimental package which allows you to write
simplifiers in Maxima code.  For example, here is some basic code for a
simplifying absolute value function called aabs.

/* Define simplifier */
simp_aabs(x):=
block([inflag:true],                 /* Work on internal form */
if     maybe(x>=0)=true then x      /* Must use maybe to avoid errors */
elseif maybe(x<=0)=true then -x
elseif atom(x) then simpfuncall('aabs,x)
/* simpfuncall(f,x) constructs f(x) and
marks it as already simplified. */
elseif part(x,0)="*" then map(simp_aabs,x)
else simpfuncall('aabs,x));
simplifying(aabs,simp_aabs)\$

aabs(x) => aabs(x)
aabs(-x/2) => aabs(x)/2
aabs(x^2) => x^2

So far, it is just like an evaluating function.  But here is where the
difference begins.

ex: aabs(x) => aabs(x)
subst(x^2,x,ex) => x^2        /* Subst resimplifies */

Try it out and give me feedback -- I plan to put it in the contrib section
of share when it's a bit more stable.

-s

----------------------------------------

This package, simplifying.lisp, allows the definition of simplifying
functions using the Maxima language (not Lisp).  After simplifying(f,simp_f)
is called, simp_f is called every time an expression in f(...) needs to be
simplified or resimplified.  simp_f can construct a simplified expression
f(...) using simpfuncall('f,...) or simpfunmake('f,...).

Nothing prevents the user from overriding built-in functions (with possibly
dire consequences) or from invoking simplification recursively (which is
sometimes a perfectly sensible thing to do, other times a bug.  I considered
checking for these cases, but they may sometimes be useful.

;;; Package to allow Maxima-level user-defined simplifying functions
;;;
;;; For example, suppose we want to write a step function stepfn(x)
;;; which is 0 for x<- and 1 for x>0.
;;;
;;; /* Must guarantee it is not a simplifying function before redefining */
;;; simplifying('stepfn,false)\$
;;; /* Define simplifying function */
;;; stepfn(x):=
;;;   block([prederror:false],
;;;      if is(x<=0)=true then 0
;;;      elseif is(x>0)=true then 1
;;;      else simpfuncall('stepfn,x))\$
;;; /* Declare stepfn to be simplifying */
;;; simplifying('stepfn)\$
;;;
;;; /* Test simple cases */
;;; stepfn(-x^2);      /* 0 */
;;; stepfn(x^2+1);     /* 1 */
;;; ex: stepfn(x^2);   /* stepfn(x^2) -- no simplifications apply */
;;; assume(x>0)\$
;;; ex;                /* Assumptions not consulted */
;;; resimplify(ex):=expand(ex,0,0)\$
;;; /* Force resimplification */
;;; resimplify(ex);    /* 1 */
;;; forget(x>0)\$
;;; resimplify(ex);    /* stepfn(x^2) */

;;; Utilities

(defun defined-functionp (ex)
(cond ((null ex) nil)
((symbolp ex)
(if (or (fboundp ex)
(safe-mgetl ex '(mexpr mmacro)))
t))
((and (not (atom ex))
(eq (caar ex) 'lambda))
t)
(t nil)))

;;; Declare a user Maxima function to be a simplifying function
;;; simplifying(f,g) -- uses g as the simplifier
;;; simplifying(f,false) -- removes simplifying property
;;;
;;; You can override built-in simplifiers, but it is not recommended

(defun \$simplifying (f simplifier)
(if (not (symbolp f)) (merror "Simplifying function ~s must be a symbol"
f))
(if (not (defined-functionp simplifier))
(mtell "Warning: simplifier function ~M is not defined"
simplifier))
(if (and (get f 'operators) (not (get f 'user-simplifying)))
(mtell "Warning: ~M is overriding the built-in simplifier for ~M"
simplifier f))
(cond ((null simplifier)
(setf (get f 'operators) nil)
(setf (get f 'user-simplifying) nil))
(t (setf (get f 'operators) #'user-simplifying)
(setf (get f 'user-simplifying) simplifier))))

;;; Create the expression fun(args...) and mark it as simplified.
;;; Thus, simpfuncall(sin,0) => sin(0), not 0, but resimplifying with
;;; expand(simpfuncall(sin,0)) does simplify to 0.
;;; It is generally not recommended to use this for functions with
;;; built-in simplifiers. (i.e. be very careful)
(defun \$simpfuncall (fun &rest args)
(if (not (or (symbolp fun) (\$subvarp fun)
(and (not (atom fun)) (eq (caar fun) 'lambda))))
(merror "Bad first argument to `simpfuncall': ~M" fun))
(simpcons (getopr fun) args))

(defun \$simpfunmake (fun args)
(if (not (\$listp args)) (merror "Bad second argument to `simpfunmake': ~M"
args))
(\$simpfuncall fun (cdr args)))

(defmfun simpcons (op args)
(if (symbolp op)
`((,op simp) , at args)
`((mqapply simp) ,op , at args)))

;;; The generic simplifying function for user simplification functions
(defun user-simplifying (l ignore simpflag)
(let* ((op (caar l))
(simplifier (get op 'user-simplifying))
;; args are (re)simplified *outside* the simplification fnc
(args (mapcar #'(lambda (i) (simpcheck i simpflag)) (cdr l))))
(let ( ;; args have already been resimplified if necessary
(dosimp nil))
(declare (special dosimp))
(if (defined-functionp simplifier)
(mapply simplifier args op)
(simpcons op args)))))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.math.utexas.edu/pipermail/maxima/attachments/20070502/0594519e/attachment.htm
```