mapsum.cpp
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 
26 #include "mapsum.hpp"
27 #include "serializing_stream.hpp"
28 
29 namespace casadi {
30 
31  Function MapSum::create(const std::string& name, const std::string& parallelization,
32  const Function& f, casadi_int n,
33  const std::vector<bool>& reduce_in,
34  const std::vector<bool>& reduce_out,
35  const Dict& opts) {
36  if (reduce_out.empty()) return create(name, parallelization, f, n,
37  reduce_in, std::vector<bool>(f.n_out(), false));
38  casadi_assert(reduce_in.size()==f.n_in(), "Dimension mismatch");
39  casadi_assert(reduce_out.size()==f.n_out(), "Dimension mismatch");
40 
41  if (parallelization == "serial") {
42  std::string suffix = str(reduce_in)+str(reduce_out);
43  Function ret;
44  if (!f->incache(name, ret, suffix)) {
45  // Create new serial map
46  ret = Function::create(new MapSum(name, f, n, reduce_in, reduce_out), opts);
47  casadi_assert_dev(ret.name()==name);
48  // Save in cache
49  f->tocache_if_missing(ret, suffix);
50  }
51  return ret.wrap_as_needed(opts);
52  } else {
53  casadi_error("Unknown parallelization: " + parallelization);
54  }
55  }
56 
57  MapSum::MapSum(const std::string& name, const Function& f, casadi_int n,
58  const std::vector<bool>& reduce_in,
59  const std::vector<bool>& reduce_out)
60  : FunctionInternal(name), f_(f), n_(n), reduce_in_(reduce_in), reduce_out_(reduce_out) {
61  casadi_assert_dev(reduce_in.size()==f.n_in());
62  casadi_assert_dev(reduce_out.size()==f.n_out());
63  }
64 
67  s.pack("MapSum::f", f_);
68  s.pack("MapSum::n", n_);
69  s.pack("MapSum::reduce_in", reduce_in_);
70  s.pack("MapSum::reduce_out", reduce_out_);
71  }
72 
75  s.pack("MapSum::class_name", class_name());
76  }
77 
79  s.unpack("MapSum::f", f_);
80  s.unpack("MapSum::n", n_);
81  s.unpack("MapSum::reduce_in", reduce_in_);
82  s.unpack("MapSum::reduce_out", reduce_out_);
83  }
84 
86  std::string class_name;
87  s.unpack("MapSum::class_name", class_name);
88  if (class_name=="MapSum") {
89  return new MapSum(s);
90  } else {
91  casadi_error("class name '" + class_name + "' unknown.");
92  }
93  }
94 
96  clear_mem();
97  }
98 
99  std::vector<std::string> MapSum::get_function() const {
100  return {"f"};
101  }
102 
103  const Function& MapSum::get_function(const std::string &name) const {
104  casadi_assert(has_function(name),
105  "No function \"" + name + "\" in " + name_ + ". " +
106  "Available functions: " + join(get_function()) + ".");
107  return f_;
108  }
109 
110  void MapSum::find(std::map<FunctionInternal*, std::pair<Function, size_t>> & all_fun,
111  casadi_int max_depth) const {
112  // Call to base class
113  FunctionInternal::find(all_fun, max_depth);
114  add_embedded(all_fun, f_, max_depth);
115  }
116 
117  bool MapSum::has_function(const std::string& fname) const {
118  return fname=="f";
119  }
120 
121  void MapSum::init(const Dict& opts) {
124 
125  // Call the initialization method of the base class
127 
128  // Allocate sufficient memory for serial evaluation
129  alloc_arg(f_.sz_arg());
130  alloc_res(f_.sz_res());
131  alloc_w(f_.sz_w(), true);
132  alloc_iw(f_.sz_iw());
133 
134  // Allocate scratch space for dummping result of reduced outputs
135  for (casadi_int j=0;j<n_out_;++j) {
136  if (reduce_out_[j]) alloc_w(f_.nnz_out(j), true);
137  }
138  }
139 
140  template<typename T1>
141  void casadi_add(casadi_int n, const T1* x, T1* y) {
142  casadi_int i;
143  if (!x || !y) return;
144  for (i=0; i<n; ++i) *y++ += *x++;
145  }
146 
147  template<>
148  void casadi_add(casadi_int n, const bvec_t* x, bvec_t* y) {
149  casadi_int i;
150  if (!x || !y) return;
151  for (i=0; i<n; ++i) *y++ |= *x++;
152  }
153 
154  template<typename T>
155  int MapSum::eval_gen(const T** arg, T** res, casadi_int* iw, T* w, int mem) const {
156  const T** arg1 = arg+n_in_;
157  std::copy_n(arg, n_in_, arg1);
158  T** res1 = res+n_out_;
159 
160  T* w_scratch = w + f_.sz_w();
161  for (casadi_int j=0;j<n_out_;++j) {
162  if (res[j] && reduce_out_[j]) {
163  casadi_clear(res[j], f_.nnz_out(j)); // clear sums
164  res1[j] = w_scratch; // Make the function dump result in scratch space
165  w_scratch += f_.nnz_out(j);
166  } else {
167  res1[j] = res[j];
168  }
169  }
170  for (casadi_int i=0; i<n_; ++i) {
171  if (f_(arg1, res1, iw, w, mem)) return 1;
172  for (casadi_int j=0; j<n_in_; ++j) {
173  if (arg1[j] && !reduce_in_[j]) arg1[j] += f_.nnz_in(j);
174  }
175  for (casadi_int j=0; j<n_out_; ++j) {
176  if (res1[j]) {
177  if (reduce_out_[j]) {
178  casadi_add(f_.nnz_out(j), res1[j], res[j]); // Perform sum
179  } else {
180  res1[j] += f_.nnz_out(j);
181  }
182  }
183  }
184  }
185  return 0;
186  }
187 
188  int MapSum::eval_sx(const SXElem** arg, SXElem** res,
189  casadi_int* iw, SXElem* w, void* mem,
190  bool always_inline, bool never_inline) const {
191  return eval_gen(arg, res, iw, w);
192  }
193 
194  int MapSum::sp_forward(const bvec_t** arg, bvec_t** res,
195  casadi_int* iw, bvec_t* w, void* mem) const {
196  return eval_gen(arg, res, iw, w);
197  }
198 
199  int MapSum::sp_reverse(bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
200  // Note: f_.rev(arg,res,iw,w)
201  // has a side effect of clearing res
202  // Reduced outputs should not be cleared;
203  // they must influence each iteration
204 
205  // Store reduced res in scratch space
206  bvec_t* w_scratch = w + f_.sz_w();
207  for (casadi_int j=0;j<n_out_;++j) {
208  if (res[j] && reduce_out_[j]) {
209  casadi_copy(res[j], f_.nnz_out(j), w_scratch);
210  w_scratch += f_.nnz_out(j);
211  }
212  }
213  bvec_t** arg1 = arg+n_in_;
214  std::copy_n(arg, n_in_, arg1);
215  bvec_t** res1 = res+n_out_;
216  std::copy_n(res, n_out_, res1);
217  for (casadi_int i=0; i<n_; ++i) {
218  // Restore res1[j] from scratch space
219  w_scratch = w + f_.sz_w();
220  for (casadi_int j=0;j<n_out_;++j) {
221  if (res[j] && reduce_out_[j]) {
222  casadi_copy(w_scratch, f_.nnz_out(j), res1[j]);
223  w_scratch += f_.nnz_out(j);
224  }
225  }
226  if (f_.rev(arg1, res1, iw, w)) return 1;
227  for (casadi_int j=0; j<n_in_; ++j) {
228  if (arg1[j] && !reduce_in_[j]) arg1[j] += f_.nnz_in(j);
229  }
230  for (casadi_int j=0; j<n_out_; ++j) {
231  if (res1[j] && !reduce_out_[j]) res1[j] += f_.nnz_out(j);
232  }
233  }
234  return 0;
235  }
236 
238  g.add_dependency(f_);
239  }
240 
243  g.local("i", "casadi_int");
244  g.local("arg1", "const casadi_real*", "*");
245  g.local("res1", "casadi_real*", "*");
246  g.local("w_scratch", "casadi_real*", "*");
247  // Input buffer
248  g << "arg1 = arg+" << n_in_ << ";\n"
249  << "for (i=0; i<" << n_in_ << "; ++i) arg1[i]=arg[i];\n";
250  // Output buffer
251  g << "res1 = res+" << n_out_ << ";\n";
252  g << "w_scratch = w+" << f_.sz_w() << ";\n";
253  for (casadi_int j=0;j<n_out_;++j) {
254  if (reduce_out_[j]) {
255  g << "if (res[" << j << "]) {\n";
256  g << "casadi_clear(res[" << j << "], " << f_.nnz_out(j) << ");\n";
257  g << "res1[" << j << "] = w_scratch;\n";
258  g << "w_scratch+=" << f_.nnz_out(j) << ";\n";
259  g << "} else {\n";
260  g << "res1[" << j << "] = res[" << j << "];\n";
261  g << "}\n";
262  } else {
263  g << "res1[" << j << "] = res[" << j << "];\n";
264  }
265  }
266 
267  g << "for (i=0; i<" << n_ << "; ++i) {\n";
268  // Evaluate
269  g << "if (" << g(f_, "arg1", "res1", "iw", "w") << ") return 1;\n";
270  // Update input buffers
271  for (casadi_int j=0; j<n_in_; ++j) {
272  if (!reduce_in_[j] && f_.nnz_in(j)) {
273  g << "if (arg1[" << j << "]) arg1[" << j << "]+=" << f_.nnz_in(j) << ";\n";
274  }
275  }
276  // Update output buffers
277  for (casadi_int j=0; j<n_out_; ++j) {
278  if (reduce_out_[j]) {
279  g << "if (res1[" << j << "]) ";
280  g << g.axpy(f_.nnz_out(j), "1.0", "res1[" + str(j) + "]", "res[" + str(j) + "]") << "\n";
281  } else {
282  if (f_.nnz_out(j)) {
283  g << "if (res1[" << j << "]) ";
284  g << "res1[" << j << "]+=" << f_.nnz_out(j) << ";\n";
285  }
286  }
287  }
288  g << "}\n";
289  }
290 
292  ::get_forward(casadi_int nfwd, const std::string& name,
293  const std::vector<std::string>& inames,
294  const std::vector<std::string>& onames,
295  const Dict& opts) const {
296  // Generate map of derivative
297  Function df = f_.forward(nfwd);
298 
299  for (casadi_int i=0;i<n_out_;++i) {
300  if (reduce_out_[i]) casadi_assert(df.nnz_in(n_in_+i)==0, "Case not implemented");
301  }
302 
303  std::vector<bool> reduce_in = join(reduce_in_, reduce_out_, reduce_in_);
304  Function dm = MapSum::create("mapsum" + str(n_) + "_" + df.name(), parallelization(),
305  df, n_, reduce_in, reduce_out_);
306 
307  // Input expressions
308  std::vector<MX> arg = dm.mx_in();
309 
310  // Need to reorder sensitivity inputs
311  std::vector<MX> res = arg;
312  std::vector<MX>::iterator it=res.begin()+n_in_+n_out_;
313  std::vector<casadi_int> ind;
314  for (casadi_int i=0; i<n_in_; ++i, ++it) {
315  if (reduce_in_[i]) continue;
316  casadi_int sz = f_.size2_in(i);
317  ind.clear();
318  for (casadi_int k=0; k<n_; ++k) {
319  for (casadi_int d=0; d<nfwd; ++d) {
320  for (casadi_int j=0; j<sz; ++j) {
321  ind.push_back((d*n_ + k)*sz + j);
322  }
323  }
324  }
325  *it = (*it)(Slice(), ind); // NOLINT
326  }
327 
328  // Get output expressions
329  res = dm(res);
330 
331  // Reorder sensitivity outputs
332  it = res.begin();
333  for (casadi_int i=0; i<n_out_; ++i, ++it) {
334  if (reduce_out_[i]) continue;
335  casadi_int sz = f_.size2_out(i);
336  ind.clear();
337  for (casadi_int d=0; d<nfwd; ++d) {
338  for (casadi_int k=0; k<n_; ++k) {
339  for (casadi_int j=0; j<sz; ++j) {
340  ind.push_back((k*nfwd + d)*sz + j);
341  }
342  }
343  }
344  *it = (*it)(Slice(), ind); // NOLINT
345  }
346 
347  // Construct return function
348  Dict custom_opts = opts;
349  custom_opts["always_inline"] = true;
350  custom_opts["allow_duplicate_io_names"] = true;
351  return Function(name, arg, res, inames, onames, custom_opts);
352  }
353 
355  ::get_reverse(casadi_int nadj, const std::string& name,
356  const std::vector<std::string>& inames,
357  const std::vector<std::string>& onames,
358  const Dict& opts) const {
359  // Generate map of derivative
360  Function df = f_.reverse(nadj);
361 
362  for (casadi_int i=0;i<n_out_;++i) {
363  if (reduce_out_[i]) casadi_assert(df.nnz_in(n_in_+i)==0, "Case not implemented");
364  }
365 
366  std::vector<bool> reduce_in = join(reduce_in_, reduce_out_, reduce_out_);
367  Function dm = MapSum::create("mapsum" + str(n_) + "_" + df.name(), parallelization(),
368  df, n_, reduce_in, reduce_in_);
369 
370  // Input expressions
371  std::vector<MX> arg = dm.mx_in();
372 
373  // Need to reorder sensitivity inputs
374  std::vector<MX> res = arg;
375  std::vector<MX>::iterator it=res.begin()+n_in_+n_out_;
376  std::vector<casadi_int> ind;
377  for (casadi_int i=0; i<n_out_; ++i, ++it) {
378  if (reduce_out_[i]) continue;
379  casadi_int sz = f_.size2_out(i);
380  ind.clear();
381  for (casadi_int k=0; k<n_; ++k) {
382  for (casadi_int d=0; d<nadj; ++d) {
383  for (casadi_int j=0; j<sz; ++j) {
384  ind.push_back((d*n_ + k)*sz + j);
385  }
386  }
387  }
388  *it = (*it)(Slice(), ind); // NOLINT
389  }
390 
391  // Get output expressions
392  res = dm(res);
393 
394  // Reorder sensitivity outputs
395  it = res.begin();
396  for (casadi_int i=0; i<n_in_; ++i, ++it) {
397  if (reduce_in_[i]) continue;
398  casadi_int sz = f_.size2_in(i);
399  ind.clear();
400  for (casadi_int d=0; d<nadj; ++d) {
401  for (casadi_int k=0; k<n_; ++k) {
402  for (casadi_int j=0; j<sz; ++j) {
403  ind.push_back((k*nadj + d)*sz + j);
404  }
405  }
406  }
407  *it = (*it)(Slice(), ind); // NOLINT
408  }
409 
410  // Construct return function
411  Dict custom_opts = opts;
412  custom_opts["always_inline"] = true;
413  custom_opts["allow_duplicate_io_names"] = true;
414  return Function(name, arg, res, inames, onames, custom_opts);
415  }
416 
417  int MapSum::eval(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
418  // This checkout/release dance is an optimization.
419  // Could also use the thread-safe variant f_(arg1, res1, iw, w)
420  // in Map::eval_gen
421  setup(mem, arg, res, iw, w);
423  return eval_gen(arg, res, iw, w, m);
424  }
425 
426 } // namespace casadi
Helper class for C code generation.
std::string axpy(casadi_int n, const std::string &a, const std::string &x, const std::string &y)
Codegen axpy: y += a*x.
std::string add_dependency(const Function &f)
Add a function dependency.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
Internal class for Function.
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void init(const Dict &opts) override
Initialize.
void tocache_if_missing(Function &f, const std::string &suffix="") const
Save function to cache, only if missing.
std::vector< bool > is_diff_out_
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
void add_embedded(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, const Function &dep, casadi_int max_depth) const
virtual void find(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, casadi_int max_depth) const
bool incache(const std::string &fname, Function &f, const std::string &suffix="") const
Get function in cache.
size_t n_in_
Number of inputs and outputs.
void serialize_type(SerializingStream &s) const override
Serialize type information.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
std::vector< bool > is_diff_in_
Are inputs and outputs differentiable?
Function object.
Definition: function.hpp:60
Function forward(casadi_int nfwd) const
Get a function that calculates nfwd forward derivatives.
Definition: function.cpp:1324
casadi_int nnz_out() const
Get number of output nonzeros.
Definition: function.cpp:1007
size_t sz_res() const
Get required length of res field.
Definition: function.cpp:1237
const MX mx_in(casadi_int ind) const
Get symbolic primitives equivalent to the input expressions.
Definition: function.cpp:1781
const std::string & name() const
Name of the function.
Definition: function.cpp:1504
Function reverse(casadi_int nadj) const
Get a function that calculates nadj adjoint derivatives.
Definition: function.cpp:1332
static Function create(FunctionInternal *node)
Create from node.
Definition: function.cpp:488
bool is_diff_out(casadi_int ind) const
Get differentiability of inputs/output.
Definition: function.cpp:1207
int rev(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, int mem=0) const
Propagate sparsity backward.
Definition: function.cpp:1252
size_t sz_iw() const
Get required length of iw field.
Definition: function.cpp:1239
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:975
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:971
bool is_diff_in(casadi_int ind) const
Get differentiability of inputs/output.
Definition: function.cpp:1199
size_t sz_w() const
Get required length of w field.
Definition: function.cpp:1241
size_t sz_arg() const
Get required length of arg field.
Definition: function.cpp:1235
Function wrap_as_needed(const Dict &opts) const
Wrap in a Function with options.
Definition: function.cpp:2120
casadi_int nnz_in() const
Get number of input nonzeros.
Definition: function.cpp:1003
MapSum(DeserializingStream &s)
Deserializing constructor.
Definition: mapsum.cpp:78
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: mapsum.cpp:65
int eval_gen(const T **arg, T **res, casadi_int *iw, T *w, int mem=0) const
Evaluate or propagate sparsities.
Definition: mapsum.cpp:155
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity backwards.
Definition: mapsum.cpp:199
~MapSum() override
Destructor.
Definition: mapsum.cpp:95
void serialize_type(SerializingStream &s) const override
Serialize type information.
Definition: mapsum.cpp:73
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity forward.
Definition: mapsum.cpp:194
Function f_
Definition: mapsum.hpp:225
std::string class_name() const override
Get type name.
Definition: mapsum.hpp:66
static Function create(const std::string &name, const std::string &parallelization, const Function &f, casadi_int n, const std::vector< bool > &reduce_in, const std::vector< bool > &reduce_out, const Dict &opts=Dict())
Definition: mapsum.cpp:31
void find(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, casadi_int max_depth) const override
Definition: mapsum.cpp:110
virtual std::string parallelization() const
Type of parallellization.
Definition: mapsum.hpp:111
casadi_int n_
Definition: mapsum.hpp:228
void init(const Dict &opts) override
Initialize.
Definition: mapsum.cpp:121
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Definition: mapsum.cpp:85
std::vector< bool > reduce_in_
Definition: mapsum.hpp:231
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
Definition: mapsum.cpp:237
virtual std::vector< std::string > get_function() const override
Definition: mapsum.cpp:99
std::vector< bool > reduce_out_
Definition: mapsum.hpp:234
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate the function numerically.
Definition: mapsum.cpp:417
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w, void *mem, bool always_inline, bool never_inline) const override
evaluate symbolically while also propagating directional derivatives
Definition: mapsum.cpp:188
void codegen_body(CodeGenerator &g) const override
Generate code for the body of the C function.
Definition: mapsum.cpp:241
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nadj adjoint derivatives.
Definition: mapsum.cpp:355
bool has_function(const std::string &fname) const override
Definition: mapsum.cpp:117
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nfwd forward derivatives.
Definition: mapsum.cpp:292
Base class for FunctionInternal and LinsolInternal.
void clear_mem()
Clear all memory (called from destructor)
The basic scalar symbolic class of CasADi.
Definition: sx_elem.hpp:75
Helper class for Serialization.
void pack(const Sparsity &e)
Serializes an object to the output stream.
Class representing a Slice.
Definition: slice.hpp:48
The casadi namespace.
Definition: archiver.cpp:28
std::string join(const std::vector< std::string > &l, const std::string &delim)
unsigned long long bvec_t
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
std::string str(const T &v)
String representation, any type.
void casadi_add(casadi_int n, const T1 *x, T1 *y)
Definition: mapsum.cpp:141
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.