macro for efficient array operations

Quote:

> > > In Scheme, I would like to be able to write:

> > > (tensor (i j) (assign-to (C i j) (+ (A i j) (B i j))))

As Will noted, this is missing vital information and is also somewhat

restrictive of the operations permissible within the TENSOR special

form.

Something like:

(tensor-for-each (A B C) (i j)

(tensor-set! C (+ (tensor-ref A i j)

(tensor-ref B i j))))

would be rather more Scheme-like. TENSOR-FOR-EACH is still a special

form here (because of the list notation for the list of tensors). A

fully procedural abstraction would look like:

(tensor-for-each

(lambda (i j)

(tensor-set! C (+ (tensor-ref A i j)

(tensor-ref B i j))))

A B C)

The salient points of both forms being that TENSOR-FOR-EACH needs to

be able to look at each of the tensors to derive the correct shape of

the iteration. This also gives you an oppurtunity to detect various

type problems (mismatching dimensionalities, different basis sizes,

etc...) A very naive implementation (completely untested and essentially

pulled directly out of my arse) that uses SRFI-9 for the tedious

structure definition bits follows immediately:

(define-record-type tensor

; going to use a flattened data representation instead of

; a vector of vectors; just for grins

(make-tensor dimension-vector data)

tensor?

(dimension-vector tensor-shape tensor-shape!)

(data tensor-data tensor-data!))

(define (tensor-dimensionality tensor) (vector-length (tensor-shape tensor)))

(define (tensor-dimension tensor n) (vector-ref (tensor-shape tensor) n))

(define (tensor-validate-indices t . indices)

(let ((requested-shape (length indices))

(shape (tensor-shape t)))

(if (= requested-shape (tensor-dimensionality t))

(let build ((dim 0) (indices indices) (data-ref 0))

(if (null? indices)

data-ref

(let ((index (car indices))

(dimension (tensor-dimension t dim)))

(and (< index dimension)

(build (+ dim 1)

(cdr indices)

(+ (* data-ref dimension)

index))

)))))))

(define (tensor-ref tensor . indices)

(let ((index (tensor-validate-indices tensor indices)))

(if index

(vector-ref (tensor-data tensor index))

(error 'invalid-tensor-ref))))

(define (tensor-set! tensor val . indices)

(let ((index (tensor-validate-indices tensor indices)))

(if index

(vector-set! (tensor-data tensor index) val)

(error 'invalid-tensor-ref))))

(define-syntax tensor-for-each

; because I think that the special form is more readable

; and has slightly greater potential for robustness...

(syntax-rules ()

((tensor-for-each (tensor0 tensor+ ...)

(index0 index+ ...)

exp0 exp+ ...)

(%tensor-for-each (list tensor0 tensor+ ...)

(length '(index0 index+ ...))

(lambda (index0 index+ ...) exp0 exp+ ...)))

))

(define (%tensor-for-each ts iteration-depth f)

(if (pair? ts)

(let ((basic-shape (tensor-shape (car ts))))

(if (fold (lambda (t valid?)

(and valid?

(< (- iteration-depth 1)

(tensor-dimensionality t))

(equal? basic-shape (tensor-shape t))

))

#t

ts)

(for-all-tuples-in (vector-take basic-shape iteration-depth) f)

))))

FOLD, FOR-ALL-TUPLES-IN, and VECTOR-TAKE are left as an exercise for the reader.

Quote:

> > > Other examples for the notation is :

> > > (tensor (i j) (assign-to (B i j) (A j i))) will do B[i,j] = A[j,i]

> > > (Transposition)

> > > (tensor (i j k) (assign-to (C i j k) (+ (A i j) (B j k)))) will do

> > > C[i,j,k] = A[i,j] + B[j,k] (where C is a vector of vector of vector)

recasting these with my definitions:

(tensor-for-each (A B) (i j) (tensor-set! B (tensor-ref A j i) i j))

(tensor-for-each (A B C) (i j k)

(tensor-set! C (+ (tensor-ref A i j) (tensor-ref B j k))

i j k))

OK, the fact that the variable-length indices must be last arguments

to the tensor mutator is annoying, but that could be macrologized,

too. e.g.

(define-syntax tensor

(syntax-rules (set ref)

((tensor set t (ix0 ix+ ...) v) (tensor-set! t v ix0 ix+ ...))

((tensor ref t (ix0 ix+ ...) v) (tensor-ref t ix0 ix+ ...))

))

or somesuch (again *completely* untested).

Quote:

> > > How can I implement the macro tensor to be able to deal with any number

> > > of indices (i j ... ) and to do what I have described ?

I hope that I have added some value to Will Clinger's observation. The

above, while not exactly providing the forms you wanted, captures the

fundamental abstractions which I think you were expressing in a more

flexible fashion. HTH.

david rush

--

The beginning of wisdom for a [software engineer] is to recognize the

difference between getting a program to work, and getting it right.

-- M A Jackson, 1975