shared.hpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include "casadi_runtime.hpp"
26 #include "../casadi_misc.hpp"
27 #include <vector>
28 #include <map>
29 
30 #ifndef CASADI_CASADI_SHARED_HPP
31 #define CASADI_CASADI_SHARED_HPP
32 
33 namespace casadi {
34 template<typename T>
35 casadi_int einstein_process(const T& A, const T& B, const T& C,
36  const std::vector<casadi_int>& dim_a, const std::vector<casadi_int>& dim_b, const std::vector<casadi_int>& dim_c,
37  const std::vector<casadi_int>& a, const std::vector<casadi_int>& b, const std::vector<casadi_int>& c,
38  std::vector<casadi_int>& iter_dims,
39  std::vector<casadi_int>& strides_a, std::vector<casadi_int>& strides_b, std::vector<casadi_int>& strides_c
40 ) {
41 
42  casadi_assert_dev(A.is_vector() && A.is_dense());
43  casadi_assert_dev(B.is_vector() && B.is_dense());
44  casadi_assert_dev(C.is_vector() && C.is_dense());
45 
46  // Dimension check
47  casadi_assert_dev(A.numel()==product(dim_a));
48  casadi_assert_dev(B.numel()==product(dim_b));
49  casadi_assert_dev(C.numel()==product(dim_c));
50 
51  casadi_assert_dev(dim_a.size()==a.size());
52  casadi_assert_dev(dim_b.size()==b.size());
53 
54  casadi_assert_dev(c.size()<=a.size()+b.size());
55 
56  std::map<casadi_int, casadi_int> dim_map;
57 
58  // Check if shared nodes dimensions match up
59  for (casadi_int i=0;i<a.size();++i) {
60  casadi_int ai = a[i];
61  if (ai>=0) continue;
62  auto al = dim_map.find(ai);
63  if (al==dim_map.end()) {
64  dim_map[ai] = dim_a[i];
65  } else {
66  casadi_assert_dev(al->second==dim_a[i]);
67  }
68  }
69 
70  for (casadi_int i=0;i<b.size();++i) {
71  casadi_int bi = b[i];
72  if (bi>=0) continue;
73  auto bl = dim_map.find(bi);
74  if (bl==dim_map.end()) {
75  dim_map[bi] = dim_b[i];
76  } else {
77  casadi_assert_dev(bl->second==dim_b[i]);
78  }
79  }
80 
81  for (casadi_int i=0;i<c.size();++i) {
82  casadi_int ci = c[i];
83  if (ci>=0) continue;
84  auto cl = dim_map.find(ci);
85  if (cl==dim_map.end()) {
86  dim_map[ci] = dim_c[i];
87  } else {
88  casadi_assert_dev(cl->second==dim_c[i]);
89  }
90  }
91 
92  std::vector< std::pair<casadi_int, casadi_int> > dim_map_pair;
93  for (const auto & i : dim_map) dim_map_pair.push_back(i);
94 
95  std::sort(dim_map_pair.begin(), dim_map_pair.end(),
96  [](const std::pair<casadi_int, casadi_int>& a, const std::pair<casadi_int, casadi_int>& b) { return a.second < b.second;});
97 
98  std::vector<casadi_int> dim_map_keys;
99  // Compute the total number of iterations needed
100  casadi_int n_iter = 1;
101  for (const auto& e : dim_map_pair) {
102  n_iter*= e.second;
103  dim_map_keys.push_back(-e.first);
104  iter_dims.push_back(e.second);
105  }
106 
107  strides_a.clear();
108  strides_a.resize(iter_dims.size()+1);
109  strides_b.clear();
110  strides_b.resize(iter_dims.size()+1);
111  strides_c.clear();
112  strides_c.resize(iter_dims.size()+1);
113 
114  std::vector<casadi_int> lu;
115 
116  if (!dim_map_keys.empty()) lu = lookupvector(dim_map_keys);
117 
118  // Update data pointers to match indices
119  casadi_int cumprod = 1;
120  for (casadi_int j=0;j<a.size();++j) {
121  if (a[j]<0) {
122  strides_a[1+lu[-a[j]]] = cumprod;
123  } else {
124  strides_a[0]+=a[j]*cumprod;
125  }
126  cumprod*= dim_a[j];
127  }
128  cumprod = 1;
129  for (casadi_int j=0;j<b.size();++j) {
130  if (b[j]<0) {
131  strides_b[1+lu[-b[j]]] = cumprod;
132  } else {
133  strides_b[0]+=b[j]*cumprod;
134  }
135  cumprod*= dim_b[j];
136  }
137  cumprod = 1;
138  for (casadi_int j=0;j<c.size();++j) {
139  if (c[j]<0) {
140  strides_c[1+lu[-c[j]]] = cumprod;
141  } else {
142  strides_c[0]+=c[j]*cumprod;
143  }
144  cumprod*= dim_c[j];
145  }
146 
147  return n_iter;
148  }
149 
150  template<typename T>
151  void Contraction(const T&a, const T& b, T& r) {
152  r+= a*b;
153  }
154 
155  template<> inline
156  void Contraction(const bvec_t&a, const bvec_t& b, bvec_t& r) {
157  r|= a | b;
158  }
159 
160  template<typename T>
161  void einstein_eval(casadi_int n_iter,
162  const std::vector<casadi_int>& iter_dims,
163  const std::vector<casadi_int>& strides_a, const std::vector<casadi_int>& strides_b, const std::vector<casadi_int>& strides_c,
164  const T* a_in, const T* b_in, T* c_in) {
165 
166  if (!n_iter) return;
167 
168  casadi_int iter_dim1 = 1, iter_dim2 = 1, iter_dim3 = 1;
169 
170  casadi_int n = iter_dims.size();
171 
172  casadi_int stridea1=0, strideb1=0, stridec1=0;
173  casadi_int stridea2=0, strideb2=0, stridec2=0;
174  casadi_int stridea3=0, strideb3=0, stridec3=0;
175  if (n>0) {
176  iter_dim3 = iter_dims[n-1];
177  stridea3 = strides_a[n];
178  strideb3 = strides_b[n];
179  stridec3 = strides_c[n];
180  }
181  if (n>1) {
182  iter_dim2 = iter_dims[n-2];
183  stridea2 = strides_a[n-1];
184  strideb2 = strides_b[n-1];
185  stridec2 = strides_c[n-1];
186  }
187  if (n>2) {
188  iter_dim1 = iter_dims[n-3];
189  stridea1 = strides_a[n-2];
190  strideb1 = strides_b[n-2];
191  stridec1 = strides_c[n-2];
192  }
193 
194 
195  const casadi_int* ptr_iter_dims = get_ptr(iter_dims);
196 
197  const casadi_int *ptr_strides_a = get_ptr(strides_a)+1;
198  const casadi_int *ptr_strides_b = get_ptr(strides_b)+1;
199  const casadi_int *ptr_strides_c = get_ptr(strides_c)+1;
200 
201  // Data pointers
202  const T* a_perm = a_in+strides_a[0];
203  const T* b_perm = b_in+strides_b[0];
204  T* c_perm = c_in+strides_c[0];
205 
206  n_iter/= iter_dim1*iter_dim2*iter_dim3;
207 
208  // Main loop
209  for (casadi_int i=0;i<n_iter;++i) {
210 
211  // Data pointers
212  const T* a = a_perm;
213  const T* b = b_perm;
214  T* c = c_perm;
215 
216  // Construct indices
217  casadi_int sub = i;
218  for (casadi_int j=0;j<n-3;++j) {
219  casadi_int ind = sub % ptr_iter_dims[j];
220  a+= ptr_strides_a[j]*ind;
221  b+= ptr_strides_b[j]*ind;
222  c+= ptr_strides_c[j]*ind;
223  sub/= ptr_iter_dims[j];
224  }
225 
226  const T* a1 = a;
227  const T* b1 = b;
228  T* c1 = c;
229  for (casadi_int i1=0;i1<iter_dim1;++i1) {
230  const T* a2 = a1;
231  const T* b2 = b1;
232  T* c2 = c1;
233  for (casadi_int i2=0;i2<iter_dim2;++i2) {
234  const T* a3 = a2;
235  const T* b3 = b2;
236  T* c3 = c2;
237  for (casadi_int i3=0;i3<iter_dim3;++i3) {
238  // Perform the actual multiplication
239  Contraction<T>(*a3, *b3, *c3);
240 
241  a3+= stridea3;
242  b3+= strideb3;
243  c3+= stridec3;
244  }
245  a2+= stridea2;
246  b2+= strideb2;
247  c2+= stridec2;
248  }
249  a1+= stridea1;
250  b1+= strideb1;
251  c1+= stridec1;
252  }
253 
254 
255  }
256 
257  }
258 
259 }// namespace casadi
260 
261 #endif // CASADI_CASADI_SHARED_HPP
The casadi namespace.
void einstein_eval(casadi_int n_iter, const std::vector< casadi_int > &iter_dims, const std::vector< casadi_int > &strides_a, const std::vector< casadi_int > &strides_b, const std::vector< casadi_int > &strides_c, const T *a_in, const T *b_in, T *c_in)
Definition: shared.hpp:161
casadi_int einstein_process(const T &A, const T &B, const T &C, const std::vector< casadi_int > &dim_a, const std::vector< casadi_int > &dim_b, const std::vector< casadi_int > &dim_c, const std::vector< casadi_int > &a, const std::vector< casadi_int > &b, const std::vector< casadi_int > &c, std::vector< casadi_int > &iter_dims, std::vector< casadi_int > &strides_a, std::vector< casadi_int > &strides_b, std::vector< casadi_int > &strides_c)
Definition: shared.hpp:35
void Contraction(const T &a, const T &b, T &r)
Definition: shared.hpp:151
CASADI_EXPORT std::vector< casadi_int > lookupvector(const std::vector< casadi_int > &v, casadi_int size)
Returns a vector for quickly looking up entries of supplied list.