This library re-implements the standard Common Lisp numeric functions as CLOS generic functions. This allows developers to easily extend the numeric tower with custom number types, making it possible to perform arithmetic operations on them in a natural and idiomatic way.
In standard Common Lisp, arithmetic functions like +, -, *, and / are not generic. This means they can only operate on the built-in number types (integer, ratio, float, complex). If you define a new number-like type, such as quaternions, dual numbers for automatic differentiation, or interval arithmetic, you cannot use the standard arithmetic operators on them directly. You would have to define new functions (quaternion-+, dual-*, etc.) which is cumbersome and un-lispy.
This library solves this problem by providing a set of generic functions that shadow the standard cl names. By implementing methods for these generic functions for your custom number types, you can seamlessly integrate them into the Lisp arithmetic system.
This library is not yet in Quicklisp. To use it, clone this repository into your ~/quicklisp/local-projects/ directory:
git clone https://github.com/jrm-code-project/generic-arithmetic.git ~/quicklisp/local-projects/generic-arithmeticThen, you can load it in your Lisp image:
(ql:quickload :generic-arithmetic)To use the generic functions, you need to use the generic-arithmetic package. It's recommended to shadow the cl symbols.
(defpackage #:my-new-numerics
(:use #:cl #:generic-arithmetic))Here is an example of how to define a new number type, dual, for forward-mode automatic differentiation. A dual number has two parts: a real part u and a dual part u'. The dual part represents the derivative.
The arithmetic rules for dual numbers are:
(u + u'ε) + (v + v'ε) = (u + v) + (u' + v')ε(u + u'ε) * (v + v'ε) = (uv) + (uv' + u'v)ε
Here is how you can implement this with generic-arithmetic:
(in-package #:my-new-numerics)
(defstruct (dual (:constructor dual (realpart deriv)))
(realpart 0.0 :type double-float)
(deriv 0.0 :type double-float))
(defmethod print-object ((d dual) stream)
(format stream "#<~S u=~A, u'=~A>"
(type-of d)
(dual-realpart d)
(dual-deriv d)))
;; Addition
(defmethod generic-arithmetic:add2 ((left dual) (right dual))
(dual (+ (dual-realpart left) (dual-realpart right))
(+ (dual-deriv left) (dual-deriv right))))
(defmethod generic-arithmetic:add2 ((left dual) (right number))
(add2 left (dual right 0.0d0)))
(defmethod generic-arithmetic:add2 ((left number) (right dual))
(add2 (dual left 0.0d0) right))
;; Multiplication
(defmethod generic-arithmetic:multiply2 ((left dual) (right dual))
(dual (* (dual-realpart left) (dual-realpart right))
(+ (* (dual-realpart left) (dual-deriv right))
(* (dual-deriv left) (dual-realpart right)))))
(defmethod generic-arithmetic:multiply2 ((left dual) (right number))
(multiply2 left (dual right 0.0d0)))
(defmethod generic-arithmetic:multiply2 ((left number) (right dual))
(multiply2 (dual left 0.0d0) right))
;; Now you can use the standard operators
(let ((x (dual 5.0d0 1.0d0))) ; x = 5, dx/dx = 1
(print (+ x 2)) ; => #<DUAL u=7.0, u'=1.0>
(print (* x 3)) ; => #<DUAL u=15.0, u'=3.0>
(print (* x x))) ; => #<DUAL u=25.0, u'=10.0> (derivative of x^2 is 2x)This library provides generic versions of most of the standard Common Lisp numeric functions.
+, -, *, /, 1+, 1-
add2, subtract2, multiply2, divide2, =2, max2, min2
<, <=, =, >, >=, /=
max, min
abs, negate, reciprocal, square, cube, sqrt, exp, log, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, signum, zerop
complex, realpart, imagpart, conjugate, numerator, denominator, rational, rationalize
float, decode-float, integer-decode-float, scale-float, float-digits, float-precision, float-radix, float-sign
ceiling, floor, round, truncate, fceiling, ffloor, fround, ftruncate
cis, phase, mod, rem, random
- fold: For folding over lists of numbers in n-ary operations.
This project is licensed under the MIT License. See the LICENSE file for details.