Emacs philosophy usually leads to implement core functions not related to text processing in specialized external programs, and then use Elisp to provide an Emacs interface to those programs. As an advantage, the core software is not specific to Emacs (that is the case for Elisp programs) and can be used in other contexts.
However, it may be convenient to implement a few simple numerical computations directly in Elisp, rather than always interfacing it with external programs. Being Elisp mathematical library rather limited, a more comprehensive numerical library is welcome. Fortunately, Emacs incorporates Calc, an algebra system written in Elisp. Calc is a program mainly meant to be called from Emacs, rather than a proper numerical library. However, a simple interface to Calc from other Elisp code is provided by the nice function calc-eval. Here we show its usage through a couple of examples.
For a complete guide to Calc see the official documentation (the source code is under-documented, don't waste time on that and refer to the manual instead), in particular the section about how to call Calc from other Lisp programs. The following code has been tested with GNU Emacs 24.5.1 and calc-eval is already on the list of recommended autoload functions, so no special preparations are needed to load Calc before calling calc-eval.
On this page: |
If you are reading this, chances are that you are already familiar with Elisp. If not, let's recall that Elisp (Emacs Lisp) is a dialect of the Lisp language family. It was designed as the extension language for the Emacs text editor. As such, its main focus is string manipulation and representation on screen. Despite more than 30 years of development, there seems to be no library dedicated to numerical computing in Elisp. Indeed, this is not why Elisp was created. Heavy computations may stuck Emacs, hence the full development environment at once.
Elisp code is composed by symbolic expressions enclosed by parenthesis and can be evaluated as follows. Copy the text on this page and paste it into Emacs. Place the cursor at the end of code parenthesis and press the key strokes `C-x C-e' (where C is the Ctrl key). For example:
(+ 1 1) <= cursor here, press C-x C-e
The result of the evaluation will be displayed in the Emacs mini-buffer. If anything goes wrong and you are stuck in some buffer without knowing what to do, press the key combination `C-g'.
See also An Introduction to Programming in Emacs Lisp for a gentle initiation to Elisp (and to programming in general).
First, to avoid bugs it is useful to point out that Calc gives to division a lower precedence than multiplication. Hence the algebraic expression 1 / 2 * 3 is interpreted as 1 / (2 * 3), not as (1 / 2) * 3 as it may be commonly expected. Apart from this convention, Calc algebraic expressions are fairly intuitive. The simplest usage of calc-eval is illustrated by the following call.
(calc-eval "1+1")
;; => "2"
Of course, the example above has no practical advantage over evaluating directly (+ 1 1). The advantage of calc-eval is that it allows to access several functions defined in Calc and not in the Elisp mathematical library. For instance, this is the case for hyperbolic functions. They can computed from scratch given the exponential function of Elisp mathematical library (or possibly using approximations or asymptotic forms depending on the value of the argument). Alternatively, they can easily be accessed from the Calc interface. Let's consider the hyperbolic sine sinh(x).
(calc-eval "sinh(0.5)")
;; => "0.521095305494"
Arguments can be passed separately using the placeholders $, $$, ... for the first, second and so on argument. In the following nil is a separator and in general it can be replaced by a symbol triggering some option (see later).
(calc-eval "sinh($)" nil "0.5")
;; => "0.521095305494"
The previous calls are easy to use but inefficient, because they needs to convert strings to numbers. Passing a Calc data structure is more efficient, but cumbersome. For instance, the float `0.5' must be passed in a list separating mantissa and exponent `(float 5 -1)', see the section about data type formats of Calc manual. That's often too ugly, see the example below to compute once again sinh(0.5), but passing a raw Calc data structure with the raw symbol.
(calc-eval "sinh($)" 'raw '(float 5 -1))
;; => (float 521095305494 -12)
In this case it is more convenient to call directly Calc internal functions. Calc manual states that those functions starting by calcFunc- are also meant to be called from other programs.
(calcFunc-sinh '(float 5 -1))
;; => (float 521095305494 -12)
However, internal functions are not always intuitive to use (see the next example) and the source code lacks of documentation.
The list of Calc internal functions mentions ninteg for numerical integration. The respective internal function callable from external Elisp program is then calcFunc-ninteg. However, its use is not documented. Inspection of source code suggests that an expression formatted according to Calc raw format should be given as an argument, which is not trivial. Here the function calc-eval is then especially useful, since it allows the use of ninteg through more intuitive expressions. The use of ninteg is documented in a solution of the a Calc programming tutorial exercise. Let's integrate sin(x)/x from 0 to 1.
(calc-eval "ninteg(sin(x)/x, x, 0, 1)")
;; => "0.0174529971573"
Now let's look at further options associated to calc-eval. As already seen, an argument list can be passed separately using the placeholders $, $$, ... .
(calc-eval "ninteg($, $$, $$$, $$$$)" nil "sin(x)/x" "x" "0" "1")
;; => "0.0174529971573"
Numerical integration is of course faster if the default precision (12 digits) is reduced to, say, 6 digits.
(calc-eval '("ninteg(sin(x)/x, x, 0, 1)" calc-internal-prec 6))
;; => "0.017453"
If the integral cannot be evaluated a symbolic expression is returned. For instance, if a large upper integration limit is required, e.g., ninteg(sin(x)/x, x, 0, 10000), the integral may fail to converge (this is the case with default 12 digits precision). Let's look also at another straightforward example independent from the precision. The manual states that quadrature is performed with the open Romberg method, so that it is safe to integrate sin(x)/x from zero, as shown above. However, integration across singularities is not supported. For instance, the integral of 1/sqrt(x) from 0 to 1 should give 2.
(calc-eval "ninteg(1/sqrt(x), x, 0, 1)")
;; => "ninteg(1 / sqrt(x), x, 0, 1)"
The numerical integral could not be evaluated, and the formal integral is returned back. When performing numerical integration in Elisp, however, it is often better to rise an error if an integral fails to converge instead of returning a formal expression. The option num specifies that we expect to operate with numbers, and rises an error if this is not the case.
(calc-eval "ninteg(1/sqrt(x), x, 0, 1)" 'num)
;; => (0 "Integral failed to converge")
Depending on the application it may be better to rise a standard Elisp error instead. This can also be done. Setting the variable calc-eval-error to t, Calc calls the standard Elisp error function. To avoid conflicts with other programs, it is good practice to set the variable in a local scope through a let statement, and not globally through setq.
(let ((calc-eval-error t))
(calc-eval "ninteg(1/sqrt(x), x, 0, 1)" 'num))
;; => Debugger entered--Lisp error: (error "Integral failed to converge")
;; ...
Finally, let's combine some of the options illustrated above to integrate sin(x)/x from 0 to 1.
(let ((calc-eval-error t))
(calc-eval '("ninteg(sin(x)/x, x, $, $$)" calc-internal-prec 6)
'num "0" "1"))
;; => "0.017453"
Reading the previous expression from the inner parenthesis to the outer one, we first set 6 digit precision and define the algebraic expression for the integral. The list is passed to calc-eval. The integration boundaries are set separately (corresponding to the place holders $ and $$), after the separator num that tells Calc to return an error in case non-numerical expressions are encountered. Finally, thanks to the outer let statement, if an error is found the standard Elisp error function is called, instead of the one from Calc.
The function calc-eval provides a fairly intuitive interface to Calc functions library and it can be easily integrated in Elisp programs. Since it requires strings conversion to numbers, the solution is not optimal in terms of performance and it should only be used for simple computations. Calling directly Calc internal functions is more efficient but too cumbersome. Implementing algorithms from scratch may be a better option.
As a benchmark, we measured (on an Intel Core i7-2620M CPU @ 2.70GHz) the time necessary to evaluate sinh(0.5):
Hence, implementing the function from scratch provides faster results by a factor 10 compared to Calc internal function, and by a factor 104 compared to the calc-eval interface. Furthermore, while the implementation from scratch requires about the same time for much larger or smaller arguments, Calc functions result in significantly slower results. Clearly, the Calc interface should be used only if efficiency is not an issue.
On the other hand, Elisp itself is not the optimal language for non-trivial numerical computations. Other programs with optimized numerical libraries may also provide Elisp interfaces (it may be worth to check if this is the case for Octave or Maxima), but it requires the installation of further components (instead, Calc is already included in recent enough Emacs versions). If time-consuming algorithms are required it is advisable to implement them with external programs. Elisp can then be used, for instance, to read a result printed on stdout and format it in a Emacs buffer. Several tools are provided in Elisp standard library for that. For instance, the following line displays in Emacs mini-buffer the output of the shell command echo "4+10" | bc (that calculates the sum with the bc program). Any program callable from command line can be used in a similar way.
(shell-command-to-string "echo \"4+10\" | bc")
;; => "14"
Happy hacking!