242 lines
5.1 KiB
CoffeeScript
242 lines
5.1 KiB
CoffeeScript
# Transpose tensor indices
|
|
|
|
|
|
|
|
Eval_transpose = ->
|
|
push(cadr(p1))
|
|
Eval()
|
|
|
|
# add default params if they
|
|
# have not been passed
|
|
if (cddr(p1) == symbol(NIL))
|
|
push_integer(1)
|
|
push_integer(2)
|
|
else
|
|
push(caddr(p1))
|
|
Eval()
|
|
push(cadddr(p1))
|
|
Eval()
|
|
transpose()
|
|
|
|
transpose = ->
|
|
i = 0
|
|
j = 0
|
|
k = 0
|
|
l = 0
|
|
m = 0
|
|
ndim = 0
|
|
nelem = 0
|
|
t = 0
|
|
ai = []
|
|
an = []
|
|
for i in [0...MAXDIM]
|
|
ai[i] = 0
|
|
an[i] = 0
|
|
|
|
#U **a, **b
|
|
|
|
save()
|
|
|
|
# by default p3 is 2 and p2 is 1
|
|
p3 = pop() # index to be transposed
|
|
p2 = pop() # other index to be transposed
|
|
p1 = pop() # what needs to be transposed
|
|
|
|
# a transposition just goes away when
|
|
# applied to a scalar
|
|
if (isNumericAtom(p1))
|
|
push p1
|
|
restore()
|
|
return
|
|
|
|
# transposition goes away for identity matrix
|
|
if ((isplusone(p2) and isplustwo(p3)) or (isplusone(p3) and isplustwo(p2)))
|
|
if isidentitymatrix(p1)
|
|
push p1
|
|
restore()
|
|
return
|
|
|
|
# a transposition just goes away when
|
|
# applied to another transposition with
|
|
# the same columns to be switched
|
|
if (istranspose(p1))
|
|
innerTranspSwitch1 = car(cdr(cdr(p1)))
|
|
innerTranspSwitch2 = car(cdr(cdr(cdr(p1))))
|
|
|
|
if ( equal(innerTranspSwitch1,p3) and equal(innerTranspSwitch2,p2) ) or
|
|
( equal(innerTranspSwitch2,p3) and equal(innerTranspSwitch1,p2) ) or
|
|
(( equal(innerTranspSwitch1,symbol(NIL)) and equal(innerTranspSwitch2,symbol(NIL)) ) and ((isplusone(p3) and isplustwo(p2)) or ((isplusone(p2) and isplustwo(p3)))))
|
|
push car(cdr(p1))
|
|
restore()
|
|
return
|
|
|
|
# if operand is a sum then distribute
|
|
# (if we are in expanding mode)
|
|
if (expanding && isadd(p1))
|
|
p1 = cdr(p1)
|
|
push(zero)
|
|
while (iscons(p1))
|
|
push(car(p1))
|
|
# add the dimensions to switch but only if
|
|
# they are not the default ones.
|
|
push(p2)
|
|
push(p3)
|
|
transpose()
|
|
add()
|
|
p1 = cdr(p1)
|
|
restore()
|
|
return
|
|
|
|
# if operand is a multiplication then distribute
|
|
# (if we are in expanding mode)
|
|
if (expanding && ismultiply(p1))
|
|
p1 = cdr(p1)
|
|
push(one)
|
|
while (iscons(p1))
|
|
push(car(p1))
|
|
# add the dimensions to switch but only if
|
|
# they are not the default ones.
|
|
push(p2)
|
|
push(p3)
|
|
transpose()
|
|
multiply()
|
|
p1 = cdr(p1)
|
|
restore()
|
|
return
|
|
|
|
# distribute the transpose of a dot
|
|
# if in expanding mode
|
|
# note that the distribution happens
|
|
# in reverse as per tranpose rules.
|
|
# The dot operator is not
|
|
# commutative, so, it matters.
|
|
if (expanding && isinnerordot(p1))
|
|
p1 = cdr(p1)
|
|
accumulator = []
|
|
while (iscons(p1))
|
|
accumulator.push [car(p1),p2,p3]
|
|
p1 = cdr(p1)
|
|
|
|
for eachEntry in [accumulator.length-1..0]
|
|
push(accumulator[eachEntry][0])
|
|
push(accumulator[eachEntry][1])
|
|
push(accumulator[eachEntry][2])
|
|
transpose()
|
|
if eachEntry != accumulator.length-1
|
|
inner()
|
|
|
|
restore()
|
|
return
|
|
|
|
|
|
if (!istensor(p1))
|
|
if (!isZeroAtomOrTensor(p1))
|
|
#stop("transpose: tensor expected, 1st arg is not a tensor")
|
|
push_symbol(TRANSPOSE)
|
|
push(p1)
|
|
# remove the default "dimensions to be switched"
|
|
# parameters
|
|
if (!isplusone(p2) or !isplustwo(p3)) and (!isplusone(p3) or !isplustwo(p2))
|
|
push(p2)
|
|
push(p3)
|
|
list(4)
|
|
else
|
|
list(2)
|
|
restore()
|
|
return
|
|
push(zero)
|
|
restore()
|
|
return
|
|
|
|
ndim = p1.tensor.ndim
|
|
nelem = p1.tensor.nelem
|
|
|
|
# is it a vector?
|
|
# so here it's something curious - note how vectors are
|
|
# not really special two-dimensional matrices, but rather
|
|
# 1-dimension objects (like tensors can be). So since
|
|
# they have one dimension, transposition has no effect.
|
|
# (as opposed as if they were special two-dimensional
|
|
# matrices)
|
|
# see also Ran Pan, Tensor Transpose and Its Properties. CoRR abs/1411.1503 (2014)
|
|
|
|
if (ndim == 1)
|
|
push(p1)
|
|
restore()
|
|
return
|
|
|
|
push(p2)
|
|
l = pop_integer()
|
|
|
|
push(p3)
|
|
m = pop_integer()
|
|
|
|
if (l < 1 || l > ndim || m < 1 || m > ndim)
|
|
stop("transpose: index out of range")
|
|
|
|
l--
|
|
m--
|
|
|
|
p2 = alloc_tensor(nelem)
|
|
|
|
p2.tensor.ndim = ndim
|
|
|
|
for i in [0...ndim]
|
|
p2.tensor.dim[i] = p1.tensor.dim[i]
|
|
|
|
p2.tensor.dim[l] = p1.tensor.dim[m]
|
|
p2.tensor.dim[m] = p1.tensor.dim[l]
|
|
|
|
a = p1.tensor.elem
|
|
b = p2.tensor.elem
|
|
|
|
# init tensor index
|
|
|
|
for i in [0...ndim]
|
|
ai[i] = 0
|
|
an[i] = p1.tensor.dim[i]
|
|
|
|
# copy components from a to b
|
|
|
|
for i in [0...nelem]
|
|
|
|
# swap indices l and m
|
|
|
|
t = ai[l]; ai[l] = ai[m]; ai[m] = t;
|
|
t = an[l]; an[l] = an[m]; an[m] = t;
|
|
|
|
# convert tensor index to linear index k
|
|
|
|
k = 0
|
|
for j in [0...ndim]
|
|
k = (k * an[j]) + ai[j]
|
|
|
|
# swap indices back
|
|
|
|
t = ai[l]; ai[l] = ai[m]; ai[m] = t;
|
|
t = an[l]; an[l] = an[m]; an[m] = t;
|
|
|
|
# copy one element
|
|
|
|
b[k] = a[i]
|
|
|
|
# increment tensor index
|
|
|
|
# Suppose the tensor dimensions are 2 and 3.
|
|
# Then the tensor index ai increments as follows:
|
|
# 00 -> 01
|
|
# 01 -> 02
|
|
# 02 -> 10
|
|
# 10 -> 11
|
|
# 11 -> 12
|
|
# 12 -> 00
|
|
|
|
for j in [(ndim - 1)..0]
|
|
if (++ai[j] < an[j])
|
|
break
|
|
ai[j] = 0
|
|
|
|
push(p2)
|
|
restore()
|
|
|