casadi_tensor_ttv.hpp
1 //
2 // MIT No Attribution
3 //
4 // Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
7 // software and associated documentation files (the "Software"), to deal in the Software
8 // without restriction, including without limitation the rights to use, copy, modify,
9 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 
20 // Tensor times vector in all modes (ttv) - recursive multi-mode contraction.
21 //
22 // Given an N-dimensional coefficient tensor C of shape (m, n_0, n_1, ..., n_{N-1})
23 // stored in column-major order with m as the leading (fastest) dimension,
24 // and N weight vectors w_0, w_1, ..., w_{N-1}, compute:
25 //
26 // ret_j += sum_{i_0, ..., i_{N-1}} w_0[i_0] * w_1[i_1] * ... * w_{N-1}[i_{N-1}]
27 // * C[j, s_0+i_0, s_1+i_1, ..., s_{N-1}+i_{N-1}]
28 //
29 // for j = 0, ..., m-1, where s_k = starts[k] selects the active sub-tensor.
30 //
31 // The result is accumulated into ret (not cleared).
32 //
33 // The recursion peels off one dimension at a time (outermost first):
34 // - dim > 0: loop over i_{dim}, multiply weight, add offset, recurse
35 // - dim == 0: inner-product with the m-wide coefficient row
36 //
37 // Parameters:
38 // ret [m] output vector (accumulated into, not cleared)
39 // dim current dimension index (call with n_dims-1)
40 // n_dims total number of dimensions N
41 // all_w [sum n_k] packed weight vectors [w_0 | w_1 | ... | w_{N-1}]
42 // w_offset [N+1] w_k starts at all_w[w_offset[k]], length w_offset[k+1]-w_offset[k]
43 // starts [N] start index per dimension (selects sub-tensor)
44 // strides [N] stride per dimension in the flat coefficient array
45 // strides[0] = m, strides[k+1] = strides[k] * n_k
46 // c [total] flat coefficient tensor
47 // m number of outputs (leading dimension)
48 // weight accumulated product of weights from outer dims (call with 1)
49 // offset accumulated flat offset from outer dims (call with 0)
50 //
51 // SYMBOL "tensor_ttv"
52 template<typename T1>
53 void casadi_tensor_ttv(T1* ret, casadi_int dim, casadi_int n_dims,
54  const T1* all_w, const casadi_int* w_offset,
55  const casadi_int* starts, const casadi_int* strides,
56  const T1* c, casadi_int m,
57  T1 weight, casadi_int offset) {
58  casadi_int i, j, n_w;
59  const T1* w;
60  // Number of weights and pointer for this dimension
61  n_w = w_offset[dim+1] - w_offset[dim];
62  w = all_w + w_offset[dim];
63  if (dim == 0) {
64  // Base case: innermost dimension - contract with coefficient row
65  const T1* coeff = c + offset + starts[0]*m;
66  for (i = 0; i < n_w; ++i) {
67  T1 ww = weight * w[i];
68  for (j = 0; j < m; ++j)
69  ret[j] += ww * coeff[i*m + j];
70  }
71  } else {
72  // Recursive case: peel off dimension `dim`, accumulate weight and offset
73  for (i = 0; i < n_w; ++i) {
74  casadi_tensor_ttv(ret, dim-1, n_dims, all_w, w_offset,
75  starts, strides, c, m,
76  weight * w[i],
77  offset + (starts[dim]+i)*strides[dim]);
78  }
79  }
80 }