A Clojure(script) implementation of the Scmutils system for math and physics investigations in the Clojure language.
Scmutils is extensively used in the textbooks The Structure and Interpretation of Classical Mechanics and Functional Differential Geometry by G.J. Sussman and J. Wisdom.
Need help getting started? Say hi on Twitter or Clojurians Slack in #sicmutils. |
Install SICMUtils into your Clojure(script) project using the instructions at its Clojars page:
Initialize a REPL and simplify a trigonometric identity:
user=> (require '[sicmutils.env :as env])
user=> (env/bootstrap-repl!)
user=> (def render (comp ->infix simplify))
user=> (render
(+ (square (sin 'x))
(square (cos 'x))))
1
Define a Lagrangian and generate the Lagrange equations of motion for the physical system it describes, rendered in polar coordinates:
(defn L-central-polar [m U]
(fn [[_ [r] [rdot φdot]]]
(- (* 1/2 m
(+ (square rdot)
(square (* r φdot))))
(U r))))
#'user/L-central-polar
user=>
(let [potential-fn (literal-function 'U)
L (L-central-polar 'm potential-fn)
state (up (literal-function 'r)
(literal-function 'φ))]
(render
(((Lagrange-equations L) state) 't)))
"down(- m (Dφ(t))² r(t) + m D²r(t) + DU(r(t)), 2 m Dφ(t) r(t) Dr(t) + m (r(t))² D²φ(t))"
Confused? You’re not alone! This is a very dense library, and not well documented (yet). Some suggested next steps, for now:
We’ve ported the original SCMUtils Reference Manual over to SICMUtils. Visit the SICMUtils Reference Manual for a comprehensive overview of SICMUtils.
Clone this repository and run lein repl
for a batteries included REPL
environment. All of the examples in the SICMUtils refman
will work.
Browse the table of contents on the left hand side. Many of the pages are populated with good information, and we’re working hard to flesh this out further.
For a deep dive into classical mechanics using this library, visit the HTML version of Structure and Interpretation of Classical Mechanics. Follow along with the solutions at @sritchie’s SICM solutions repository.
SICM and FDG can be thought of as spiritual successors to The Structure and Interpretation of Computer Programs, a very influential text—as I can attest, since carefully reading this book in my 30s changed my life as a programmer. To see the same techniques applied to differential geometry and physics is an irresistible lure.
Scmutils is an excellent system, but it is written in an older variant of LISP (Scheme) and is tied to a particular implementation of Scheme—MIT/GNU Scheme. (There is a port to Guile, but due to the fact that Guile does not support MIT Scheme’s apply hooks some glue code is required to run examples from the book in that environment.)
Having the system in Clojure offers a number of advantages. It is not necessary to obtain or prepare a MIT/GNU Scheme executable to execute: only a Java runtime is required. It does not require the X Window System for graphics, as MIT Scheme does. All of the standard tooling for Java and Clojure become available, and this is a lot compared to what we get with MIT/GNU scheme. Clojure support is now extensive in any number of editors and IDEs. Even better, you can interact with the system in the context of a Jupyter notebook.
You can invoke the system from within Java code or use any Java packages you like together with the mathematics system. It’s my hope that continuing this project will extend the reach of SICM and FDG by allowing experimentation and collaboration with them in modern environments.
Rather than just quasi-mechanically translate the Scheme to Clojure, We’ve studied the implementation of the system before bringing it to Clojure, and have used TDD throughout the project (which turned out to be absolutely essential as I considered various approaches to problems posed by the Scheme code base). At this writing there are over 4200 unit tests.
Here’s a side-by-side example of scmutils
Scheme code and SICMUtils
Clojure
code. First, the Scheme:
;; Scheme
(define ((L-central-polar m U) local)
(let ((q (coordinate local))
(qdot (velocity local)))
(let ((r (ref q 0)) (phi (ref q 1))
(rdot (ref qdot 0)) (phidot (ref qdot 1)))
(- (* 1/2 m
(+ (square rdot)
(square (* r phidot))) )
(U r)))))
Then the same function in idiomatic Clojure:
;; Clojure
(defn L-central-polar [m U]
(fn [[_ [r] [rdot φdot]]]
(- (* 1/2 m
(+ (square rdot)
(square (* r φdot))))
(U r))))
We can see a few things from this example. L-central-polar
wants to compute a
Lagrangian for a point mass m
in a potential field U
. In Scheme, it’s
possible to specify currying at the site of a function’s definition:
(L-central-polar m U)
returns a function of the local
tuple (a sequence of
time, generalized coordinates, and generalized velocities). We don’t have that
syntax in Clojure, but instead have something even more useful: argument
destructuring. We can pick out exactly the coordinates we want out of the local
tuple components directly.
While function definitions cannot be typed directly from the book, function applications in Clojure and Scheme are the same. The following works in both systems:
(((Lagrange-equations (L-central-polar 'm (literal-function 'U)))
(up (literal-function 'r)
(literal-function 'φ)))
't)
yielding:
(down
(+ (* -1N m (expt ((D φ) t) 2) (r t)) (* m (((expt D 2) r) t)) ((D U) (r t)))
(+ (* 2N m ((D φ) t) (r t) ((D r) t)) (* m (expt (r t) 2) (((expt D 2) φ) t))))
Which, modulo a few things, is what Scmutils would give. From later in SICM (pp. 81-2) we have, in Scheme:
(define ((T3-spherical m) state)
(let ((t (time state))
(q (coordinate state))
(qdot (velocity state)))
(let ((r (ref q 0))
(theta (ref q 1))
(phi (ref q 2))
(rdot (ref qdot 0))
(thetadot (ref qdot 1))
(phidot (ref qdot 2)))
(* 1/2 m
(+ (square rdot)
(square (* r thetadot))
(square (* r (sin theta) phidot)))))))
(define (L3-central m Vr)
(define (Vs state)
(let ((r (ref (coordinate state) 0)))
(Vr r)))
(- (T3-spherical m) Vs))
(((partial 1) (L3-central ’m (literal-function ’V)))
(up ’t
(up ’r ’theta ’phi)
(up ’rdot ’thetadot ’phidot)))
And in Clojure, using a couple of simplifying definitions:
(def V (literal-function 'V))
(def spherical-state (up 't
(up 'r 'θ 'φ)
(up 'rdot 'θdot 'φdot)))
(defn T3-spherical [m]
(fn [[t [r θ φ] [rdot θdot φdot]]]
(* 1/2 m (+ (square rdot)
(square (* r θdot))
(square (* r (sin θ) φdot))))))
(defn L3-central [m Vr]
(let [Vs (fn [[_ [r]]] (Vr r))]
(- (T3-spherical m) Vs)))
(((partial 1) (L3-central 'm V)) spherical-state)
yielding
(down
(+ (* m r (expt φdot 2) (expt (sin θ) 2)) (* m r (expt θdot 2)) (* -1 ((D V) r)))
(* m (expt r 2) (expt φdot 2) (sin θ) (cos θ))
0)
Which again agrees with Scmutils modulo notation. (These results are examples of "down tuples", or covariant vectors, since they represent derivatives of objects in primal space.)
The partial derivative operation is called partial
in Scmutils, but Clojure
defines partial
to mean partial function application. In this system, we take
a page from JavaScript and replace partial with a shim which will compute
partial derivatives when all the arguments are integers and fall back to
Clojure’s definition of partial otherwise. Since it doesn’t make sense to
partially apply an integer, partial
should just do the right thing.
You could render that result in TeX:
using the →TeX
function. You can also use →infix
to obtain:
down(m r φdot² sin²(θ) + m r θdot² - DV(r), m r² φdot² sin(θ) cos(θ), 0)
or even →JavaScript
to get:
function(D, V, m, r, θ, θdot, φdot) {
var _0001 = Math.sin(θ);
var _0002 = Math.pow(φdot, 2);
return [m * r * _0002 * Math.pow(_0001, 2) + m * r * Math.pow(θdot, 2) - (D(V)(r)), m * Math.pow(r, 2) * _0002 * _0001 * Math.cos(θ), 0];
}
(For rendering into code, a simple common-subexpression extraction algorithm is used.)
Please explore the documentation, and send us (@sritchie
, @colins
) in
particular a note in the
#sicmutils channel on
Clojurians slack if you’d like to talk or have any questions at all!
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close