maxima in lisp

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.netp/maxima/code/ci/master/tree/INSTALL.lispBROKEN 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))))