From: Tamas Papp
Subject: arrays + ffi
Date: 
Message-ID: <87r6m2dw78.fsf@pu100877.student.princeton.edu>
When I first encountered Lisp a couple of months ago, I decided that
it was the ideal language for numerical computations (at least for the
kind I am doing).  The only thing I disliked were the arrays: they
were column-major (while I got used to thinking column-major), and I
found it messy to interface them to foreign functions.

Now I realize that I have been too quick to judge Lisp arrays: they
are neat and well designed.  I figured out a few ways to work around
some things, and I would like advice on how to do things right.

1. reduce and arrays

Suppose I want to find maxima, minima etc of arrays.  Is this the
right way?

(defun array-reduce (function array)
  (reduce function (make-array (array-total-size array) 
			       :element-type (array-element-type array)
			       :displaced-to array)))

(array-reduce #'max array)  ; find maximum


2. multidimensional arrays and FFI

Most implementations are happy to provide a pointer to arrays with
certain element types (I know, you better stop the GC (CMUCL) or pin
down the array (SBCL), I will disregard these things in the code
below).  Now consider this code:

(defparameter *a-m* (make-array '(3 4) 
				:element-type '(unsigned-byte 32)
				:initial-element 0))
(defparameter *a-v* (make-array 12 :displaced-to *a-m*
				:element-type '(unsigned-byte 32)))
(sb-sys:vector-sap *a-v*)		; won't work

(defparameter *b-v* (make-array 12
				:element-type '(unsigned-byte 32)
				:initial-element 0))
(defparameter *b-m* (make-array '(3 4) :displaced-to *b-v*
				:element-type '(unsigned-byte 32)))
(sb-sys:vector-sap *b-v*)		; will work

Apparently, if an array was created flat, then I can easily get its
address.  But when it was created flat using displacement, I can't.

Possible workaround: for making every array, create one with a flat
original array and then displace.

(defun make-array-with-flat-original (dimensions element-type)
  (if (= (length dimensions) 1)
      (make-array dimensions :element-type element-type)
      (let ((original (make-array (reduce #'* dimensions)
				  :element-type element-type)))
	(make-array dimensions :element-type element-type
		    :displaced-to original))))

(defun find-original-array (array)
  "Chase down original array."
  (do ((a array))
      (nil)
    (let ((displacement (array-displacement a)))
      (if displacement
	  (setf a displacement)
	  (return-from find-original-array a)))))

(sb-sys:vector-sap (find-original-array
		    (make-array-with-flat-original 
		     '(3 4)
		     'double-float)))    ; works fine

From: Pillsy
Subject: Re: arrays + ffi
Date: 
Message-ID: <1187367212.550580.21840@w3g2000hsg.googlegroups.com>
On Aug 17, 11:20 am, Tamas Papp <······@gmail.com> wrote:
[...]
> 1. reduce and arrays

> Suppose I want to find maxima, minima etc of arrays.  Is this the
> right way?

> (defun array-reduce (function array)
>   (reduce function (make-array (array-total-size array)
>                                :element-type (array-element-type array)
>                                :displaced-to array)))

It works and it's elegant. Are you having a problem with it?

The only thing I might do is change it so it looks like this:

(defun array-reduce (function array &rest args &key &allow-other-keys)
  (apply #'reduce function
	 (make-array (array-total-size array)
		     :element-type (array-element-type array)
		     :displaced-to array)
	 args))

which allows you to pass the various keyword arguments to REDUCE.
However, I don't think most of them are super-relevant to this
function in the first place, so it's no big deal.

> 2. multidimensional arrays and FFI
[...]
> (defun find-original-array (array)
>   "Chase down original array."
>   (do ((a array))
>       (nil)
>     (let ((displacement (array-displacement a)))
>       (if displacement
>           (setf a displacement)
>           (return-from find-original-array a)))))

The way you're using DO here looks really obscure to me. It's perfect
job for recursion.

(defun find-original-array (array)
  (let ((displacement (array-displacement array)))
    (if displacement
	(find-original-array displacement)
	array)))

Cheers,
Pillsy
From: Tamas Papp
Subject: Re: arrays + ffi
Date: 
Message-ID: <87mywqds1c.fsf@pu100877.student.princeton.edu>
Pillsy <·········@gmail.com> writes:

> On Aug 17, 11:20 am, Tamas Papp <······@gmail.com> wrote:
> [...]
>> 1. reduce and arrays
>
>> Suppose I want to find maxima, minima etc of arrays.  Is this the
>> right way?
>
>> (defun array-reduce (function array)
>>   (reduce function (make-array (array-total-size array)
>>                                :element-type (array-element-type array)
>>                                :displaced-to array)))
>
> It works and it's elegant. Are you having a problem with it?
>
> The only thing I might do is change it so it looks like this:
>
> (defun array-reduce (function array &rest args &key &allow-other-keys)
>   (apply #'reduce function
> 	 (make-array (array-total-size array)
> 		     :element-type (array-element-type array)
> 		     :displaced-to array)
> 	 args))
>
> which allows you to pass the various keyword arguments to REDUCE.
> However, I don't think most of them are super-relevant to this
> function in the first place, so it's no big deal.

Thanks, I will think about it.  :from-end could be relevant, but not
the rest.  Am I mistaken in thinking that using apply could be less
efficient compared to funcall?

>
>> 2. multidimensional arrays and FFI
> [...]
>> (defun find-original-array (array)
>>   "Chase down original array."
>>   (do ((a array))
>>       (nil)
>>     (let ((displacement (array-displacement a)))
>>       (if displacement
>>           (setf a displacement)
>>           (return-from find-original-array a)))))
>
> The way you're using DO here looks really obscure to me. It's perfect
> job for recursion.
>
> (defun find-original-array (array)
>   (let ((displacement (array-displacement array)))
>     (if displacement
> 	(find-original-array displacement)
> 	array)))

Yes, but this is not tail-recursive.  Not a big deal for small depths
I guess.  

I have been working on this and realized that I might need the sum of
index offsets.  I came up with a version using tagbody (because do is
indeed obscure here), is this bad style?  If so, improvements would be
appreaciated, especially non-recursive ones.

(defun displace-array (array dimensions index-offset)
  "Make a displaced array from array with the given dimensions and the
index-offset and the same element-type as array."
  (make-array dimensions :element-type (array-element-type array)
	      :displaced-to array :displaced-index-offset index-offset))

(defun find-original-array (array)
  "Find the original parent of a displaced array, return this and the
sum of displaced index offsets."
  (let ((sum-of-offsets 0))
    (tagbody
     check-displacement
       (multiple-value-bind (displaced-to displaced-index-offset)
	   (array-displacement array)
	 (format t "~a ~a~%" displaced-to displaced-index-offset)
	 (when displaced-to
	   (setf array displaced-to)
	   (incf sum-of-offsets displaced-index-offset)
	   (go check-displacement))))
    (values array sum-of-offsets)))


;; example
(defparameter *a* (make-array 19))
(defparameter *b* (displace-array *a* '(4 4) 2))
(defparameter *c* (displace-array *b* 7 3))
(multiple-value-bind (original index-sum) (find-original-array *c*)
  (values (eq *a* original) index-sum)) ; => T,5

Thanks,

Tamas
From: Pillsy
Subject: Re: arrays + ffi
Date: 
Message-ID: <1187371495.513634.269020@w3g2000hsg.googlegroups.com>
On Aug 17, 12:50 pm, Tamas Papp <······@gmail.com> wrote:
> Pillsy <·········@gmail.com> writes:
[...]
> > The only thing I might do is change it so it looks like this:

> > (defun array-reduce (function array &rest args &key &allow-other-keys)
> >   (apply #'reduce function
> >     (make-array (array-total-size array)
> >                 :element-type (array-element-type array)
> >                 :displaced-to array)
> >     args))

> > which allows you to pass the various keyword arguments to REDUCE.
> > However, I don't think most of them are super-relevant to this
> > function in the first place, so it's no big deal.

> Thanks, I will think about it.  :from-end could be relevant, but not
> the rest.  Am I mistaken in thinking that using apply could be less
> efficient compared to funcall?

It could be, but I doubt it's likely to be particularly important in
most cases. It's only going to come up once per call to ARRAY-REDUCE,
after all.

> > The way you're using DO here looks really obscure to me. It's perfect
> > job for recursion.

> > (defun find-original-array (array)
> >   (let ((displacement (array-displacement array)))
> >     (if displacement
> >    (find-original-array displacement)
> >    array)))

> Yes, but this is not tail-recursive.  Not a big deal for small depths
> I guess.  

Sure it is! Look at it again, and if you still don't believe me, try
tracing it.

This all assumes you aren't using declarations that prevent tail-call
elimination (high levels of
DEBUG will do this in SBCL):

http://www.sbcl.org/manual/Debug-Tail-Recursion.html

> I have been working on this and realized that I might need the sum of
> index offsets.  I came up with a version using tagbody (because do is
> indeed obscure here), is this bad style?  If so, improvements would be
> appreaciated, especially non-recursive ones.

I don't find it particularly ugly in this case, but then I usually
worked in Fortran before discovering CL. ;)

I still think the (tail-)recursive version is nicer:

(defun find-original-array (array)
  (labels ((recurse (array total-offset)
	     (multiple-value-bind (displaced-to offset)
		 (array-displacement array)
	       (if displaced-to
		   (recurse displaced-to (+ offset total-offset))
		   (values displaced-to total-offset)))))
    (recurse array 0)))

Anyway, for those times you are using a TAGBODY and a LET one inside
the other, you can fold them both into PROG.

Cheers,
Pillsy
From: Zach Beane
Subject: Re: arrays + ffi
Date: 
Message-ID: <m3mywqdqkv.fsf@unnamed.xach.com>
Tamas Papp <······@gmail.com> writes:

> I have been working on this and realized that I might need the sum of
> index offsets.  I came up with a version using tagbody (because do is
> indeed obscure here), is this bad style?  If so, improvements would be
> appreaciated, especially non-recursive ones.
[snip]

Here's Erik Naggum's version:

  http://groups.google.com/group/comp.lang.lisp/msg/f527904bd5167a83

Zach