root/trunk/data/data.lisp

Revision 40, 15.1 kB (checked in by lhealy, 8 months ago)

When a matrix is a literal intialization, expand macro sensibly.
Example/test added.

  • Property svn:keywords set to Id
Line 
1;; Using GSL bulk data (vectors, matrices, etc.) storage.
2;; Liam Healy, Sun Mar 26 2006 - 16:32
3;; Time-stamp: <2008-03-18 21:19:05EDT data.lisp>
4;; $Id$
5
6(in-package :gsl)
7
8;;; To do:
9;;; - create a CL object of class gsl-data with the right GSL
10;;; pointer just from a raw CL object
11;;; - recreate GSL object should be possible to recreate the C object
12;;; (need this?)
13;;; - master list of objects, for manual memory management?
14
15;;;;****************************************************************************
16;;;; Class gsl-data and generic functions
17;;;;****************************************************************************
18
19(defclass gsl-data ()
20  ((pointer :initarg :pointer :accessor pointer
21            :documentation "A C pointer to the GSL representation of the data.")
22   (storage-size :initarg :storage-size :reader storage-size)
23   (data :accessor data-cache
24         :documentation "The Lisp object corresponding to the GSL data.")
25   (cl-invalid
26    :initform t :accessor cl-invalid
27    :documentation
28    "An indication of whether the Lisp object (slot 'data) agrees with the
29     GSL C data.  If NIL, they agree.  If T, they disagree in an unspecified
30     way.  If a list of index sets, those indices disagree and the remainder
31     are correct."))
32  (:documentation
33   "A superclass for all GSL data storage structures, such as vector, matrix,
34   etc."))
35
36(defmethod print-object ((object gsl-data) stream)
37  (print-data-object object *print-array* stream))
38
39(defparameter *print-data-contents* t)
40
41(defun print-data-object (object contents stream)
42  "Print the data object to the stream, possibly showing contents."
43  (print-unreadable-object (object stream :type t :identity t)
44    (when (and contents *print-data-contents*)
45      (princ (data object) stream))))
46
47(defgeneric gsl-array (object)
48  (:documentation "A pointer to the GSL array with the data contents."))
49
50(defun dim0 (object)
51  "The first dimension of the object."
52  (first (storage-size object)))
53
54(defun dim1 (object)
55  "The second dimension of the object."
56  (second (storage-size object)))
57
58(defgeneric cl-elt-type (object)
59  (:documentation "The CL type of an element."))
60
61;;; Accessing elements
62(export 'maref)
63(defgeneric maref (object &rest indices)
64  (:documentation "An element of the data."))
65
66(defgeneric (setf maref) (value object &rest indices)
67  (:method :after (value (object gsl-data) &rest indices)
68    (push indices (cl-invalid object)))
69  (:documentation "Set an element of the data."))
70
71(export '(write-binary read-binary write-formatted read-formatted))
72(defgeneric write-binary (object stream)
73  (:documentation "Write the binary GSL data."))
74
75(defgeneric read-binary (object stream)
76  (:documentation "Read the binary GSL data."))
77
78(defgeneric write-formatted (object stream format)
79  (:documentation "Write the formatted GSL data."))
80
81(defgeneric read-formatted (object stream format)
82  (:documentation "Read the formatted GSL data."))
83
84;;;;****************************************************************************
85;;;; Macro defdata to define class etc.
86;;;;****************************************************************************
87
88(defun assign-pointer (object pointer)
89  "Check that a GSL data pointer is not null, then assign it to the object."
90  (check-null-pointer
91   pointer
92   :ENOMEM
93   (format nil "for ~a."
94           (with-output-to-string (stream)
95             (print-data-object object nil stream))))
96  (setf (pointer object) pointer))
97
98;;; (args (loop for i below dimensions collect (intern (format nil "I~d" i))))
99;;; (mapcar (lambda (v) `(,v size))                         args)
100
101(defmacro data-go (type matrixp)
102  "Define the letm function for data types."
103  (if matrixp                           ; Matrix (two indices)
104      `(defgo ,type (size-or-initial &optional size-or-zero zero)
105        (cond ((and (numberp size-or-initial) (numberp size-or-zero))
106               ;; Array dimensions are given as literal numbers for matrix
107               (list
108                `(make-data ',',type ,zero ,size-or-initial ,size-or-zero)
109                'free))
110              ((typep size-or-initial 'array)
111               (let ((argsymb (gensym "ARG")))
112                 (list
113                  `(apply #'make-data
114                    ',',type ,size-or-zero (array-dimensions ,argsymb))
115                  'free
116                  (lambda (symb)
117                    `(setf (data ,symb) ,argsymb))
118                  (lambda () (list argsymb size-or-initial)))))
119              (t
120               ;; Determine at runtime whether first arg is initial or dimension
121               (let ((argsymb (gensym "ARG")))
122                 (list
123                  `(apply #'make-data ',',type nil
124                    (if (and (numberp ,argsymb) (numberp ,size-or-zero))
125                        (list ,argsymb ,size-or-zero)
126                        (array-dimensions ,argsymb)))
127                  'free
128                  (lambda (symb)
129                    `(unless (and (numberp ,argsymb) (numberp ,size-or-zero))
130                      (setf (data ,symb) ,argsymb)))
131                  (lambda () (list argsymb size-or-initial)))))))
132      ;; Vector or other one-index object
133      `(defgo ,type (size-or-initial &optional zero)
134        (typecase size-or-initial
135          (number
136           ;; Size is given as literal number
137           (list
138            `(make-data ',',type ,zero ,size-or-initial)
139            'free))
140          (vector
141           ;; Initial value supplied as literal CL vector)
142           (let ((argsymb (gensym "ARG")))
143             (list
144              `(make-data ',',type ,zero
145                (length ,argsymb))
146              'free
147              (lambda (symb) `(setf (data ,symb) ,argsymb))
148              (lambda () (list argsymb size-or-initial)))))
149          (t
150           ;; Determine at runtime whether first arg is initial or size
151           (let ((argsymb (gensym "ARG")))
152             (list
153              `(make-data ',',type ,zero
154                (if (numberp ,argsymb) ,argsymb (length ,argsymb)))
155              'free
156              (lambda (symb) `(unless (numberp ,argsymb) (setf (data ,symb) ,argsymb)))
157              (lambda () (list argsymb size-or-initial)))))))))
158
159(defparameter *data-class-name* nil
160  "A list classes, each consisting of a list
161   superclass, CL element type, class, GSL splice name.")
162(defmacro add-data-class (category element-type class superclass GSL-string)
163  `(eval-when (:compile-toplevel :load-toplevel :execute)
164      (pushnew (list ',category ',element-type ',class ',superclass ',GSL-string)
165               *data-class-name*
166               :test #'equal)))
167(defun data-type-lookup (category element-type)
168  (find (list category element-type)
169        *data-class-name*
170        :key (lambda (l) (subseq l 0 2))
171        :test #'equal))
172(defun data-class-name (category element-type)
173  (third (data-type-lookup category element-type)))
174(defun data-superclass-name (category element-type)
175  (fourth (data-type-lookup category element-type)))
176(defun data-gsl-string (category element-type)
177  (fifth (data-type-lookup category element-type)))
178
179(defmacro defdata (category cl-elt-type &optional (dimensions 1) splice-name)
180  "For the type named in the string,
181   define the allocator (gsl-*-alloc), zero allocator (gsl-*-calloc),
182   freeing (gsl-*-free), binary writing (binary-*-write) and
183   reading (binary-*-read), formatted writing (write-*-formatted)
184   and reading (read-*-formatted) functions."
185  (flet ((gsl-name (function-name)
186           (format nil "gsl_~a~a_~a"
187                   (data-gsl-string category cl-elt-type)
188                   (or splice-name
189                       (lookup-splice-name cl-elt-type)) function-name)))
190    (let* ((cargs (loop for i below dimensions
191                        collect `((nth ,i (storage-size object)) size)))
192           (class-name (data-class-name category cl-elt-type)))
193      `(progn
194        (defclass ,class-name (,(data-superclass-name category cl-elt-type))
195          ((cl-elt-type :initform ',cl-elt-type :reader cl-elt-type
196                        :allocation :class)))
197        (data-go ,class-name
198         ,(or (member category '(matrix combination))))
199        (defmfun alloc ((object ,class-name))
200          ,(gsl-name "alloc") ,cargs
201          :type :method
202          :c-return (cr :pointer)
203          :return ((assign-pointer object cr)))
204        (defmfun calloc ((object ,class-name))
205          ,(gsl-name "calloc") ,cargs
206          :type :method
207          :c-return (cr :pointer)
208          :return ((assign-pointer object cr)))
209        (defmfun free ((object ,class-name))
210          ,(gsl-name "free") (((pointer object) :pointer))
211          :type :method
212          :c-return :void)
213        (defmfun write-binary ((object ,class-name) stream)
214          ,(gsl-name "fwrite") ((stream :pointer) ((pointer object) :pointer))
215          :type :method)
216        (defmfun read-binary ((object ,class-name) stream)
217          ,(gsl-name "fread") ((stream :pointer) ((pointer object) :pointer))
218          :type :method)
219        (defmfun write-formatted ((object ,class-name) stream format)
220          ,(gsl-name "fprintf")
221          ((stream :pointer) ((pointer object) :pointer) (format :string))
222          :type :method)
223        (defmfun read-formatted ((object ,class-name) stream format)
224          ,(gsl-name "fscanf")
225          ((stream :pointer) ((pointer object) :pointer) (format :string))
226          :type :method)))))
227
228(defun defmfun-all (category cl-types args &optional key-string)
229  "A defmfun for each of the declared data types."
230  (let ((categories (if (listp category) category (list category))))
231    `(progn
232      ,@(loop for type in cl-types
233              for ctype = (lookup-C-type type)
234              collect
235              `(defmfun ,(first args)
236                ;; Set the class name for the arglist
237                ,(mapcar
238                  (lambda (x)
239                    (if (and (listp x) (member (second x) categories))
240                        (list (first x)
241                              (data-class-name (second x) type))
242                        x))
243                  (second args))
244                ;; Create the correct GSL C library function name
245                ,(splice-name
246                  (third args)
247                  ;; Use key-string to override the lookup of the category.
248                  (or key-string (data-gsl-string (first categories) type))
249                  type)
250                ,(mapcar
251                  (lambda (x)
252                    (if (eq (st-type x) :c-base-type)
253                        (list (st-symbol x) ctype)
254                        x))
255                  (fourth args))
256                ,@(let ((restargs (copy-list (nthcdr 4 args))))
257                       (when (eq (getf restargs :c-return) :c-base-type)
258                         (setf (getf restargs :c-return) ctype))
259                       (setf (getf restargs :type) :method)
260                       restargs))))))
261
262;;;;****************************************************************************
263;;;; Making data objects and initializing storage
264;;;;****************************************************************************
265
266(export '(make-data))
267
268(defgeneric alloc (object)
269  (:documentation "Allocate GSL data; used internally."))
270
271(defgeneric calloc (object)
272  (:documentation "Allocate GSL data and clear; used internally."))
273
274(export 'free)
275(defgeneric free (object)
276  (:documentation "Free GSL data.")
277  (:method :after ((object gsl-data)) (setf (pointer object) nil)))
278
279(defun make-data (type zero &rest size)
280  "Make the GSL data object, including the allocation of space.
281   The user is responsible for calling #'free to free the foreign
282   memory when done."
283  (let ((obj (make-instance type :storage-size size)))
284    (if zero (calloc obj) (alloc obj))
285    obj))
286
287;;;;****************************************************************************
288;;;; Getting values into CL
289;;;;****************************************************************************
290
291(defun cl-invalidate (&rest objects)
292  "Mark the CL image of the GSL array as invalid."
293  (mapc (lambda (obj) (setf (cl-invalid obj) t))
294        objects))
295
296(export 'data)
297(defgeneric data (object &optional destination)
298  (:documentation "Extract the values in the object to a CL object.
299   The destination may be a sequence, 'vector, 'list.
300   If it is a sequence, that object is filled with the values.
301   If it is any other object, a new sequence is made of the type
302   specified.")
303  ;; Default method is to make a sequence
304  (:method ((object gsl-data) &optional (sequence 'vector))
305    (if (eq (cl-invalid object) t)
306        ;; set everything
307        (let* ((total-size (apply #'* (storage-size object)))
308               (seq
309                (case sequence
310                  (list (make-list total-size))
311                  ((nil vector)
312                   (make-array (list total-size)
313                               :element-type (cl-elt-type object)))
314                  (t sequence))))
315          (loop for i from 0
316             below (min (length seq) total-size)
317             do (setf (elt seq i) (maref object i)))
318          seq)
319        ;; set selected
320        (let ((seq (data-cache object)))
321          (mapc (lambda (is)
322                  (let ((i (first is)))
323                    (setf (elt seq i) (maref object i))))
324                (cl-invalid object))
325          seq)))
326  ;; Around method looks for cached value and returns it if valid;
327  ;; otherwise computes CL element(s).
328  (:method :around ((object gsl-data) &optional (sequence 'vector))
329           (declare (ignore sequence))
330           (when (cl-invalid object)
331             (setf (data-cache object)
332                   (call-next-method)
333                   (cl-invalid object)
334                   nil))
335           (data-cache object)))
336
337;;;;****************************************************************************
338;;;; Setting values from CL
339;;;;****************************************************************************
340
341(defgeneric (setf data) (cl-array object)
342  (:documentation "Set the values in the object from a CL array.")
343  ;; Default method is to read from a sequence
344  (:method (sequence (object gsl-data))
345    (loop for i from 0
346       below (min (length sequence) (apply #'* (storage-size object)))
347       do (setf (maref object i) (elt sequence i))))
348  (:method :after (source (object gsl-data))
349           (setf (cl-invalid object) t)))
350
351(export 'set-all)
352(defgeneric set-all (object value)
353  (:documentation "Set all elements to the value.")
354  (:method :after ((object gsl-data) value) (cl-invalidate object)))
355
356(export 'set-zero)
357(defgeneric set-zero (object)
358  (:documentation "Set all elements to 0.")
359  (:method :after ((object gsl-data)) (cl-invalidate object)))
360
361(export 'set-identity)
362(defgeneric set-identity (object)
363  (:documentation "Set elements to represent the identity.")
364  (:method :after ((object gsl-data)) (cl-invalidate object)))
365
366(export 'set-basis)
367(defgeneric set-basis (object index)
368  (:documentation "Set indexth basis vector."))
369
370(export 'data-valid)
371(defgeneric data-valid (object)
372  (:documentation "Validate the values in the object."))
373
374;;;;****************************************************************************
375;;;; Copying
376;;;;****************************************************************************
377
378(export '(copy swap))
379(defgeneric copy (destination source)
380  (:documentation "Copy from source to destination."))
381
382(defgeneric swap (obj1 obj2)
383  (:documentation "Swap contents of obj1 and obj2."))
384
385;;;;****************************************************************************
386;;;; Arithmetic operations
387;;;;****************************************************************************
388
389(export '(m+ m- m* m/ m*c m+c))
390
391(defgeneric m+ (a b)
392  (:documentation "Add."))
393
394(defgeneric m- (a b)
395  (:documentation "Subtract."))
396
397(defgeneric m* (a b)
398  (:documentation "Multiply."))
399
400(defgeneric m/ (a b)
401  (:documentation "Divide."))
402
403(defgeneric m+c (a x)
404  (:documentation "Add scalar."))
405
406(defgeneric m*c (a x)
407  (:documentation "Multiply by scalar."))
408
409;;;;****************************************************************************
410;;;; Maximum and minimum elements
411;;;;****************************************************************************
412
413(export
414 '(gsl-max gsl-min gsl-minmax gsl-max-index gsl-min-index gsl-minmax-index))
415
416(defgeneric gsl-min (a)
417  (:documentation "Minimum."))
418
419(defgeneric gsl-max (a)
420  (:documentation "Maximum."))
421
422(defgeneric gsl-minmax (a)
423  (:documentation "Minimum and maximum."))
424
425(defgeneric gsl-min-index (a)
426  (:documentation "Index of minimum."))
427
428(defgeneric gsl-max-index (a)
429  (:documentation "Index of maximum."))
430
431(defgeneric gsl-minmax-index (a)
432  (:documentation "Indices of minimum and maximum."))
433
434;;;;****************************************************************************
435;;;; Properties
436;;;;****************************************************************************
437
438(export '(gsl-zerop))
439
440(defgeneric gsl-zerop (a)
441  (:documentation "Object is zero."))
Note: See TracBrowser for help on using the browser.