screwlisp proposes kittens

Common lisp computer algebra with maxima, finally.

Maxima (current, August 2025) has been used continuously by governments and industry since 1982, when it forked from Macsyma main line (1968 - 1999). In 1999 Maxima’s Bill Schelter obtained permission to GPL license Maxima. Maxima is loosely speaking the same thing as, and originally derived from Macsyma, so when Maxima went open in 1999 the last Macsyma users switched to it.

Like most big lisp programs, Maxima provides its own specialized repl. But if you want to use it as part of common lisp, my friend JMBR has the simple maxima-interface package I will use here.

I use Eduardo Ochs’ eev in emacs throughout, but you could copy or type the lines by hand if you are super good at typing and doing pointless extra work.

(integrate '(tan x) 'x 0 1)
'(1 2 3 4)
(coerce * '(SIMPLE-ARRAY (COMPLEX DOUBLE-FLOAT) (*)))
(maxima-fft:fft-r2-nn *)

🠊

MAXIMA-INTERFACE-USER> (integrate '(tan x) 'x 0 1)
(* -1 (LOG (COS 1)))
MAXIMA-INTERFACE-USER> '(1 2 3 4)
(1 2 3 4)
MAXIMA-INTERFACE-USER> (coerce * '(SIMPLE-ARRAY (COMPLEX DOUBLE-FLOAT) (*)))
#(#C(1.0 0.0) #C(2.0 0.0) #C(3.0 0.0) #C(4.0 0.0))
MAXIMA-INTERFACE-USER> (maxima-fft:fft-r2-nn *)
#(#C(2.5 0.0) #C(-0.5 -0.5) #C(-0.5 0.0) #C(-0.5 0.5))
MAXIMA-INTERFACE-USER> 

I nuked my computer recently and was just grabbing this again. Like other continuously active major public and private popular projects that are sixty years old, installation is necessarily easy but a bit historical, notably it does not look like pop tooling that is one fifth of its age. So let us join decades of computer heavyweight lisp wizards by setting this up.

Getting sources

Actual source: https://sourceforge.net/projects/maxima/

• (eepitch-shell)
mkdir -p ~/common-lisp
cd ~/common-lisp
git clone https://github.com/calyau/maxima
git clone https://git.sr.ht/~jmbr/maxima-interface

The build-maxima-for-use-from-common-lisp procedure

basically maxima/INSTALL.lisp. I mostly use ecl as my lisp compiler. Lisp has lots of powerful compilers, your choice will be supported too. gcl (a great compiler) has several extra steps you can see in INSTALL.lisp.

cd ~/common-lisp/maxima/
ecl <<"EOG"
(load "configure.lisp")
(configure :interactive nil)
EOG
cd src
ecl <<"EOG"
(load "maxima-build.lisp")
(maxima-compile)
EOG

Loading maxima and JMBR’s maxima-interface

Inside ecl for me, or your lisp (that you built maxima with)

(require "asdf")
(uiop:with-current-directory ("~/common-lisp/maxima/src/")
  (load "maxima-build.lisp"))
(asdf:load-system :maxima-interface)

which I have in my ~/.eclrc.

Basic useage

The lisp convention is for packages to offer a -USER package to absorb the user’s code (if you did not write the package yourself, you are the user in this case). So in my ecl again after the setup,

• (setq inferior-lisp-program "ecl")
• (slime)
• (setq eepitch-buffer-name "*slime-repl ECL*")
(in-package :maxima-interface-user)
(diff '(log (1+ x)) 'x)
(integrate * 'x)

🠊

MAXIMA-INTERFACE-USER> (diff '(log (1+ x)) 'x)
(EXPT (+ 1 X) -1)
MAXIMA-INTERFACE-USER> (integrate * 'x)
(LOG (+ 1 X))
MAXIMA-INTERFACE-USER> 

note that (expt foo bar) in lisp is foo-to-the-power-of-bar, the special one with e being (exp foo) for e to the foo.

Loading Maxima’s fourier transform module

maxima-interface provides a simple interface, but lets us do what we want via maxima-run. I wanted to load maxima’s fft module.

(maxima-run "load(fft);" :display2d nil :return-expression t)

and hence

'(1 1)
(coerce * '(SIMPLE-ARRAY (COMPLEX DOUBLE-FLOAT) (*)))
(maxima-fft:fft-r2-nn *)
(coerce * 'list)
(mapcar 'realpart *)

🠊

MAXIMA-INTERFACE-USER> '(1 1)
(1 1)
MAXIMA-INTERFACE-USER> (coerce * '(SIMPLE-ARRAY (COMPLEX DOUBLE-FLOAT) (*)))
#(#C(1.0 0.0) #C(1.0 0.0))
MAXIMA-INTERFACE-USER> (maxima-fft:fft-r2-nn *)
#(#C(1.0 0.0) #C(0.0 0.0))
MAXIMA-INTERFACE-USER> (coerce * 'list)
(#C(1.0 0.0) #C(0.0 0.0))
MAXIMA-INTERFACE-USER> (mapcar 'realpart *)
(1.0 0.0)

Noting that maxima-fft:fft-r2-nn is a radix-2 fft. We can see it divides by the is-a-power-of-2 length of the array with the fft. So the discrete fourier transform of (1 1) is (#.(/ (+ 1 1) 2) 0) as we expect.

Fin.

By the way, “tomorrow”, Kent Pitman (Kent Pitman) is going to be video streaming his lisp-error-handling-for-python on the Sunday-morning-in-Europe peertube lives which happen at 8am Zulu every week. I think this is very apropos the Swanky Python emacsconf demo https://codeberg.org/sczi/swanky-python/ .

Hopefully I got all my setup code right for maxima here. I wrote this immediately afterwards and can imagine having tricked myself.

Please let me know / your comment otherwise on the mastodon. I’m unnuking myself slowly and may be slow to respond. Any Maxima people want to come on the show either Sunday morning in Europe or Tuesday evening in the Americas please let me know. (Other than / as well as Kent I guess).

screwlisp proposes kittens