setnonzeros_impl.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 
26 #ifndef CASADI_SETNONZEROS_IMPL_HPP
27 #define CASADI_SETNONZEROS_IMPL_HPP
28 
29 #include "setnonzeros.hpp"
30 #include "casadi_misc.hpp"
31 #include "serializing_stream.hpp"
32 
34 
35 namespace casadi {
36 
37  template<bool Add>
38  MX SetNonzeros<Add>::create(const MX& y, const MX& x, const std::vector<casadi_int>& nz) {
39  if (is_slice(nz)) return create(y, x, to_slice(nz));
40  if (is_slice2(nz)) {
41  std::pair<Slice, Slice> sl = to_slice2(nz);
42  return create(y, x, sl.first, sl.second);
43  }
44  return MX::create(new SetNonzerosVector<Add>(y, x, nz));
45  }
46 
47  template<bool Add>
48  MX SetNonzeros<Add>::create(const MX& y, const MX& x, const Slice& s) {
49  // Simplify if assignment
50  if (y.sparsity()==x.sparsity() && s.start==0 && s.step==1 && s.stop==x.nnz()) {
51  if (Add) {
52  return y + x;
53  } else {
54  return x;
55  }
56  }
57  return MX::create(new SetNonzerosSlice<Add>(y, x, s));
58  }
59 
60  template<bool Add>
61  MX SetNonzeros<Add>::create(const MX& y, const MX& x, const Slice& inner, const Slice& outer) {
62  return MX::create(new SetNonzerosSlice2<Add>(y, x, inner, outer));
63  }
64 
65  template<bool Add>
66  SetNonzeros<Add>::SetNonzeros(const MX& y, const MX& x) {
67  this->set_sparsity(y.sparsity());
68  this->set_dep(y, x);
69  }
70 
71  template<bool Add>
72  SetNonzerosVector<Add>::SetNonzerosVector(const MX& y, const MX& x,
73  const std::vector<casadi_int>& nz) : SetNonzeros<Add>(y, x), nz_(nz) {
74  // Ignore duplicate assignments
75  if (!Add) {
76  std::vector<bool> already_set(this->nnz(), false);
77  for (std::vector<casadi_int>::reverse_iterator i=nz_.rbegin(); i!=nz_.rend(); ++i) {
78  if (*i>=0) {
79  if (already_set[*i]) {
80  *i = -1;
81  } else {
82  already_set[*i] = true;
83  }
84  }
85  }
86  }
87  }
88 
89  template<bool Add>
90  SetNonzeros<Add>:: ~SetNonzeros() {
91  }
92 
93  template<bool Add>
94  void SetNonzerosVector<Add>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
95  if (!MXNode::matches_sparsity(arg)) {
96  SetNonzeros<Add>::eval_mx(arg, res);
97  return;
98  }
99  res[0] = SetNonzeros<Add>::create(arg[0], arg[1], nz_);
100  }
101 
102  template<bool Add>
103  void SetNonzerosSlice<Add>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
104  if (!MXNode::matches_sparsity(arg)) {
105  SetNonzeros<Add>::eval_mx(arg, res);
106  return;
107  }
108  res[0] = SetNonzeros<Add>::create(arg[0], arg[1], s_);
109  }
110 
111  template<bool Add>
112  void SetNonzerosSlice2<Add>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
113  if (!MXNode::matches_sparsity(arg)) {
114  SetNonzeros<Add>::eval_mx(arg, res);
115  return;
116  }
117  res[0] = SetNonzeros<Add>::create(arg[0], arg[1], inner_, outer_);
118  }
119 
120  template<bool Add>
121  void SetNonzeros<Add>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
122  // Get all the nonzeros
123  std::vector<casadi_int> nz = all();
124 
125  // Output sparsity
126  const Sparsity &osp = sparsity();
127  const casadi_int* orow = osp.row();
128  std::vector<casadi_int> ocol = osp.get_col();
129 
130  // Input sparsity (first input same as output)
131  const Sparsity &isp = dep(1).sparsity();
132  std::vector<casadi_int> icol = isp.get_col();
133 
134  // We next need to resort the assignment vector by outputs instead of inputs
135  // Start by counting the number of output nonzeros corresponding to each input nonzero
136  std::vector<casadi_int> onz_count(osp.nnz()+2, 0);
137  for (std::vector<casadi_int>::const_iterator it=nz.begin(); it!=nz.end(); ++it) {
138  onz_count[*it+2]++;
139  }
140 
141  // Cumsum to get index offset for output nonzero
142  for (casadi_int i=0; i<onz_count.size()-1; ++i) {
143  onz_count[i+1] += onz_count[i];
144  }
145 
146  // Get the order of assignments
147  std::vector<casadi_int> nz_order(nz.size());
148  for (casadi_int k=0; k<nz.size(); ++k) {
149  // Save the new index
150  nz_order[onz_count[1+nz[k]]++] = k;
151  }
152 
153  // Find out which elements are being set
154  std::vector<casadi_int>& with_duplicates = onz_count; // Reuse memory
155  onz_count.resize(nz.size());
156  for (casadi_int k=0; k<nz.size(); ++k) {
157  // Get output nonzero
158  casadi_int onz_k = nz[nz_order[k]];
159 
160  // Get element (note: may contain duplicates)
161  if (onz_k>=0) {
162  with_duplicates[k] = ocol[onz_k]*osp.size1() + orow[onz_k];
163  } else {
164  with_duplicates[k] = -1;
165  }
166  }
167 
168  // Get all output elements (this time without duplicates)
169  std::vector<casadi_int> el_output;
170  osp.find(el_output);
171 
172  // Sparsity pattern being formed and corresponding nonzero mapping
173  std::vector<casadi_int> r_colind, r_row, r_nz, r_ind;
174 
175  // Get references to arguments and results
176  res[0] = arg[0];
177 
178  // Entries in res with elements zero'ed out
179  if (!Add) {
180 
181  // Get the nz locations in res corresponding to the output sparsity pattern
182  r_nz.resize(with_duplicates.size());
183  std::copy(with_duplicates.begin(), with_duplicates.end(), r_nz.begin());
184  res[0].sparsity().get_nz(r_nz);
185 
186  // Zero out the corresponding entries
187  res[0] = MX::zeros(isp)->get_nzassign(res[0], r_nz);
188  }
189 
190  // Get the nz locations of the elements in arg corresponding to the argument sparsity pattern
191  arg[1].sparsity().find(r_nz);
192  isp.get_nz(r_nz);
193 
194  // Filter out ignored entries and check if there is anything to add at all
195  bool elements_to_add = false;
196  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
197  if (*k>=0) {
198  if (nz[*k]>=0) {
199  elements_to_add = true;
200  } else {
201  *k = -1;
202  }
203  }
204  }
205 
206  // Quick continue of no elements to set/add
207  if (!elements_to_add) return;
208 
209  // Get the nz locations in the argument corresponding to the inputs
210  r_ind.resize(el_output.size());
211  std::copy(el_output.begin(), el_output.end(), r_ind.begin());
212  res[0].sparsity().get_nz(r_ind);
213 
214  // Enlarge the sparsity pattern of the arguments if not all assignments fit
215  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
216  if (*k>=0 && nz[*k]>=0 && r_ind[nz[*k]]<0) {
217 
218  // Create a new pattern which includes both the the previous seed
219  // and the addition/assignment
220  Sparsity sp = res[0].sparsity().unite(osp);
221  res[0] = res[0]->get_project(sp);
222 
223  // Recalculate the nz locations in the arguments corresponding to the inputs
224  std::copy(el_output.begin(), el_output.end(), r_ind.begin());
225  res[0].sparsity().get_nz(r_ind);
226 
227  break;
228  }
229  }
230 
231  // Have r_nz point to locations in the result instead of the output
232  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
233  if (*k>=0) {
234  *k = r_ind[nz[*k]];
235  }
236  }
237 
238  // Add to the element to the sensitivity, if any
239  res[0] = arg[1]->get_nzadd(res[0], r_nz);
240  }
241 
242  template<bool Add>
243  void SetNonzeros<Add>::ad_forward(const std::vector<std::vector<MX> >& fseed,
244  std::vector<std::vector<MX> >& fsens) const {
245  // Get all the nonzeros
246  std::vector<casadi_int> nz = all();
247 
248  // Number of derivative directions
249  casadi_int nfwd = fsens.size();
250 
251  // Output sparsity
252  const Sparsity &osp = sparsity();
253  const casadi_int* orow = osp.row();
254  std::vector<casadi_int> ocol;
255 
256  // Input sparsity (first input same as output)
257  const Sparsity &isp = dep(1).sparsity();
258  std::vector<casadi_int> icol;
259 
260  bool first_run = true;
261 
262  std::vector<casadi_int> onz_count;
263 
264  std::vector<casadi_int> nz_order;
265 
266  // Find out which elements are being set
267  std::vector<casadi_int>& with_duplicates = onz_count;
268 
269  // Get all output elements (this time without duplicates)
270  std::vector<casadi_int> el_output;
271 
272  // Sparsity pattern being formed and corresponding nonzero mapping
273  std::vector<casadi_int> r_colind, r_row, r_nz, r_ind;
274 
275  // Nondifferentiated function and forward sensitivities
276  for (casadi_int d=0; d<nfwd; ++d) {
277 
278  // Get references to arguments and results
279  const MX& arg = fseed[d][1];
280  const MX& arg0 = fseed[d][0];
281 
282  MX& res = fsens[d][0];
283  res = arg0;
284 
285  if (isp==arg.sparsity() && arg0.sparsity()==osp) {
286  /*
287  dep(0) <-> y
288  dep(1) <-> x
289 
290  y[nz]+=x
291 
292  dot(y)[nz]+=dot(x)
293 
294  dot(x)->get_nzadd(dot(y), nz)
295 
296  */
297  if (!Add) {
298  // Zero out the corresponding entries
299  res = MX::zeros(isp)->get_nzassign(res, nz);
300  }
301 
302  res = arg->get_nzadd(res, nz);
303  } else {
304  if (first_run) {
305  osp.find(el_output);
306  ocol = osp.get_col();
307  icol = isp.get_col();
308 
309  // We next need to resort the assignment vector by outputs instead of inputs
310  // Start by counting the number of output nonzeros corresponding to each input nonzero
311  onz_count.resize(osp.nnz()+2, 0);
312  for (std::vector<casadi_int>::const_iterator it=nz.begin(); it!=nz.end(); ++it) {
313  onz_count[*it+2]++;
314  }
315 
316  // Cumsum to get index offset for output nonzero
317  for (casadi_int i=0; i<onz_count.size()-1; ++i) {
318  onz_count[i+1] += onz_count[i];
319  }
320 
321  // Get the order of assignments
322  nz_order.resize(nz.size());
323  for (casadi_int k=0; k<nz.size(); ++k) {
324  // Save the new index
325  nz_order[onz_count[1+nz[k]]++] = k;
326  }
327 
328  onz_count.resize(nz.size());
329  for (casadi_int k=0; k<nz.size(); ++k) {
330  // Get output nonzero
331  casadi_int onz_k = nz[nz_order[k]];
332 
333  // Get element (note: may contain duplicates)
334  if (onz_k>=0) {
335  with_duplicates[k] = ocol[onz_k]*osp.size1() + orow[onz_k];
336  } else {
337  with_duplicates[k] = -1;
338  }
339  }
340 
341 
342  first_run = false;
343  }
344 
345  // Entries in res with elements zero'ed out
346  if (!Add) {
347 
348  // Get the nz locations in res corresponding to the output sparsity pattern
349  r_nz.resize(with_duplicates.size());
350  std::copy(with_duplicates.begin(), with_duplicates.end(), r_nz.begin());
351  res.sparsity().get_nz(r_nz);
352 
353  // Zero out the corresponding entries
354  res = MX::zeros(isp)->get_nzassign(res, r_nz);
355  }
356 
357  // Get the nz locations of the elements in arg corresponding to the argument
358  // sparsity pattern
359  arg.sparsity().find(r_nz);
360  isp.get_nz(r_nz);
361 
362  // Filter out ignored entries and check if there is anything to add at all
363  bool elements_to_add = false;
364  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
365  if (*k>=0) {
366  if (nz[*k]>=0) {
367  elements_to_add = true;
368  } else {
369  *k = -1;
370  }
371  }
372  }
373 
374  // Quick continue of no elements to set/add
375  if (!elements_to_add) continue;
376 
377  // Get the nz locations in the argument corresponding to the inputs
378  r_ind.resize(el_output.size());
379  std::copy(el_output.begin(), el_output.end(), r_ind.begin());
380  res.sparsity().get_nz(r_ind);
381 
382  // Enlarge the sparsity pattern of the arguments if not all assignments fit
383  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
384  if (*k>=0 && nz[*k]>=0 && r_ind[nz[*k]]<0) {
385 
386  // Create a new pattern which includes both the the previous seed
387  // and the addition/assignment
388  Sparsity sp = res.sparsity().unite(osp);
389  res = res->get_project(sp);
390 
391  // Recalculate the nz locations in the arguments corresponding to the inputs
392  std::copy(el_output.begin(), el_output.end(), r_ind.begin());
393  res.sparsity().get_nz(r_ind);
394 
395  break;
396  }
397  }
398 
399  // Have r_nz point to locations in the result instead of the output
400  for (std::vector<casadi_int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) {
401  if (*k>=0) {
402  *k = r_ind[nz[*k]];
403  }
404  }
405 
406  // Add to the element to the sensitivity, if any
407  res = arg->get_nzadd(res, r_nz);
408  }
409  }
410  }
411 
412  template<bool Add>
413  void SetNonzeros<Add>::ad_reverse(const std::vector<std::vector<MX> >& aseed,
414  std::vector<std::vector<MX> >& asens) const {
415  // Get all the nonzeros
416  std::vector<casadi_int> nz = all();
417 
418  // Number of derivative directions
419  casadi_int nadj = aseed.size();
420 
421  // Output sparsity
422  const Sparsity &osp = sparsity();
423  const casadi_int* orow = osp.row();
424  std::vector<casadi_int> ocol;
425 
426  // Input sparsity (first input same as output)
427  const Sparsity &isp = dep(1).sparsity();
428  const casadi_int* irow = isp.row();
429  std::vector<casadi_int> icol;
430 
431  std::vector<casadi_int> onz_count;
432 
433  // Get the order of assignments
434  std::vector<casadi_int> nz_order;
435 
436  std::vector<casadi_int>& with_duplicates = onz_count; // Reuse memory
437 
438  // Get all output elements (this time without duplicates)
439  std::vector<casadi_int> el_output;
440 
441  bool first_run = true;
442 
443  // Sparsity pattern being formed and corresponding nonzero mapping
444  std::vector<casadi_int> r_colind, r_row, r_nz, r_ind;
445 
446  for (casadi_int d=0; d<nadj; ++d) {
447  if (osp==aseed[d][0].sparsity()) {
448  /*
449  dep(0) <-> y
450  dep(1) <-> x
451 
452  z: y[nz]+=x
453 
454  bar(x) += bar(z)[nz]
455  bar(y) += bar(z)
456  */
457  asens[d][1] += aseed[d][0]->get_nzref(isp, nz);
458  if (!Add) {
459  asens[d][0] += MX::zeros(isp)->get_nzassign(aseed[d][0], nz);
460  } else {
461  asens[d][0] += aseed[d][0];
462  }
463  } else {
464  if (first_run) {
465  ocol = osp.get_col();
466  icol = isp.get_col();
467  // We next need to resort the assignment vector by outputs instead of inputs
468  // Start by counting the number of output nonzeros corresponding to each input nonzero
469  onz_count.resize(osp.nnz()+2, 0);
470  for (std::vector<casadi_int>::const_iterator it=nz.begin(); it!=nz.end(); ++it) {
471  onz_count[*it+2]++;
472  }
473 
474  // Cumsum to get index offset for output nonzero
475  for (casadi_int i=0; i<onz_count.size()-1; ++i) {
476  onz_count[i+1] += onz_count[i];
477  }
478 
479  // Get the order of assignments
480  nz_order.resize(nz.size());
481  for (casadi_int k=0; k<nz.size(); ++k) {
482  // Save the new index
483  nz_order[onz_count[1+nz[k]]++] = k;
484  }
485 
486  // Find out which elements are being set
487  onz_count.resize(nz.size());
488  for (casadi_int k=0; k<nz.size(); ++k) {
489  // Get output nonzero
490  casadi_int onz_k = nz[nz_order[k]];
491 
492  // Get element (note: may contain duplicates)
493  if (onz_k>=0) {
494  with_duplicates[k] = ocol[onz_k]*osp.size1() + orow[onz_k];
495  } else {
496  with_duplicates[k] = -1;
497  }
498  }
499 
500  osp.find(el_output);
501  first_run = false;
502  }
503 
504  // Get the matching nonzeros
505  r_ind.resize(el_output.size());
506  std::copy(el_output.begin(), el_output.end(), r_ind.begin());
507  aseed[d][0].sparsity().get_nz(r_ind);
508 
509  // Sparsity pattern for the result
510  r_colind.resize(isp.size2()+1); // Col count
511  std::fill(r_colind.begin(), r_colind.end(), 0);
512  r_row.clear();
513 
514  // Perform the assignments
515  r_nz.clear();
516  for (casadi_int k=0; k<nz.size(); ++k) {
517 
518  // Get the corresponding nonzero for the input
519  casadi_int el = nz[k];
520 
521  // Skip if zero assignment
522  if (el==-1) continue;
523 
524  // Get the corresponding nonzero in the argument
525  casadi_int el_arg = r_ind[el];
526 
527  // Skip if no argument
528  if (el_arg==-1) continue;
529 
530  // Save the assignment
531  r_nz.push_back(el_arg);
532 
533  // Get the corresponding element
534  casadi_int i=icol[k], j=irow[k];
535 
536  // Add to sparsity pattern
537  r_row.push_back(j);
538  r_colind[1+i]++;
539  }
540 
541  // col count -> col offset
542  for (casadi_int i=1; i<r_colind.size(); ++i) r_colind[i] += r_colind[i-1];
543 
544  // If anything to set/add
545  if (!r_nz.empty()) {
546  // Create a sparsity pattern from vectors
547  Sparsity f_sp(isp.size1(), isp.size2(), r_colind, r_row);
548  asens[d][1] += aseed[d][0]->get_nzref(f_sp, r_nz);
549  if (!Add) {
550  asens[d][0] += MX::zeros(f_sp)->get_nzassign(aseed[d][0], r_nz);
551  } else {
552  asens[d][0] += aseed[d][0];
553  }
554  } else {
555  asens[d][0] += aseed[d][0];
556  }
557  }
558  }
559  }
560 
561  template<bool Add>
562  int SetNonzerosVector<Add>::
563  eval(const double** arg, double** res, casadi_int* iw, double* w) const {
564  return eval_gen<double>(arg, res, iw, w);
565  }
566 
567  template<bool Add>
568  int SetNonzerosVector<Add>::
569  eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
570  return eval_gen<SXElem>(arg, res, iw, w);
571  }
572 
573  template<bool Add>
574  template<typename T>
575  int SetNonzerosVector<Add>::
576  eval_gen(const T** arg, T** res, casadi_int* iw, T* w) const {
577  const T* idata0 = arg[0];
578  const T* idata = arg[1];
579  T* odata = res[0];
580  if (idata0 != odata) {
581  std::copy(idata0, idata0+this->dep(0).nnz(), odata);
582  }
583  for (auto k=this->nz_.begin(); k!=this->nz_.end(); ++k, ++idata) {
584  if (Add) {
585  if (*k>=0) odata[*k] += *idata;
586  } else {
587  if (*k>=0) odata[*k] = *idata;
588  }
589  }
590  return 0;
591  }
592 
593  template<bool Add>
594  int SetNonzerosSlice<Add>::
595  eval(const double** arg, double** res, casadi_int* iw, double* w) const {
596  return eval_gen<double>(arg, res, iw, w);
597  }
598 
599  template<bool Add>
600  int SetNonzerosSlice<Add>::
601  eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
602  return eval_gen<SXElem>(arg, res, iw, w);
603  }
604 
605  template<bool Add>
606  template<typename T>
607  int SetNonzerosSlice<Add>::
608  eval_gen(const T** arg, T** res, casadi_int* iw, T* w) const {
609  const T* idata0 = arg[0];
610  const T* idata = arg[1];
611  T* odata = res[0];
612  if (idata0 != odata) {
613  std::copy(idata0, idata0+this->dep(0).nnz(), odata);
614  }
615  T* odata_stop = odata + s_.stop;
616  for (odata += s_.start; odata != odata_stop; odata += s_.step) {
617  if (Add) {
618  *odata += *idata++;
619  } else {
620  *odata = *idata++;
621  }
622  }
623  return 0;
624  }
625 
626  template<bool Add>
627  int SetNonzerosSlice2<Add>::
628  eval(const double** arg, double** res, casadi_int* iw, double* w) const {
629  return eval_gen<double>(arg, res, iw, w);
630  }
631 
632  template<bool Add>
633  int SetNonzerosSlice2<Add>::
634  eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
635  return eval_gen<SXElem>(arg, res, iw, w);
636  }
637 
638  template<bool Add>
639  template<typename T>
640  int SetNonzerosSlice2<Add>::
641  eval_gen(const T** arg, T** res, casadi_int* iw, T* w) const {
642  const T* idata0 = arg[0];
643  const T* idata = arg[1];
644  T* odata = res[0];
645  if (idata0 != odata) {
646  std::copy(idata0, idata0 + this->dep(0).nnz(), odata);
647  }
648  T* outer_stop = odata + outer_.stop;
649  T* outer = odata + outer_.start;
650  for (; outer != outer_stop; outer += outer_.step) {
651  for (T* inner = outer+inner_.start;
652  inner != outer+inner_.stop;
653  inner += inner_.step) {
654  if (Add) {
655  *inner += *idata++;
656  } else {
657  *inner = *idata++;
658  }
659  }
660  }
661  return 0;
662  }
663 
664  template<bool Add>
665  int SetNonzerosVector<Add>::
666  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
667  const bvec_t *a0 = arg[0];
668  const bvec_t *a = arg[1];
669  bvec_t *r = res[0];
670  casadi_int n = this->nnz();
671 
672  // Propagate sparsity
673  if (r != a0) std::copy(a0, a0+n, r);
674  for (auto k=this->nz_.begin(); k!=this->nz_.end(); ++k, ++a) {
675  if (Add) {
676  if (*k>=0) r[*k] |= *a;
677  } else {
678  if (*k>=0) r[*k] = *a;
679  }
680  }
681  return 0;
682  }
683 
684  template<bool Add>
685  int SetNonzerosVector<Add>::
686  sp_reverse(bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
687  bvec_t *a = arg[1];
688  bvec_t *r = res[0];
689  for (auto k=this->nz_.begin(); k!=this->nz_.end(); ++k, ++a) {
690  if (*k>=0) {
691  *a |= r[*k];
692  if (!Add) {
693  r[*k] = 0;
694  }
695  }
696  }
697  MXNode::copy_rev(arg[0], r, this->nnz());
698  return 0;
699  }
700 
701  template<bool Add>
702  int SetNonzerosSlice<Add>::
703  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
704  const bvec_t *a0 = arg[0];
705  const bvec_t *a = arg[1];
706  bvec_t *r = res[0];
707  casadi_int n = this->nnz();
708 
709  // Propagate sparsity
710  if (r != a0) std::copy(a0, a0+n, r);
711  for (casadi_int k=s_.start; k!=s_.stop; k+=s_.step) {
712  if (Add) {
713  r[k] |= *a++;
714  } else {
715  r[k] = *a++;
716  }
717  }
718  return 0;
719  }
720 
721  template<bool Add>
722  int SetNonzerosSlice<Add>::
723  sp_reverse(bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
724  bvec_t *a = arg[1];
725  bvec_t *r = res[0];
726  for (casadi_int k=s_.start; k!=s_.stop; k+=s_.step) {
727  *a++ |= r[k];
728  if (!Add) {
729  r[k] = 0;
730  }
731  }
732  MXNode::copy_rev(arg[0], r, this->nnz());
733  return 0;
734  }
735 
736  template<bool Add>
737  int SetNonzerosSlice2<Add>::
738  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
739  const bvec_t *a0 = arg[0];
740  const bvec_t *a = arg[1];
741  bvec_t *r = res[0];
742  casadi_int n = this->nnz();
743 
744  // Propagate sparsity
745  if (r != a0) std::copy(a0, a0+n, r);
746  for (casadi_int k1=outer_.start; k1!=outer_.stop; k1+=outer_.step) {
747  for (casadi_int k2=k1+inner_.start; k2!=k1+inner_.stop; k2+=inner_.step) {
748  if (Add) {
749  r[k2] |= *a++;
750  } else {
751  r[k2] = *a++;
752  }
753  }
754  }
755  return 0;
756  }
757 
758  template<bool Add>
759  int SetNonzerosSlice2<Add>::
760  sp_reverse(bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
761  bvec_t *a = arg[1];
762  bvec_t *r = res[0];
763  for (casadi_int k1=outer_.start; k1!=outer_.stop; k1+=outer_.step) {
764  for (casadi_int k2=k1+inner_.start; k2!=k1+inner_.stop; k2+=inner_.step) {
765  *a++ |= r[k2];
766  if (!Add) {
767  r[k2] = 0;
768  }
769  }
770  }
771  MXNode::copy_rev(arg[0], r, this->nnz());
772  return 0;
773  }
774 
775  template<bool Add>
776  std::string SetNonzerosVector<Add>::disp(const std::vector<std::string>& arg) const {
777  std::stringstream ss;
778  ss << "(" << arg.at(0) << nz_ << (Add ? " += " : " = ") << arg.at(1) << ")";
779  return ss.str();
780  }
781 
782  template<bool Add>
783  std::string SetNonzerosSlice<Add>::disp(const std::vector<std::string>& arg) const {
784  std::stringstream ss;
785  ss << "(" << arg.at(0) << "[" << s_ << "]" << (Add ? " += " : " = ") << arg.at(1) << ")";
786  return ss.str();
787  }
788 
789  template<bool Add>
790  std::string SetNonzerosSlice2<Add>::disp(const std::vector<std::string>& arg) const {
791  std::stringstream ss;
792  ss << "(" << arg.at(0) << "[" << outer_ << ";" << inner_ << "]" << (Add ? " += " : " = ")
793  << arg.at(1) << ")";
794  return ss.str();
795  }
796 
797  template<bool Add>
798  Matrix<casadi_int> SetNonzeros<Add>::mapping() const {
799  std::vector<casadi_int> nz = all();
800  return Matrix<casadi_int>(this->dep(1).sparsity(), nz, false);
801  }
802 
803  template<bool Add>
804  bool SetNonzerosVector<Add>::is_equal(const MXNode* node, casadi_int depth) const {
805  // Check dependencies
806  if (!this->sameOpAndDeps(node, depth)) return false;
807 
808  // Check if same node
809  const SetNonzerosVector<Add>* n = dynamic_cast<const SetNonzerosVector<Add>*>(node);
810  if (n==nullptr) return false;
811 
812  // Check sparsity
813  if (this->sparsity()!=node->sparsity()) return false;
814 
815  // Check indices
816  if (this->nz_.size()!=n->nz_.size()) return false;
817  if (!std::equal(this->nz_.begin(), this->nz_.end(), n->nz_.begin())) return false;
818 
819  return true;
820  }
821 
822  template<bool Add>
823  bool SetNonzerosSlice<Add>::is_equal(const MXNode* node, casadi_int depth) const {
824  // Check dependencies
825  if (!this->sameOpAndDeps(node, depth)) return false;
826 
827  // Check if same node
828  const SetNonzerosSlice<Add>* n = dynamic_cast<const SetNonzerosSlice<Add>*>(node);
829  if (n==nullptr) return false;
830 
831  // Check sparsity
832  if (this->sparsity()!=node->sparsity()) return false;
833 
834  // Check indices
835  if (this->s_ != n->s_) return false;
836 
837  return true;
838  }
839 
840  template<bool Add>
841  bool SetNonzerosSlice2<Add>::is_equal(const MXNode* node, casadi_int depth) const {
842  // Check dependencies
843  if (!this->sameOpAndDeps(node, depth)) return false;
844 
845  // Check if same node
846  const SetNonzerosSlice2<Add>* n = dynamic_cast<const SetNonzerosSlice2<Add>*>(node);
847  if (n==nullptr) return false;
848 
849  // Check sparsity
850  if (this->sparsity()!=node->sparsity()) return false;
851 
852  // Check indices
853  if (this->inner_ != n->inner_ || this->outer_!=n->outer_) return false;
854 
855  return true;
856  }
857 
858  template<bool Add>
859  void SetNonzerosVector<Add>::
860  generate(CodeGenerator& g,
861  const std::vector<casadi_int>& arg, const std::vector<casadi_int>& res) const {
862  // Copy first argument if not inplace
863  if (arg[0]!=res[0]) {
864  g << g.copy(g.work(arg[0], this->dep(0).nnz()), this->nnz(),
865  g.work(res[0], this->nnz())) << '\n';
866  }
867 
868  // Condegen the indices
869  std::string ind = g.constant(this->nz_);
870 
871  // Perform the operation inplace
872  g.local("cii", "const casadi_int", "*");
873  g.local("rr", "casadi_real", "*");
874  g.local("ss", "casadi_real", "*");
875  g << "for (cii=" << ind << ", rr=" << g.work(res[0], this->nnz()) << ", "
876  << "ss=" << g.work(arg[1], this->dep(1).nnz()) << "; cii!=" << ind
877  << "+" << this->nz_.size() << "; ++cii, ++ss) ";
878  if (has_negative(this->nz_)) {
879  g << "if (*cii>=0) ";
880  }
881  g << "rr[*cii] " << (Add?"+=":"=") << " *ss;\n";
882  }
883 
884  template<bool Add>
885  void SetNonzerosSlice<Add>::
886  generate(CodeGenerator& g,
887  const std::vector<casadi_int>& arg, const std::vector<casadi_int>& res) const {
888  // Copy first argument if not inplace
889  if (arg[0]!=res[0]) {
890  g << g.copy(g.work(arg[0], this->dep(0).nnz()), this->nnz(),
891  g.work(res[0], this->nnz())) << '\n';
892  }
893 
894  // Perform the operation inplace
895  g.local("rr", "casadi_real", "*");
896  g.local("ss", "casadi_real", "*");
897  g << "for (rr=" << g.work(res[0], this->nnz()) << "+" << s_.start << ", ss="
898  << g.work(arg[1], this->dep(1).nnz()) << "; rr!="
899  << g.work(res[0], this->nnz()) << "+" << s_.stop
900  << "; rr+=" << s_.step << ")"
901  << " *rr " << (Add?"+=":"=") << " *ss++;\n";
902  }
903 
904  template<bool Add>
905  void SetNonzerosSlice2<Add>::
906  generate(CodeGenerator& g,
907  const std::vector<casadi_int>& arg, const std::vector<casadi_int>& res) const {
908  // Copy first argument if not inplace
909  if (arg[0]!=res[0]) {
910  g << g.copy(g.work(arg[0], this->dep(0).nnz()), this->nnz(),
911  g.work(res[0], this->nnz())) << '\n';
912  }
913 
914  // Perform the operation inplace
915  g.local("rr", "casadi_real", "*");
916  g.local("ss", "casadi_real", "*");
917  g.local("tt", "casadi_real", "*");
918  g << "for (rr=" << g.work(res[0], this->nnz()) << "+" << outer_.start
919  << ", ss=" << g.work(arg[1], this->dep(1).nnz()) << "; rr!="
920  << g.work(res[0], this->nnz()) << "+" << outer_.stop
921  << "; rr+=" << outer_.step << ")"
922  << " for (tt=rr+" << inner_.start << "; tt!=rr+" << inner_.stop
923  << "; tt+=" << inner_.step << ")"
924  << " *tt " << (Add?"+=":"=") << " *ss++;\n";
925  }
926 
927  template<bool Add>
928  void SetNonzerosVector<Add>::serialize_body(SerializingStream& s) const {
929  MXNode::serialize_body(s);
930  s.pack("SetNonzerosVector::nonzeros", nz_);
931  }
932 
933  template<bool Add>
934  SetNonzerosVector<Add>::SetNonzerosVector(DeserializingStream& s) : SetNonzeros<Add>(s) {
935  s.unpack("SetNonzerosVector::nonzeros", nz_);
936  }
937 
938  template<bool Add>
939  void SetNonzerosVector<Add>::serialize_type(SerializingStream& s) const {
940  MXNode::serialize_type(s);
941  s.pack("SetNonzeros::type", 'a');
942  }
943 
944  template<bool Add>
945  void SetNonzerosSlice<Add>::serialize_body(SerializingStream& s) const {
946  MXNode::serialize_body(s);
947  s.pack("SetNonzerosSlice::slice", s_);
948  }
949 
950  template<bool Add>
951  SetNonzerosSlice<Add>::SetNonzerosSlice(DeserializingStream& s) : SetNonzeros<Add>(s) {
952  s.unpack("SetNonzerosSlice::slice", s_);
953  }
954 
955  template<bool Add>
956  void SetNonzerosSlice<Add>::serialize_type(SerializingStream& s) const {
957  MXNode::serialize_type(s);
958  s.pack("SetNonzeros::type", 'b');
959  }
960 
961  template<bool Add>
962  void SetNonzerosSlice2<Add>::serialize_body(SerializingStream& s) const {
963  MXNode::serialize_body(s);
964  s.pack("SetNonzerosSlice2::inner", inner_);
965  s.pack("SetNonzerosSlice2::outer", outer_);
966  }
967 
968  template<bool Add>
969  SetNonzerosSlice2<Add>::SetNonzerosSlice2(DeserializingStream& s) : SetNonzeros<Add>(s) {
970  s.unpack("SetNonzerosSlice2::inner", inner_);
971  s.unpack("SetNonzerosSlice2::outer", outer_);
972  }
973 
974  template<bool Add>
975  void SetNonzerosSlice2<Add>::serialize_type(SerializingStream& s) const {
976  MXNode::serialize_type(s);
977  s.pack("SetNonzeros::type", 'c');
978  }
979 
980  template<bool Add>
981  MXNode* SetNonzeros<Add>::deserialize(DeserializingStream& s) {
982  char t;
983  s.unpack("SetNonzeros::type", t);
984  switch (t) {
985  case 'a': return new SetNonzerosVector<Add>(s);
986  case 'b': return new SetNonzerosSlice<Add>(s);
987  case 'c': return new SetNonzerosSlice2<Add>(s);
988  default: casadi_assert_dev(false);
989  }
990  }
991 
992 } // namespace casadi
993 
995 
996 #endif // CASADI_SETNONZEROS_IMPL_HPP
The casadi namespace.
bool has_negative(const std::vector< T > &v)
Check if the vector has negative entries.
bool CASADI_EXPORT is_slice(const IM &x, bool ind1=false)
Is the IM a Slice.
std::pair< Slice, Slice > CASADI_EXPORT to_slice2(const std::vector< casadi_int > &v)
Construct nested slices from an index vector (requires is_slice2(v) to be true)
bool CASADI_EXPORT is_slice2(const std::vector< casadi_int > &v)
Check if an index vector can be represented more efficiently as two nested slices.
Slice CASADI_EXPORT to_slice(const IM &x, bool ind1=false)
Convert IM to Slice.