AutoDiffBlock.hpp
Go to the documentation of this file.
1 /*
2  Copyright 2013 SINTEF ICT, Applied Mathematics.
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_AUTODIFFBLOCK_HEADER_INCLUDED
21 #define OPM_AUTODIFFBLOCK_HEADER_INCLUDED
22 
23 #include <opm/common/utility/platform_dependent/disable_warnings.h>
24 
25 #include <Eigen/Eigen>
26 #include <Eigen/Sparse>
28 
29 #include <opm/common/utility/platform_dependent/reenable_warnings.h>
30 
32 
33 
34 #include <utility>
35 #include <vector>
36 #include <cassert>
37 #include <iostream>
38 
39 namespace Opm
40 {
41 
93  template <typename Scalar>
95  {
96  public:
98  typedef Eigen::Array<Scalar, Eigen::Dynamic, 1> V;
100  typedef AutoDiffMatrix M;
101 
104  {
105  return AutoDiffBlock(V(), {});
106  }
107 
110  static AutoDiffBlock constant(V&& val)
111  {
112  return AutoDiffBlock(std::move(val));
113  }
114 
117  static AutoDiffBlock constant(const V& val)
118  {
119  return AutoDiffBlock(val);
120  }
121 
128  static AutoDiffBlock constant(const V& val, const std::vector<int>& blocksizes)
129  {
130  std::vector<M> jac;
131  const int num_elem = val.size();
132  const int num_blocks = blocksizes.size();
133  // For constants, all jacobian blocks are zero.
134  jac.resize(num_blocks);
135  for (int i = 0; i < num_blocks; ++i) {
136  jac[i] = M(num_elem, blocksizes[i]);
137  }
138  V val_copy(val);
139  return AutoDiffBlock(std::move(val_copy), std::move(jac));
140  }
141 
149  static AutoDiffBlock variable(const int index, V&& val, const std::vector<int>& blocksizes)
150  {
151  std::vector<M> jac;
152  const int num_elem = val.size();
153  const int num_blocks = blocksizes.size();
154  // First, set all jacobian blocks to zero...
155  jac.resize(num_blocks);
156  for (int i = 0; i < num_blocks; ++i) {
157  jac[i] = M(num_elem, blocksizes[i]);
158  }
159  // ... then set the one corrresponding to this variable to identity.
160  assert(blocksizes[index] == num_elem);
161  jac[index] = M::createIdentity(val.size());
162  return AutoDiffBlock(std::move(val), std::move(jac));
163  }
164 
172  static AutoDiffBlock variable(const int index, const V& val, const std::vector<int>& blocksizes)
173  {
174  V value = val;
175  return variable(index, std::move(value), blocksizes);
176  }
177 
183  static AutoDiffBlock function(V&& val, std::vector<M>&& jac)
184  {
185  return AutoDiffBlock(std::move(val), std::move(jac));
186  }
187 
193  static AutoDiffBlock function(const V& val, const std::vector<M>& jac)
194  {
195  V val_copy(val);
196  std::vector<M> jac_copy(jac);
197  return AutoDiffBlock(std::move(val_copy), std::move(jac_copy));
198  }
199 
202  static std::vector<AutoDiffBlock> variables(const std::vector<V>& initial_values)
203  {
204  const int num_vars = initial_values.size();
205  std::vector<int> bpat;
206  for (int v = 0; v < num_vars; ++v) {
207  bpat.push_back(initial_values[v].size());
208  }
209  std::vector<AutoDiffBlock> vars;
210  for (int v = 0; v < num_vars; ++v) {
211  vars.emplace_back(variable(v, initial_values[v], bpat));
212  }
213  return vars;
214  }
215 
218  {
219  if (jac_.empty()) {
220  jac_ = rhs.jac_;
221  } else if (!rhs.jac_.empty()) {
222  assert (numBlocks() == rhs.numBlocks());
223  assert (value().size() == rhs.value().size());
224 
225  const int num_blocks = numBlocks();
226 #pragma omp parallel for schedule(static)
227  for (int block = 0; block < num_blocks; ++block) {
228  assert(jac_[block].rows() == rhs.jac_[block].rows());
229  assert(jac_[block].cols() == rhs.jac_[block].cols());
230  jac_[block] += rhs.jac_[block];
231  }
232  }
233 
234  val_ += rhs.val_;
235 
236  return *this;
237  }
238 
241  {
242  if (jac_.empty()) {
243  const int num_blocks = rhs.numBlocks();
244  jac_.resize(num_blocks);
245 #pragma omp parallel for schedule(static)
246  for (int block = 0; block < num_blocks; ++block) {
247  jac_[block] = rhs.jac_[block] * (-1.0);
248  }
249  } else if (!rhs.jac_.empty()) {
250  assert (numBlocks() == rhs.numBlocks());
251  assert (value().size() == rhs.value().size());
252 
253  const int num_blocks = numBlocks();
254 #pragma omp parallel for schedule(static)
255  for (int block = 0; block < num_blocks; ++block) {
256  assert(jac_[block].rows() == rhs.jac_[block].rows());
257  assert(jac_[block].cols() == rhs.jac_[block].cols());
258  jac_[block] -= rhs.jac_[block];
259  }
260  }
261 
262  val_ -= rhs.val_;
263 
264  return *this;
265  }
266 
269  {
270  if (jac_.empty() && rhs.jac_.empty()) {
271  return constant(val_ + rhs.val_);
272  }
273  if (jac_.empty()) {
274  return val_ + rhs;
275  }
276  if (rhs.jac_.empty()) {
277  return *this + rhs.val_;
278  }
279  std::vector<M> jac = jac_;
280  assert(numBlocks() == rhs.numBlocks());
281  int num_blocks = numBlocks();
282 #pragma omp parallel for schedule(static)
283  for (int block = 0; block < num_blocks; ++block) {
284  assert(jac[block].rows() == rhs.jac_[block].rows());
285  assert(jac[block].cols() == rhs.jac_[block].cols());
286  jac[block] += rhs.jac_[block];
287  }
288  return function(val_ + rhs.val_, std::move(jac));
289  }
290 
293  {
294  if (jac_.empty() && rhs.jac_.empty()) {
295  return constant(val_ - rhs.val_);
296  }
297  if (jac_.empty()) {
298  return val_ - rhs;
299  }
300  if (rhs.jac_.empty()) {
301  return *this - rhs.val_;
302  }
303  std::vector<M> jac = jac_;
304  assert(numBlocks() == rhs.numBlocks());
305  int num_blocks = numBlocks();
306 #pragma omp parallel for schedule(static)
307  for (int block = 0; block < num_blocks; ++block) {
308  assert(jac[block].rows() == rhs.jac_[block].rows());
309  assert(jac[block].cols() == rhs.jac_[block].cols());
310  jac[block] -= rhs.jac_[block];
311  }
312  return function(val_ - rhs.val_, std::move(jac));
313  }
314 
317  {
318  if (jac_.empty() && rhs.jac_.empty()) {
319  return constant(val_ * rhs.val_);
320  }
321  if (jac_.empty()) {
322  return val_ * rhs;
323  }
324  if (rhs.jac_.empty()) {
325  return *this * rhs.val_;
326  }
327  int num_blocks = numBlocks();
328  std::vector<M> jac(num_blocks);
329  assert(numBlocks() == rhs.numBlocks());
330  M D1(val_.matrix().asDiagonal());
331  M D2(rhs.val_.matrix().asDiagonal());
332 #pragma omp parallel for schedule(dynamic)
333  for (int block = 0; block < num_blocks; ++block) {
334  assert(jac_[block].rows() == rhs.jac_[block].rows());
335  assert(jac_[block].cols() == rhs.jac_[block].cols());
336  if( jac_[block].nonZeros() == 0 && rhs.jac_[block].nonZeros() == 0 ) {
337  jac[block] = M( D2.rows(), jac_[block].cols() );
338  }
339  else if( jac_[block].nonZeros() == 0 )
340  jac[block] = D1*rhs.jac_[block];
341  else if ( rhs.jac_[block].nonZeros() == 0 ) {
342  jac[block] = D2*jac_[block];
343  }
344  else {
345  jac[block] = D2*jac_[block] + D1*rhs.jac_[block];
346  }
347  }
348  return function(val_ * rhs.val_, std::move(jac));
349  }
350 
353  {
354  if (jac_.empty() && rhs.jac_.empty()) {
355  return constant(val_ / rhs.val_);
356  }
357  if (jac_.empty()) {
358  return val_ / rhs;
359  }
360  if (rhs.jac_.empty()) {
361  return *this / rhs.val_;
362  }
363  int num_blocks = numBlocks();
364  std::vector<M> jac(num_blocks);
365  assert(numBlocks() == rhs.numBlocks());
366  M D1(val_.matrix().asDiagonal());
367  M D2(rhs.val_.matrix().asDiagonal());
368  M D3((1.0/(rhs.val_*rhs.val_)).matrix().asDiagonal());
369 #pragma omp parallel for schedule(dynamic)
370  for (int block = 0; block < num_blocks; ++block) {
371  assert(jac_[block].rows() == rhs.jac_[block].rows());
372  assert(jac_[block].cols() == rhs.jac_[block].cols());
373  if( jac_[block].nonZeros() == 0 && rhs.jac_[block].nonZeros() == 0 ) {
374  jac[block] = M( D3.rows(), jac_[block].cols() );
375  }
376  else if( jac_[block].nonZeros() == 0 ) {
377  jac[block] = D3 * ( D1*rhs.jac_[block]) * (-1.0);
378  }
379  else if ( rhs.jac_[block].nonZeros() == 0 )
380  {
381  jac[block] = D3 * (D2*jac_[block]);
382  }
383  else {
384  jac[block] = D3 * (D2*jac_[block] + (D1*rhs.jac_[block]*(-1.0)));
385  }
386  }
387  return function(val_ / rhs.val_, std::move(jac));
388  }
389 
391  template <class Ostream>
392  Ostream&
393  print(Ostream& os) const
394  {
395  int num_blocks = jac_.size();
396  os << "Value =\n" << val_ << "\n\nJacobian =\n";
397  for (int i = 0; i < num_blocks; ++i) {
398  Eigen::SparseMatrix<double> m;
399  jac_[i].toSparse(m);
400  os << "Sub Jacobian #" << i << '\n' << m << "\n";
401  }
402  return os;
403  }
404 
406  void swap(AutoDiffBlock& other)
407  {
408  val_.swap(other.val_);
409  jac_.swap(other.jac_);
410  }
411 
413  int size() const
414  {
415  return val_.size();
416  }
417 
419  int numBlocks() const
420  {
421  return jac_.size();
422  }
423 
425  std::vector<int> blockPattern() const
426  {
427  const int nb = numBlocks();
428  std::vector<int> bp(nb);
429  for (int block = 0; block < nb; ++block) {
430  bp[block] = jac_[block].cols();
431  }
432  return bp;
433  }
434 
436  const V& value() const
437  {
438  return val_;
439  }
440 
442  const std::vector<M>& derivative() const
443  {
444  return jac_;
445  }
446 
447  private:
448  AutoDiffBlock(const V& val)
449  : val_(val)
450  {
451  }
452 
453  AutoDiffBlock(V&& val)
454  : val_(std::move(val))
455  {
456  }
457 
458  AutoDiffBlock(V&& val, std::vector<M>&& jac)
459  : val_(std::move(val)), jac_(std::move(jac))
460  {
461 #ifndef NDEBUG
462  const int num_elem = val_.size();
463  const int num_blocks = jac_.size();
464  for (int block = 0; block < num_blocks; ++block) {
465  assert(num_elem == jac_[block].rows());
466  }
467 #endif
468  }
469 
470  V val_;
471  std::vector<M> jac_;
472  };
473 
474 
475  // --------- Free functions and operators for AutoDiffBlock ---------
476 
478  template <class Ostream, typename Scalar>
479  Ostream&
480  operator<<(Ostream& os, const AutoDiffBlock<Scalar>& fw)
481  {
482  return fw.print(os);
483  }
484 
485 
487  template <typename Scalar>
489  const AutoDiffBlock<Scalar>& rhs)
490  {
491  int num_blocks = rhs.numBlocks();
493  assert(lhs.cols() == rhs.value().rows());
494 #pragma omp parallel for schedule(dynamic)
495  for (int block = 0; block < num_blocks; ++block) {
496  fastSparseProduct(lhs, rhs.derivative()[block], jac[block]);
497  }
498  typename AutoDiffBlock<Scalar>::V val = lhs*rhs.value().matrix();
499  return AutoDiffBlock<Scalar>::function(std::move(val), std::move(jac));
500  }
501 
502 
504  template <typename Scalar>
505  AutoDiffBlock<Scalar> operator*(const Eigen::SparseMatrix<Scalar>& lhs,
506  const AutoDiffBlock<Scalar>& rhs)
507  {
508  int num_blocks = rhs.numBlocks();
510  assert(lhs.cols() == rhs.value().rows());
511  for (int block = 0; block < num_blocks; ++block) {
512  fastSparseProduct(lhs, rhs.derivative()[block], jac[block]);
513  }
514  typename AutoDiffBlock<Scalar>::V val = lhs*rhs.value().matrix();
515  return AutoDiffBlock<Scalar>::function(std::move(val), std::move(jac));
516  }
517 
518 
520  template <typename Scalar>
522  const AutoDiffBlock<Scalar>& rhs)
523  {
524  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) * rhs;
525  }
526 
527 
529  template <typename Scalar>
531  const typename AutoDiffBlock<Scalar>::V& rhs)
532  {
533  return rhs * lhs; // Commutative operation.
534  }
535 
536 
538  template <typename Scalar>
540  const AutoDiffBlock<Scalar>& rhs)
541  {
542  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) + rhs;
543  }
544 
545 
547  template <typename Scalar>
549  const typename AutoDiffBlock<Scalar>::V& rhs)
550  {
551  return rhs + lhs; // Commutative operation.
552  }
553 
554 
556  template <typename Scalar>
558  const AutoDiffBlock<Scalar>& rhs)
559  {
560  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) - rhs;
561  }
562 
563 
565  template <typename Scalar>
567  const typename AutoDiffBlock<Scalar>::V& rhs)
568  {
569  return lhs - AutoDiffBlock<Scalar>::constant(rhs, lhs.blockPattern());
570  }
571 
572 
574  template <typename Scalar>
576  const AutoDiffBlock<Scalar>& rhs)
577  {
578  return AutoDiffBlock<Scalar>::constant(lhs, rhs.blockPattern()) / rhs;
579  }
580 
581 
583  template <typename Scalar>
585  const typename AutoDiffBlock<Scalar>::V& rhs)
586  {
587  return lhs / AutoDiffBlock<Scalar>::constant(rhs, lhs.blockPattern());
588  }
589 
590 
598  template <typename Scalar>
600  const Scalar& rhs)
601  {
603  jac.reserve( lhs.numBlocks() );
604  for (int block=0; block<lhs.numBlocks(); block++) {
605  jac.emplace_back( lhs.derivative()[block] * rhs );
606  }
607  return AutoDiffBlock<Scalar>::function( lhs.value() * rhs, std::move(jac) );
608  }
609 
610 
618  template <typename Scalar>
619  AutoDiffBlock<Scalar> operator*(const Scalar& lhs,
620  const AutoDiffBlock<Scalar>& rhs)
621  {
622  return rhs * lhs; // Commutative operation.
623  }
624 
625 
626 } // namespace Opm
627 
628 
629 
630 #endif // OPM_AUTODIFFBLOCK_HEADER_INCLUDED
AutoDiff< Scalar > operator+(const AutoDiff< Scalar > &lhs, const AutoDiff< Scalar > &rhs)
Definition: AutoDiff.hpp:148
AutoDiff< Scalar > operator*(const AutoDiff< Scalar > &lhs, const AutoDiff< Scalar > &rhs)
Definition: AutoDiff.hpp:211
Eigen::Array< Scalar, Eigen::Dynamic, 1 > V
Underlying type for values.
Definition: AutoDiffBlock.hpp:98
static std::vector< AutoDiffBlock > variables(const std::vector< V > &initial_values)
Definition: AutoDiffBlock.hpp:202
static AutoDiffMatrix createIdentity(const int num_rows_cols)
Definition: AutoDiffMatrix.hpp:81
Ostream & print(Ostream &os) const
I/O.
Definition: AutoDiffBlock.hpp:393
Definition: AdditionalObjectDeleter.hpp:22
AutoDiffMatrix M
Underlying type for jacobians.
Definition: AutoDiffBlock.hpp:100
std::vector< int > blockPattern() const
Sizes (number of columns) of Jacobian blocks.
Definition: AutoDiffBlock.hpp:425
int cols() const
Definition: AutoDiffMatrix.hpp:563
static AutoDiffBlock null()
Construct an empty AutoDiffBlock.
Definition: AutoDiffBlock.hpp:103
static AutoDiffBlock constant(const V &val)
Definition: AutoDiffBlock.hpp:117
int numBlocks() const
Number of Jacobian blocks.
Definition: AutoDiffBlock.hpp:419
AutoDiffBlock operator/(const AutoDiffBlock &rhs) const
Elementwise operator /.
Definition: AutoDiffBlock.hpp:352
AutoDiffBlock & operator-=(const AutoDiffBlock &rhs)
Elementwise operator -=.
Definition: AutoDiffBlock.hpp:240
void fastSparseProduct(const AutoDiffMatrix &lhs, const AutoDiffMatrix &rhs, AutoDiffMatrix &res)
Definition: AutoDiffMatrix.hpp:696
AutoDiffBlock operator*(const AutoDiffBlock &rhs) const
Elementwise operator *.
Definition: AutoDiffBlock.hpp:316
int size() const
Number of elements.
Definition: AutoDiffBlock.hpp:413
AutoDiffBlock & operator+=(const AutoDiffBlock &rhs)
Elementwise operator +=.
Definition: AutoDiffBlock.hpp:217
AutoDiffBlock operator+(const AutoDiffBlock &rhs) const
Elementwise operator +.
Definition: AutoDiffBlock.hpp:268
STL namespace.
static AutoDiffBlock constant(V &&val)
Definition: AutoDiffBlock.hpp:110
AutoDiff< Scalar > operator/(const AutoDiff< Scalar > &lhs, const AutoDiff< Scalar > &rhs)
Definition: AutoDiff.hpp:244
Definition: AutoDiffMatrix.hpp:43
static AutoDiffBlock constant(const V &val, const std::vector< int > &blocksizes)
Definition: AutoDiffBlock.hpp:128
AutoDiff< Scalar > operator-(const AutoDiff< Scalar > &lhs, const AutoDiff< Scalar > &rhs)
Definition: AutoDiff.hpp:181
void swap(AutoDiffBlock &other)
Efficient swap function.
Definition: AutoDiffBlock.hpp:406
static AutoDiffBlock variable(const int index, const V &val, const std::vector< int > &blocksizes)
Definition: AutoDiffBlock.hpp:172
ADB::M M
Definition: BlackoilModelBase_impl.hpp:85
static AutoDiffBlock function(V &&val, std::vector< M > &&jac)
Definition: AutoDiffBlock.hpp:183
AutoDiffBlock operator-(const AutoDiffBlock &rhs) const
Elementwise operator -.
Definition: AutoDiffBlock.hpp:292
Definition: AutoDiffBlock.hpp:94
static AutoDiffBlock variable(const int index, V &&val, const std::vector< int > &blocksizes)
Definition: AutoDiffBlock.hpp:149
const V & value() const
Function value.
Definition: AutoDiffBlock.hpp:436
const std::vector< M > & derivative() const
Function derivatives.
Definition: AutoDiffBlock.hpp:442