UNPKG

4.82 kBtext/coffeescriptView Raw
1# Transpose tensor indices
2
3
4
5Eval_transpose = ->
6 push(cadr(p1))
7 Eval()
8
9 # add default params if they
10 # have not been passed
11 if (cddr(p1) == symbol(NIL))
12 push_integer(1)
13 push_integer(2)
14 else
15 push(caddr(p1))
16 Eval()
17 push(cadddr(p1))
18 Eval()
19 transpose()
20
21transpose = ->
22 i = 0
23 j = 0
24 k = 0
25 l = 0
26 m = 0
27 ndim = 0
28 nelem = 0
29 t = 0
30 ai = []
31 an = []
32 for i in [0...MAXDIM]
33 ai[i] = 0
34 an[i] = 0
35
36 #U **a, **b
37
38 save()
39
40 # by default p3 is 2 and p2 is 1
41 p3 = pop() # index to be transposed
42 p2 = pop() # other index to be transposed
43 p1 = pop() # what needs to be transposed
44
45 # a transposition just goes away when
46 # applied to a scalar
47 if (isnum(p1))
48 push p1
49 restore()
50 return
51
52 # transposition goes away for identity matrix
53 if ((isplusone(p2) and isplustwo(p3)) or (isplusone(p3) and isplustwo(p2)))
54 if isidentitymatrix(p1)
55 push p1
56 restore()
57 return
58
59 # a transposition just goes away when
60 # applied to another transposition with
61 # the same columns to be switched
62 if (istranspose(p1))
63 innerTranspSwitch1 = car(cdr(cdr(p1)))
64 innerTranspSwitch2 = car(cdr(cdr(cdr(p1))))
65
66 if ( equal(innerTranspSwitch1,p3) and equal(innerTranspSwitch2,p2) ) or
67 ( equal(innerTranspSwitch2,p3) and equal(innerTranspSwitch1,p2) ) or
68 (( equal(innerTranspSwitch1,symbol(NIL)) and equal(innerTranspSwitch2,symbol(NIL)) ) and ((isplusone(p3) and isplustwo(p2)) or ((isplusone(p2) and isplustwo(p3)))))
69 push car(cdr(p1))
70 restore()
71 return
72
73 # if operand is a sum then distribute
74 # (if we are in expanding mode)
75 if (expanding && isadd(p1))
76 p1 = cdr(p1)
77 push(zero)
78 while (iscons(p1))
79 push(car(p1))
80 # add the dimensions to switch but only if
81 # they are not the default ones.
82 push(p2)
83 push(p3)
84 transpose()
85 add()
86 p1 = cdr(p1)
87 restore()
88 return
89
90 # if operand is a multiplication then distribute
91 # (if we are in expanding mode)
92 if (expanding && ismultiply(p1))
93 p1 = cdr(p1)
94 push(one)
95 while (iscons(p1))
96 push(car(p1))
97 # add the dimensions to switch but only if
98 # they are not the default ones.
99 push(p2)
100 push(p3)
101 transpose()
102 multiply()
103 p1 = cdr(p1)
104 restore()
105 return
106
107 # distribute the transpose of a dot
108 # if in expanding mode
109 # note that the distribution happens
110 # in reverse as per tranpose rules.
111 # The dot operator is not
112 # commutative, so, it matters.
113 if (expanding && isinnerordot(p1))
114 p1 = cdr(p1)
115 accumulator = []
116 while (iscons(p1))
117 accumulator.push [car(p1),p2,p3]
118 p1 = cdr(p1)
119
120 for eachEntry in [accumulator.length-1..0]
121 push(accumulator[eachEntry][0])
122 push(accumulator[eachEntry][1])
123 push(accumulator[eachEntry][2])
124 transpose()
125 if eachEntry != accumulator.length-1
126 inner()
127
128 restore()
129 return
130
131
132 if (!istensor(p1))
133 if (!iszero(p1))
134 #stop("transpose: tensor expected, 1st arg is not a tensor")
135 push_symbol(TRANSPOSE)
136 push(p1)
137 # remove the default "dimensions to be switched"
138 # parameters
139 if (!isplusone(p2) or !isplustwo(p3)) and (!isplusone(p3) or !isplustwo(p2))
140 push(p2)
141 push(p3)
142 list(4)
143 else
144 list(2)
145 restore()
146 return
147 push(zero)
148 restore()
149 return
150
151 ndim = p1.tensor.ndim
152 nelem = p1.tensor.nelem
153
154 # is it a vector?
155 # so here it's something curious - note how vectors are
156 # not really special two-dimensional matrices, but rather
157 # 1-dimension objects (like tensors can be). So since
158 # they have one dimension, transposition has no effect.
159 # (as opposed as if they were special two-dimensional
160 # matrices)
161 # see also Ran Pan, Tensor Transpose and Its Properties. CoRR abs/1411.1503 (2014)
162
163 if (ndim == 1)
164 push(p1)
165 restore()
166 return
167
168 push(p2)
169 l = pop_integer()
170
171 push(p3)
172 m = pop_integer()
173
174 if (l < 1 || l > ndim || m < 1 || m > ndim)
175 stop("transpose: index out of range")
176
177 l--
178 m--
179
180 p2 = alloc_tensor(nelem)
181
182 p2.tensor.ndim = ndim
183
184 for i in [0...ndim]
185 p2.tensor.dim[i] = p1.tensor.dim[i]
186
187 p2.tensor.dim[l] = p1.tensor.dim[m]
188 p2.tensor.dim[m] = p1.tensor.dim[l]
189
190 a = p1.tensor.elem
191 b = p2.tensor.elem
192
193 # init tensor index
194
195 for i in [0...ndim]
196 ai[i] = 0
197 an[i] = p1.tensor.dim[i]
198
199 # copy components from a to b
200
201 for i in [0...nelem]
202
203 # swap indices l and m
204
205 t = ai[l]; ai[l] = ai[m]; ai[m] = t;
206 t = an[l]; an[l] = an[m]; an[m] = t;
207
208 # convert tensor index to linear index k
209
210 k = 0
211 for j in [0...ndim]
212 k = (k * an[j]) + ai[j]
213
214 # swap indices back
215
216 t = ai[l]; ai[l] = ai[m]; ai[m] = t;
217 t = an[l]; an[l] = an[m]; an[m] = t;
218
219 # copy one element
220
221 b[k] = a[i]
222
223 # increment tensor index
224
225 # Suppose the tensor dimensions are 2 and 3.
226 # Then the tensor index ai increments as follows:
227 # 00 -> 01
228 # 01 -> 02
229 # 02 -> 10
230 # 10 -> 11
231 # 11 -> 12
232 # 12 -> 00
233
234 for j in [(ndim - 1)..0]
235 if (++ai[j] < an[j])
236 break
237 ai[j] = 0
238
239 push(p2)
240 restore()
241