Hi,
I need to construct matrices which are "semi-constant": many elements
are constant, but a few may be calculated runtime. A further
complication is that these matrices need to be column-major. I am
optimizing for speed (and not too much consing).
I thought I would write a macro that constructs a matrix and fills it
with elements. Since many elements are zero, I thought I would set
that in make-array and not set those elements individually. I am counting
on the compiler to fold (coerced) constants, so that I don't have to use
decimal dots, making usage convenient. Here is the resulting macro, with
an example:
(defmacro c-colmajor-matrix-double (&body list-of-rows)
"Construct a matrix of double-floats from given elements (lists of
lists). Elements may be constants or forms, and are coerced to the
correct type."
(let* ((ncol (length (first list-of-rows)))
(nrow (length list-of-rows))
(array-name (gensym "vector")))
`(let ((,array-name (make-array '(,ncol ,nrow) :element-type 'double-float
:initial-element 0d0)))
,@(iter outer
(for row-list :in list-of-rows)
(for row :from 0)
(assert (= (length row-list) ncol) ()
"Invalid number of elements in row ~A." row)
(iter
(for element :in row-list)
(for col :from 0)
(unless (and (numberp element) (zerop element))
(in outer
(collect `(setf (aref ,array-name ,col ,row)
(coerce ,element 'double-float)))))))
,array-name)))
(defun make-special-matrix (a b)
"Inane example."
(declare (optimize speed)
(double-float a b))
(c-colmajor-matrix-double (0 0 0 1 (* a b))
(12 a b 0 0)
((- a b) 0 0 0 -7.8)))
where the macro expands to
(LET ((#:|vector1541|
(MAKE-ARRAY '(5 3) :ELEMENT-TYPE 'DOUBLE-FLOAT :INITIAL-ELEMENT 0.0d0)))
(SETF (AREF #:|vector1541| 3 0) (COERCE 1 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 4 0) (COERCE (* A B) 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 0 1) (COERCE 12 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 1 1) (COERCE A 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 2 1) (COERCE B 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 0 2) (COERCE (- A B) 'DOUBLE-FLOAT))
(SETF (AREF #:|vector1541| 4 2) (COERCE -7.8 'DOUBLE-FLOAT))
#:|vector1541|)
However, the disassembly indicates that this may not be the most
"natural" way to do this in SBCL. Is there a better one?
Thanks,
Tamas
|