AutoDiffBlock.hpp
1 /*
2  Copyright 2013 SINTEF ICT, Applied Mathematics.
3  Copyright 2016 IRIS AS
4 
5  This file is part of the Open Porous Media project (OPM).
6 
7  OPM is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  OPM is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with OPM. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifndef OPM_AUTODIFFBLOCK_HEADER_INCLUDED
22 #define OPM_AUTODIFFBLOCK_HEADER_INCLUDED
23 
24 #include <opm/common/utility/platform_dependent/disable_warnings.h>
25 
26 #include <Eigen/Eigen>
27 #include <Eigen/Sparse>
28 #include <opm/autodiff/fastSparseOperations.hpp>
29 
30 #include <opm/common/utility/platform_dependent/reenable_warnings.h>
31 
32 #include <opm/autodiff/AutoDiffMatrix.hpp>
33 
34 
35 #include <utility>
36 #include <vector>
37 #include <cassert>
38 #include <iostream>
39 
40 namespace Opm
41 {
42 
94  template <typename Scalar>
96  {
97  public:
99  typedef Eigen::Array<Scalar, Eigen::Dynamic, 1> V;
101  typedef AutoDiffMatrix M;
102 
105  {
106  return AutoDiffBlock(V(), {});
107  }
108 
111  static AutoDiffBlock constant(V&& val)
112  {
113  return AutoDiffBlock(std::move(val));
114  }
115 
118  static AutoDiffBlock constant(const V& val)
119  {
120  return AutoDiffBlock(val);
121  }
122 
129  static AutoDiffBlock constant(const V& val, const std::vector<int>& blocksizes)
130  {
131  std::vector<M> jac;
132  const int num_elem = val.size();
133  const int num_blocks = blocksizes.size();
134  // For constants, all jacobian blocks are zero.
135  jac.resize(num_blocks);
136  for (int i = 0; i < num_blocks; ++i) {
137  jac[i] = M(num_elem, blocksizes[i]);
138  }
139  V val_copy(val);
140  return AutoDiffBlock(std::move(val_copy), std::move(jac));
141  }
142 
150  static AutoDiffBlock variable(const int index, V&& val, const std::vector<int>& blocksizes)
151  {
152  std::vector<M> jac;
153  const int num_elem = val.size();
154  const int num_blocks = blocksizes.size();
155  // First, set all jacobian blocks to zero...
156  jac.resize(num_blocks);
157  for (int i = 0; i < num_blocks; ++i) {
158  jac[i] = M(num_elem, blocksizes[i]);
159  }
160  // ... then set the one corrresponding to this variable to identity.
161  assert(blocksizes[index] == num_elem);
162  jac[index] = M::createIdentity(val.size());
163  return AutoDiffBlock(std::move(val), std::move(jac));
164  }
165 
173  static AutoDiffBlock variable(const int index, const V& val, const std::vector<int>& blocksizes)
174  {
175  V value = val;
176  return variable(index, std::move(value), blocksizes);
177  }
178 
184  static AutoDiffBlock function(V&& val, std::vector<M>&& jac)
185  {
186  return AutoDiffBlock(std::move(val), std::move(jac));
187  }
188 
194  static AutoDiffBlock function(const V& val, const std::vector<M>& jac)
195  {
196  V val_copy(val);
197  std::vector<M> jac_copy(jac);
198  return AutoDiffBlock(std::move(val_copy), std::move(jac_copy));
199  }
200 
203  static std::vector<AutoDiffBlock> variables(const std::vector<V>& initial_values)
204  {
205  const int num_vars = initial_values.size();
206  std::vector<int> bpat;
207  for (int v = 0; v < num_vars; ++v) {
208  bpat.push_back(initial_values[v].size());
209  }
210  std::vector<AutoDiffBlock> vars;
211  for (int v = 0; v < num_vars; ++v) {
212  vars.emplace_back(variable(v, initial_values[v], bpat));
213  }
214  return vars;
215  }
216 
219  {
220  if (jac_.empty()) {
221  jac_ = rhs.jac_;
222  } else if (!rhs.jac_.empty()) {
223  assert (numBlocks() == rhs.numBlocks());
224  assert (value().size() == rhs.value().size());
225 
226  const int num_blocks = numBlocks();
227 #pragma omp parallel for schedule(static)
228  for (int block = 0; block < num_blocks; ++block) {
229  assert(jac_[block].rows() == rhs.jac_[block].rows());
230  assert(jac_[block].cols() == rhs.jac_[block].cols());
231  jac_[block] += rhs.jac_[block];
232  }
233  }
234 
235  val_ += rhs.val_;
236 
237  return *this;
238  }
239 
242  {
243  if (jac_.empty()) {
244  const int num_blocks = rhs.numBlocks();
245  jac_.resize(num_blocks);
246 #pragma omp parallel for schedule(static)
247  for (int block = 0; block < num_blocks; ++block) {
248  jac_[block] = rhs.jac_[block] * (-1.0);
249  }
250  } else if (!rhs.jac_.empty()) {
251  assert (numBlocks() == rhs.numBlocks());
252  assert (value().size() == rhs.value().size());
253 
254  const int num_blocks = numBlocks();
255 #pragma omp parallel for schedule(static)
256  for (int block = 0; block < num_blocks; ++block) {
257  assert(jac_[block].rows() == rhs.jac_[block].rows());
258  assert(jac_[block].cols() == rhs.jac_[block].cols());
259  jac_[block] -= rhs.jac_[block];
260  }
261  }
262 
263  val_ -= rhs.val_;
264 
265  return *this;
266  }
267 
270  {
271  if (jac_.empty() && rhs.jac_.empty()) {
272  return constant(val_ + rhs.val_);
273  }
274  if (jac_.empty()) {
275  return val_ + rhs;
276  }
277  if (rhs.jac_.empty()) {
278  return *this + rhs.val_;
279  }
280  std::vector<M> jac = jac_;
281  assert(numBlocks() == rhs.numBlocks());
282  int num_blocks = numBlocks();
283 #pragma omp parallel for schedule(static)
284  for (int block = 0; block < num_blocks; ++block) {
285  assert(jac[block].rows() == rhs.jac_[block].rows());
286  assert(jac[block].cols() == rhs.jac_[block].cols());
287  jac[block] += rhs.jac_[block];
288  }
289  return function(val_ + rhs.val_, std::move(jac));
290  }
291 
294  {
295  if (jac_.empty() && rhs.jac_.empty()) {
296  return constant(val_ - rhs.val_);
297  }
298  if (jac_.empty()) {
299  return val_ - rhs;
300  }
301  if (rhs.jac_.empty()) {
302  return *this - rhs.val_;
303  }
304  std::vector<M> jac = jac_;
305  assert(numBlocks() == rhs.numBlocks());
306  int num_blocks = numBlocks();
307 #pragma omp parallel for schedule(static)
308  for (int block = 0; block < num_blocks; ++block) {
309  assert(jac[block].rows() == rhs.jac_[block].rows());
310  assert(jac[block].cols() == rhs.jac_[block].cols());
311  jac[block] -= rhs.jac_[block];
312  }
313  return function(val_ - rhs.val_, std::move(jac));
314  }
315 
318  {
319  if (jac_.empty() && rhs.jac_.empty()) {
320  return constant(val_ * rhs.val_);
321  }
322  if (jac_.empty()) {
323  return val_ * rhs;
324  }
325  if (rhs.jac_.empty()) {
326  return *this * rhs.val_;
327  }
328  int num_blocks = numBlocks();
329  std::vector<M> jac(num_blocks);
330  assert(numBlocks() == rhs.numBlocks());
331  M D1(val_.matrix().asDiagonal());
332  M D2(rhs.val_.matrix().asDiagonal());
333 #pragma omp parallel for schedule(dynamic)
334  for (int block = 0; block < num_blocks; ++block) {
335  assert(jac_[block].rows() == rhs.jac_[block].rows());
336  assert(jac_[block].cols() == rhs.jac_[block].cols());
337  if( jac_[block].nonZeros() == 0 && rhs.jac_[block].nonZeros() == 0 ) {
338  jac[block] = M( D2.rows(), jac_[block].cols() );
339  }
340  else if( jac_[block].nonZeros() == 0 )
341  jac[block] = D1*rhs.jac_[block];
342  else if ( rhs.jac_[block].nonZeros() == 0 ) {
343  jac[block] = D2*jac_[block];
344  }
345  else {
346  jac[block] = D2*jac_[block];
347  jac[block] += D1*rhs.jac_[block];
348  }
349  }
350  return function(val_ * rhs.val_, std::move(jac));
351  }
352 
355  {
356  if (jac_.empty() && rhs.jac_.empty()) {
357  return constant(val_ / rhs.val_);
358  }
359  if (jac_.empty()) {
360  return val_ / rhs;
361  }
362  if (rhs.jac_.empty()) {
363  return *this / rhs.val_;
364  }
365  int num_blocks = numBlocks();
366  std::vector<M> jac(num_blocks);
367  assert(numBlocks() == rhs.numBlocks());
368  M D1(val_.matrix().asDiagonal());
369  M D2(rhs.val_.matrix().asDiagonal());
370  M D3((1.0/(rhs.val_*rhs.val_)).matrix().asDiagonal());
371 #pragma omp parallel for schedule(dynamic)
372  for (int block = 0; block < num_blocks; ++block) {
373  assert(jac_[block].rows() == rhs.jac_[block].rows());
374  assert(jac_[block].cols() == rhs.jac_[block].cols());
375  if( jac_[block].nonZeros() == 0 && rhs.jac_[block].nonZeros() == 0 ) {
376  jac[block] = M( D3.rows(), jac_[block].cols() );
377  }
378  else if( jac_[block].nonZeros() == 0 ) {
379  jac[block] = D3 * ( D1*rhs.jac_[block]) * (-1.0);
380  }
381  else if ( rhs.jac_[block].nonZeros() == 0 )
382  {
383  jac[block] = D3 * (D2*jac_[block]);
384  }
385  else {
386  jac[block] = D3 * (D2*jac_[block] + (D1*rhs.jac_[block]*(-1.0)));
387  }
388  }
389  return function(val_ / rhs.val_, std::move(jac));
390  }
391 
393  template <class Ostream>
394  Ostream&
395  print(Ostream& os) const
396  {
397  int num_blocks = jac_.size();
398  os << "Value =\n" << val_ << "\n\nJacobian =\n";
399  for (int i = 0; i < num_blocks; ++i) {
400  Eigen::SparseMatrix<double> m;
401  jac_[i].toSparse(m);
402  os << "Sub Jacobian #" << i << '\n' << m << "\n";
403  }
404  return os;
405  }
406 
408  void swap(AutoDiffBlock& other)
409  {
410  val_.swap(other.val_);
411  jac_.swap(other.jac_);
412  }
413 
415  int size() const
416  {
417  return val_.size();
418  }
419 
421  int numBlocks() const
422  {
423  return jac_.size();
424  }
425 
427  std::vector<int> blockPattern() const
428  {
429  const int nb = numBlocks();
430  std::vector<int> bp(nb);
431  for (int block = 0; block < nb; ++block) {
432  bp[block] = jac_[block].cols();
433  }
434  return bp;
435  }
436 
438  const V& value() const
439  {
440  return val_;
441  }
442 
444  const std::vector<M>& derivative() const
445  {
446  return jac_;
447  }
448 
449  private:
450  AutoDiffBlock(const V& val)
451  : val_(val)
452  {
453  }
454 
455  AutoDiffBlock(V&& val)
456  : val_(std::move(val))
457  {
458  }
459 
460  AutoDiffBlock(V&& val, std::vector<M>&& jac)
461  : val_(std::move(val)), jac_(std::move(jac))
462  {
463 #ifndef NDEBUG
464  const int num_elem = val_.size();
465  const int num_blocks = jac_.size();
466  for (int block = 0; block < num_blocks; ++block) {
467  assert(num_elem == jac_[block].rows());
468  }
469 #endif
470  }
471 
472  V val_;
473  std::vector<M> jac_;
474  };
475 
476 
477  // --------- Free functions and operators for AutoDiffBlock ---------
478 
480  template <class Ostream, typename Scalar>
481  Ostream&
482  operator<<(Ostream& os, const AutoDiffBlock<Scalar>& fw)
483  {
484  return fw.print(os);
485  }
486 
487 
489  template <typename Scalar>
490  AutoDiffBlock<Scalar> operator*(const typename AutoDiffBlock<Scalar>::M& lhs,
491  const AutoDiffBlock<Scalar>& rhs)
492  {
493  int num_blocks = rhs.numBlocks();
494  std::vector<typename AutoDiffBlock<Scalar>::M> jac(num_blocks);
495  assert(lhs.cols() == rhs.value().rows());
496 #pragma omp parallel for schedule(dynamic)
497  for (int block = 0; block < num_blocks; ++block) {
498  fastSparseProduct(lhs, rhs.derivative()[block], jac[block]);
499  }
500  typename AutoDiffBlock<Scalar>::V val = lhs*rhs.value().matrix();
501  return AutoDiffBlock<Scalar>::function(std::move(val), std::move(jac));
502  }
503 
504 
506  template <typename Scalar>
507  AutoDiffBlock<Scalar> operator*(const Eigen::SparseMatrix<Scalar>& lhs,
508  const AutoDiffBlock<Scalar>& rhs)
509  {
510  int num_blocks = rhs.numBlocks();
511  std::vector<typename AutoDiffBlock<Scalar>::M> jac(num_blocks);
512  assert(lhs.cols() == rhs.value().rows());
513  for (int block = 0; block < num_blocks; ++block) {
514  fastSparseProduct(lhs, rhs.derivative()[block], jac[block]);
515  }
516  typename AutoDiffBlock<Scalar>::V val = lhs*rhs.value().matrix();
517  return AutoDiffBlock<Scalar>::function(std::move(val), std::move(jac));
518  }
519 
520 
522  template <typename Scalar>
523  AutoDiffBlock<Scalar> operator*(const typename AutoDiffBlock<Scalar>::V& lhs,
524  const AutoDiffBlock<Scalar>& rhs)
525  {
526  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) * rhs;
527  }
528 
529 
531  template <typename Scalar>
533  const typename AutoDiffBlock<Scalar>::V& rhs)
534  {
535  return rhs * lhs; // Commutative operation.
536  }
537 
538 
540  template <typename Scalar>
541  AutoDiffBlock<Scalar> operator+(const typename AutoDiffBlock<Scalar>::V& lhs,
542  const AutoDiffBlock<Scalar>& rhs)
543  {
544  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) + rhs;
545  }
546 
547 
549  template <typename Scalar>
551  const typename AutoDiffBlock<Scalar>::V& rhs)
552  {
553  return rhs + lhs; // Commutative operation.
554  }
555 
556 
558  template <typename Scalar>
559  AutoDiffBlock<Scalar> operator-(const typename AutoDiffBlock<Scalar>::V& lhs,
560  const AutoDiffBlock<Scalar>& rhs)
561  {
562  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) - rhs;
563  }
564 
565 
567  template <typename Scalar>
569  const typename AutoDiffBlock<Scalar>::V& rhs)
570  {
571  return lhs - AutoDiffBlock<Scalar>::constant(rhs, lhs.blockPattern());
572  }
573 
574 
576  template <typename Scalar>
577  AutoDiffBlock<Scalar> operator/(const typename AutoDiffBlock<Scalar>::V& lhs,
578  const AutoDiffBlock<Scalar>& rhs)
579  {
580  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) / rhs;
581  }
582 
583 
585  template <typename Scalar>
587  const typename AutoDiffBlock<Scalar>::V& rhs)
588  {
589  return lhs / AutoDiffBlock<Scalar>::constant(rhs, lhs.blockPattern());
590  }
591 
592 
600  template <typename Scalar>
602  const Scalar& rhs)
603  {
604  std::vector< typename AutoDiffBlock<Scalar>::M > jac;
605  jac.reserve( lhs.numBlocks() );
606  for (int block=0; block<lhs.numBlocks(); block++) {
607  jac.emplace_back( lhs.derivative()[block] * rhs );
608  }
609  return AutoDiffBlock<Scalar>::function( lhs.value() * rhs, std::move(jac) );
610  }
611 
612 
620  template <typename Scalar>
621  AutoDiffBlock<Scalar> operator*(const Scalar& lhs,
622  const AutoDiffBlock<Scalar>& rhs)
623  {
624  return rhs * lhs; // Commutative operation.
625  }
626 
627 
635  template <typename Scalar>
637  const double exponent)
638  {
639  const typename AutoDiffBlock<Scalar>::V val = base.value().pow(exponent);
640  const typename AutoDiffBlock<Scalar>::V derivative = exponent * base.value().pow(exponent - 1.0);
641  const typename AutoDiffBlock<Scalar>::M derivative_diag(derivative.matrix().asDiagonal());
642 
643  std::vector< typename AutoDiffBlock<Scalar>::M > jac (base.numBlocks());
644  for (int block = 0; block < base.numBlocks(); block++) {
645  fastSparseProduct(derivative_diag, base.derivative()[block], jac[block]);
646  }
647 
648  return AutoDiffBlock<Scalar>::function( std::move(val), std::move(jac) );
649  }
650 
658  template <typename Scalar>
660  const typename AutoDiffBlock<Scalar>::V& exponent)
661  {
662  // Add trivial derivatives and use the AD pow function
663  return pow(base,AutoDiffBlock<Scalar>::constant(exponent));
664  }
665 
673  template <typename Scalar>
675  const AutoDiffBlock<Scalar>& exponent)
676  {
677  // Add trivial derivatives and use the AD pow function
678  return pow(AutoDiffBlock<Scalar>::constant(base),exponent);
679  }
680 
688  template <typename Scalar>
690  const AutoDiffBlock<Scalar>& exponent)
691  {
692  const int num_elem = base.value().size();
693  assert(exponent.size() == num_elem);
694  typename AutoDiffBlock<Scalar>::V val (num_elem);
695  for (int i = 0; i < num_elem; ++i) {
696  val[i] = std::pow(base.value()[i], exponent.value()[i]);
697  }
698 
699  // (f^g)' = f^g * ln(f) * g' + g * f^(g-1) * f' = der1 + der2
700  // if f' is empty only der1 is calculated
701  // if g' is empty only der2 is calculated
702  // if f' and g' are non empty they should have the same size
703  int num_blocks = std::max (base.numBlocks(), exponent.numBlocks());
704  if (!base.derivative().empty() && !exponent.derivative().empty()) {
705  assert(exponent.numBlocks() == base.numBlocks());
706  }
707  std::vector< typename AutoDiffBlock<Scalar>::M > jac (num_blocks);
708 
709  if ( !exponent.derivative().empty() ) {
710  typename AutoDiffBlock<Scalar>::V der1 = val;
711  for (int i = 0; i < num_elem; ++i) {
712  der1[i] *= std::log(base.value()[i]);
713  }
714  std::vector< typename AutoDiffBlock<Scalar>::M > jac1 (exponent.numBlocks());
715  const typename AutoDiffBlock<Scalar>::M der1_diag(der1.matrix().asDiagonal());
716  for (int block = 0; block < exponent.numBlocks(); block++) {
717  fastSparseProduct(der1_diag, exponent.derivative()[block], jac1[block]);
718  jac[block] = jac1[block];
719  }
720  }
721 
722  if ( !base.derivative().empty() ) {
723  typename AutoDiffBlock<Scalar>::V der2 = exponent.value();
724  for (int i = 0; i < num_elem; ++i) {
725  der2[i] *= std::pow(base.value()[i], exponent.value()[i] - 1.0);
726  }
727  std::vector< typename AutoDiffBlock<Scalar>::M > jac2 (base.numBlocks());
728  const typename AutoDiffBlock<Scalar>::M der2_diag(der2.matrix().asDiagonal());
729  for (int block = 0; block < base.numBlocks(); block++) {
730  fastSparseProduct(der2_diag, base.derivative()[block], jac2[block]);
731  if (!exponent.derivative().empty()) {
732  jac[block] += jac2[block];
733  } else {
734  jac[block] = jac2[block];
735  }
736  }
737  }
738 
739  return AutoDiffBlock<Scalar>::function(std::move(val), std::move(jac));
740  }
741 
742 
743 
744 } // namespace Opm
745 
746 
747 
748 #endif // OPM_AUTODIFFBLOCK_HEADER_INCLUDED
void swap(AutoDiffBlock &other)
Efficient swap function.
Definition: AutoDiffBlock.hpp:408
static AutoDiffBlock null()
Construct an empty AutoDiffBlock.
Definition: AutoDiffBlock.hpp:104
static AutoDiffMatrix createIdentity(const int num_rows_cols)
Creates an identity matrix with num_rows_cols x num_rows_cols entries.
Definition: AutoDiffMatrix.hpp:81
const V & value() const
Function value.
Definition: AutoDiffBlock.hpp:438
AutoDiffBlock & operator+=(const AutoDiffBlock &rhs)
Elementwise operator +=.
Definition: AutoDiffBlock.hpp:218
static AutoDiffBlock variable(const int index, const V &val, const std::vector< int > &blocksizes)
Create an AutoDiffBlock representing a single variable block.
Definition: AutoDiffBlock.hpp:173
AutoDiffMatrix is a wrapper class that optimizes matrix operations.
Definition: AutoDiffMatrix.hpp:43
AutoDiffMatrix M
Underlying type for jacobians.
Definition: AutoDiffBlock.hpp:101
Ostream & print(Ostream &os) const
I/O.
Definition: AutoDiffBlock.hpp:395
Definition: AutoDiff.hpp:297
int numBlocks() const
Number of Jacobian blocks.
Definition: AutoDiffBlock.hpp:421
AutoDiffBlock operator/(const AutoDiffBlock &rhs) const
Elementwise operator /.
Definition: AutoDiffBlock.hpp:354
This file contains a set of helper functions used by VFPProd / VFPInj.
Definition: AdditionalObjectDeleter.hpp:22
std::vector< int > blockPattern() const
Sizes (number of columns) of Jacobian blocks.
Definition: AutoDiffBlock.hpp:427
A class for forward-mode automatic differentiation with vector values and sparse jacobian matrices...
Definition: AutoDiffBlock.hpp:95
AutoDiffBlock operator-(const AutoDiffBlock &rhs) const
Elementwise operator -.
Definition: AutoDiffBlock.hpp:293
AutoDiffBlock< Scalar > pow(const AutoDiffBlock< Scalar > &base, const double exponent)
Computes the value of base raised to the power of exponent.
Definition: AutoDiffBlock.hpp:636
int cols() const
Returns number of columns in the matrix.
Definition: AutoDiffMatrix.hpp:575
static AutoDiffBlock constant(V &&val)
Create an AutoDiffBlock representing a constant.
Definition: AutoDiffBlock.hpp:111
AutoDiffBlock operator*(const AutoDiffBlock &rhs) const
Elementwise operator *.
Definition: AutoDiffBlock.hpp:317
static AutoDiffBlock constant(const V &val)
Create an AutoDiffBlock representing a constant.
Definition: AutoDiffBlock.hpp:118
static AutoDiffBlock function(V &&val, std::vector< M > &&jac)
Create an AutoDiffBlock by directly specifying values and jacobians.
Definition: AutoDiffBlock.hpp:184
static AutoDiffBlock variable(const int index, V &&val, const std::vector< int > &blocksizes)
Create an AutoDiffBlock representing a single variable block.
Definition: AutoDiffBlock.hpp:150
const std::vector< M > & derivative() const
Function derivatives.
Definition: AutoDiffBlock.hpp:444
static AutoDiffBlock constant(const V &val, const std::vector< int > &blocksizes)
Create an AutoDiffBlock representing a constant.
Definition: AutoDiffBlock.hpp:129
Eigen::Array< Scalar, Eigen::Dynamic, 1 > V
Underlying type for values.
Definition: AutoDiffBlock.hpp:99
void fastSparseProduct(const AutoDiffMatrix &lhs, const AutoDiffMatrix &rhs, AutoDiffMatrix &res)
Utility function to lessen code changes required elsewhere.
Definition: AutoDiffMatrix.hpp:708
static std::vector< AutoDiffBlock > variables(const std::vector< V > &initial_values)
Construct a set of primary variables, each initialized to a given vector.
Definition: AutoDiffBlock.hpp:203
AutoDiffBlock & operator-=(const AutoDiffBlock &rhs)
Elementwise operator -=.
Definition: AutoDiffBlock.hpp:241
int size() const
Number of elements.
Definition: AutoDiffBlock.hpp:415
AutoDiffBlock operator+(const AutoDiffBlock &rhs) const
Elementwise operator +.
Definition: AutoDiffBlock.hpp:269