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