Guile-GSL is a C/Scheme library extension for Guile, the GNU's Ubiquitous Intelligent Language for Extensions. It implements a binding to the GNU Scientific Library (GSL). The purpose is to implement an environment similar to GNU Octave, but using Scheme as a programming language.
Guile-GSL is a part of GEE, a collection of extensions for Guile.
This is a tutorial on the usage of Guile-GSL, not on how to program with Scheme. To understand what's going on here you have to know the basics of Scheme: how to define variables, how to define procedures, how to invoke procedures, what is a cons.
Many people can learn the Scheme basics in a few days; the Internet is full of introduction papers on the subject. The first chapters of the SICP are perfect:
Structure and Interpretation of Computer Programs (sicp) Harold Abelson and Gerald Sussman Copyright (C) 1996 Massachussets Institute of Technology
this book is available on the Internet, there is also a GNU Texinfo version.
On this wiki are available installation instructions GEE-Guile-GSL-install.
To load the module:
(define-module (this-script) #:use-module (oop goops) #:use-module (gee math oop) #:use-module (gee math gsl) #:duplicates merge-generics) ;; your code goes here
When Guile-GSL is installed a couple of scripts are placed on the system to use the modules from the Guile REPL (read, eval, print loop):
a Bourne shell interface scripts that sets up the environment and then runs guile with gsh.scm as script; installed under a path like /usr/local/bin/gsh;
a Guile script that loads Guile-GSL and other modules then invokes the Guile REPL; it is installed under a path like /usr/local/libexec/guile-gsl/0.1.0/gsh.scm.
If the installation was successful at the shell prompt we can do:
$ gsh Guile-GSL version 0.1.0 Written by Marco Maggi. Copyright (C) 2006, 2007 by Marco Maggi. This is free software; see the source or use the '--license' option for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. gsh>
gsh> is the default Guile-GSL shell prompt.
Both the scripts are very simple and you are encouraged to inspect and modify them to suit your needs. The gsh shell script is there exactly to be customised with site-specific environment variables.
Vectors and matrices are the basic objects of math computations with Guile-GSL. The easiest interface is the one that uses the GOOPS extension of Guile; there is only one class that we have to be aware of: <gsl>, and we rarely need to use it explicitly.
Quick code to overview:
(define a #,(gvr 3 1 2 3)) (define b #,(gvr 3 4 5 6)) (display (+ a b)) ;; -> [5 7 9] (write (+ a b)) ;; -> #,(gvr 3 5 7 9) (display (dot a b)) ;; -> 32.0 (display (cross a b)) ;; -> [-3.0 6.0 -3.0]
We may want to define vectors and matrices in one of three ways: just define an "empty" object, define an object with known constant elements, define an object with elements that we have to compute using math functions.
We use the Scheme "read syntax" with the SRFI-10 features included in Guile. There are 4 symbols:
to declare vectors of real numbers (gvr = GSL vector real);
to declare vectors of complex numbers (gvc = GSL vector complex);
to declare matrices of real numbers (gmr = GSL matrix real);
to declare matrices of complex numbers (gmc = GSL matrix complex).
the definitions look like the following:
#,(gvr 3 1 2 3) #,(gvc 3 1+2i 2-3i 3+4i) #,(gmr (2 . 3) 1 2 3 4 5 6) #,(gmc (2 . 3) 1+2i 2-3i 3+4i 4-5i 6+7i 8-9i)
The first element after the tag must be the dimension: for vectors the number of elements; for matrices a cons holding the number of rows in the car and the number of columns in the cdr. Notice that when using the read syntax the cons does not need to be quoted.
After the tag and the dimension we simply write the elements separated by blanks (spaces, tabs, newlines). The elements must be numbers.
Sometimes we need to define an object with known dimension that will be filled with values later. We do it like this:
(define a (make-gsl-vector-real 3)) (define b (make-gsl-vector-complex 3)) (define c (make-gsl-matrix-real '(3 . 5))) (define d (make-gsl-matrix-complex '(19 . 2)))
as Scheme dictates: outside the read syntax, the conses must be quoted. All the elements are set to 0.0; optionally we can give an additional parameter representing the default:
(define a (make-gsl-vector-real 3 4.5)) ;; -> [4.5 4.5 4.5]
A special maker exists for identity matrices:
(eye 3) ;; -> [[1 0 0] ;; [0 1 0] ;; [0 0 1]]
Defining objects with computed values is similar to defining empty objects in that we invoke functions for it:
(define a (read-gsl-vector-real 3 (sin 0.2) (cos 0.5) (tan 0.6))) (define b (read-gsl-vector-complex 3 (sin 0.2+9i) (cos 0.5) (tan -0.6i))) (define c (read-gsl-matrix-real '(2 . 3) (sin 0.2) (cos 0.5) (tan 0.6) (sin 0.3) (cos 0.9) (tan 0.2))) (define d (read-gsl-matrix-complex '(2 . 3) (sin 0.2) 0.5 (tan 0.6) (sin 0.2+9i) (cos 0.5) (tan -0.6i)))
Once we have defined vectors and matrices we just apply functions to them. The four arithmetic functions +, -, * and / do what we expect element by element; there is also the unary - that negates all the elements:
(- #,(gvr 3 1 2 3)) ;; -> [-1 -2 -3]
Other functions, like hyperbolic ones, are applied to all the elements, the following form:
(sinh #,(gvr 3 1 2 3))
is equivalent to:
(read-gsl-vector-real 3 (sinh 1) (sinh 2) (sinh 3))
DOT does the row by column product; if the arguments are both vectors: the operation is the scalar product; if the arguments are a matrix and a vector:
(dot #,(gmc (2 . 3) 1+2i 2-3i 3+4i 4-5i 6+7i 8-9i) #,(gvr 3 1 2 3)) ;; -> #,(gvc 2 14.0+8.0i 40.0-18.0i)
and if they are two matrices:
(dot #,(gmc (2 . 3) 1+2i 2-3i 3+4i 4-5i 6+7i 8-9i) #,(gmc (3 . 2) 1+2i 2-3i 3+4i 4-5i 6+7i 8-9i)) ;; -> #,(gmc (2 . 2) 5.0+48.0i 61.0-16.0i ;; 115.0+50.0i 35.0-168.0i)
There is no such concept as row vector or column vector: the vectors are interpreted as rows or columns as need be.
We can transpose matrices like this:
and do the conjugate:
The setter/getter synopsis is ugly, but that's the way it is. The synopsis of all the getters is:
(type object key)
so to get elements from a vector:
(define a #,(gvr 3 1 2 3)) (elm a 0) ;; -> 1 (elm a 1) ;; -> 2 (elm a 2) ;; -> 3
indexes are zero based. To get elements from a matrix we have to select both the row and the column:
(define a #,(gmr (2 . 3) 1 2 3 4 5 6)) (elm a '(0 . 0)) ;; -> 1 (elm a '(0 . 1)) ;; -> 2 (elm a '(1 . 2)) ;; -> 6
to get a whole row:
(row a 0) ;; -> [1 2 3] (row a 1) ;; -> [4 5 6]
and to get a whole column:
(column a 0) ;; -> [1 4] (column a 1) ;; -> [2 5]
The synopsis of all the setters is:
(set! (type object key) value)
to set elements:
(define a #,(gvr 3 1 2 3)) (set! (elm a 0) -1) ;; -> [-1 2 3] (define b #,(gmr (2 . 3) 1 2 3 4 5 6)) (set! (elm b '(1 . 2)) -1) ;; -> #,(gmr (2 . 3) 1 2 3 ;; 4 5 -1)
to set a whole row or column:
(define b #,(gmr (2 . 3) 1 2 3 4 5 6)) (set! (row b 0) #,(gvr 3 -1 -2 -3)) ;; -> #,(gmr (2 . 3) -1 -2 -3 ;; 4 5 6) (set! (column b 0) #,(gvr 3 -8 -9)) ;; -> #,(gmr (2 . 3) -8 -2 -3 ;; -9 5 6)
to set a diagonal:
(define b #,(gmr (2 . 3) 1 2 3 4 5 6)) (set! (diagonal b 1) #,(gvr 2 -8 -9)) ;; -> #,(gmr (2 . 3) 1 -8 3 ;; 4 5 -9)