i ended up giving up on using maxima as a visualization/symbolic math computation library for lisp, it wasnt made with this usecase in mind i guess, and so its an utter nightmare to lisp with it
maxima is written in lisp and so it can be used as a modified/extended lisp runtime
how to
to start the lisp runtime use the command:
rmaxima -r 'to_lisp();'
can also run this in emacs' slime/sly with (sly "rmaxima -r to_lisp();")
alternatively, we can clone maxima's source code to the subdirectory local-projects
of the quicklisp installation directory (usually ~/quicklisp
unless you've modified it), follow the instructions at https://sourceforge.net/p/maxima/code/ci/master/tree/INSTALL.lisp#l66 to compile maxima and you'll be able to load it as a library:
(ql:quickload "maxima")
but then you gotta prefix functions with maxima::
, e.g.
(maxima::displa (maxima::$integrate #$2/(3*x^5)$ 'x))
2 x ---- 5 3 x
unless you enter the library itself then you dont need the prefix:
(in-package :maxima)
loading maxima as a library works most of the time, but it causes some problems, for example we cant use draw
when maxima is loaded as a library, thats why using rmaxima
is better
misc
to write an expression in infix syntax we do:
(print #$10/13$)
((MAXIMA::RAT MAXIMA::SIMP) 10 13)
to display math using ascii art we use the function displa
(short for display)
(maxima::displa #$10/13$)
10 -- 13
maxima expression to lisp expression
this function helps turn maxima expressions into their corresponding lisp expression
(defun lisp-form (macsyma-string) (maxima::macsyma-read-string (concatenate 'string macsyma-string "$")))
example:
(print (lisp-form "diff(sin(x),x)"))
((MAXIMA::$DIFF) ((MAXIMA::%SIN) MAXIMA::$X) MAXIMA::$X)
some expressions will return symbols instead of functions so they cannot be run directly, e.g.
(print (lisp-form "sin(10d0)"))
((MAXIMA::%SIN) 10.0)
so to evaluate these expressions we can use meval*
:
(print (lisp-form "sin(10d0)")) (print (maxima::meval* (lisp-form "sin(10d0)"))) (print (maxima::meval* '((maxima::%sin) 10.0)))
((MAXIMA::%SIN) 10.0) -0.5440211108893698 -0.5440211108893698
although functions like $sin
do exist and can be used instead of symbols like %sin
taken from https://github.com/livelisp/livewlisp/blob/main/maxima-tutorial.txt
integration
the main function for integration is $integrate
(or sinint
)
(maxima::displa (maxima::$integrate #$2/(3*x^2)$ 'x))
2 x ---- 2 3 x
(maxima::displa (maxima::$integrate '((maxima::%cos) x) 'x))
sin(x)
lists
i havent a better approach yet
(maxima::displa (maxima::meval* `((maxima::mlist) 2 3 5)))
[2, 3, 5]
lisp list to maxima list:
(defun list->mlist (list) (let ((mlist (maxima::meval* `((maxima::mlist))))) (loop for expr in (reverse list) do (setf mlist (maxima::meval* `((maxima::$append) ((maxima::mlist) ,expr) ,mlist)))) mlist))
example:
(print (list->mlist '(1 2 3))) (maxima::displa (list->mlist '(1 2 3)))
((MAXIMA::MLIST MAXIMA::SIMP) 1 2 3) [1, 2, 3]
plotting
we need to initialize some variables (like *maxima-tempdir*
) to be able to plot, this can be done automatically using initialize-runtime-globals
(maxima::initialize-runtime-globals)
to plot a set of points (discrete plot) using gnuplot
(maxima::$plot2d '((maxima::mlist) maxima::$discrete ((maxima::mlist) 1 2 3) ((maxima::mlist) 1 2 3)))
this can be (somewhat) simplified using our function list->mlist
(see above)
(maxima::$plot2d `((maxima::mlist) ((maxima::mlist) maxima::$discrete ,(list->mlist '(1 2 3)) ,(list->mlist '(1 2 3))) ((maxima::mlist) maxima::$discrete ,(list->mlist '(1 2 3)) ,(list->mlist '(1 5 3)))))
we can draw multiple plots (this only works when running lisp using maxima
, see the how to section):
(let ((scene1 '((MAXIMA::$GR2D) ((MAXIMA::MEQUAL) MAXIMA::$TITLE "first plot") ((MAXIMA::MEQUAL) MAXIMA::$NTICKS 300) ((MAXIMA::$PARAMETRIC) ((MAXIMA::MTIMES) 2 ((MAXIMA::%COS) MAXIMA::$T)) ((MAXIMA::MTIMES) 5 ((MAXIMA::%SIN) MAXIMA::$T)) MAXIMA::$T 0 ((MAXIMA::MTIMES) 2 MAXIMA::$%PI)))) (scene2 `((MAXIMA::$GR2D) ((MAXIMA::MEQUAL) MAXIMA::$TITLE "second plot") ((MAXIMA::MEQUAL) MAXIMA::$NTICKS 300) ((maxima::$points) ((mlist) 1 2 3) ((mlist) 1 2 3))))) (maxima::meval* `((MAXIMA::$DRAW) ,scene1 ,scene2 ((MAXIMA::MEQUAL) MAXIMA::$COLUMNS 2))))
we implement some abstraction over this to make it less explicit:
(defun plot-points (x-values y-values) (maxima::$plot2d `((maxima::mlist) maxima::$discrete ,(list->mlist x-values) ,(list->mlist y-values))))