Reference documentation for deal.II version 9.1.0-pre
ad_drivers.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2017 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_differentiation_ad_ad_drivers_h
17 #define dealii_differentiation_ad_ad_drivers_h
18 
19 #include <deal.II/base/config.h>
20 
21 #include <deal.II/base/exceptions.h>
22 #include <deal.II/base/types.h>
23 #include <deal.II/base/utilities.h>
24 
25 #include <deal.II/differentiation/ad/ad_number_traits.h>
26 #include <deal.II/differentiation/ad/ad_number_types.h>
27 #include <deal.II/differentiation/ad/adolc_number_types.h>
28 #include <deal.II/differentiation/ad/sacado_number_types.h>
29 
30 #include <deal.II/lac/full_matrix.h>
31 #include <deal.II/lac/vector.h>
32 
33 #ifdef DEAL_II_WITH_ADOLC
34 
35 DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
36 # include <adolc/drivers/drivers.h>
37 # include <adolc/internal/usrparms.h>
38 # include <adolc/taping.h>
39 DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
40 
41 #endif // DEAL_II_WITH_ADOLC
42 
43 #include <vector>
44 
45 
46 DEAL_II_NAMESPACE_OPEN
47 
48 
49 namespace Differentiation
50 {
51  namespace AD
52  {
57 
64  "This function is called in a class that is expected to be specialized "
65  "for auto-differentiable numbers.");
66 
72  "This function is only available if deal.II is compiled with ADOL-C.");
73 
84  std::size_t,
85  std::size_t,
86  << "The number of derivative levels that this auto-differentiable number type supports is "
87  << arg1
88  << ", but to perform the intended operation the number must support at least "
89  << arg2 << " levels.");
90 
92 
93 
98  namespace types
99  {
104  using tape_index = unsigned short;
105 
109  using tape_buffer_sizes = unsigned int;
110 
121 
125 #ifdef DEAL_II_WITH_ADOLC
126  // Note: This value is a limitation of ADOL-C, and not something that we
127  // have control over. See test adolc/helper_tape_index_01.cc for
128  // verification that we cannot use or exceed this value. This value is
129  // defined as TBUFNUM; see
130  // https://gitlab.com/adol-c/adol-c/blob/master/ADOL-C/include/adolc/internal/usrparms.h#L34
131  static const types::tape_index max_tape_index = TBUFNUM;
132 #else
133  static const types::tape_index max_tape_index =
134  std::numeric_limits<types::tape_index>::max();
135 #endif // DEAL_II_WITH_ADOLC
136  } // namespace types
137 
155  template <typename ADNumberType, typename ScalarType, typename T = void>
157  {
158  // This dummy class definition safely supports compilation
159  // against tapeless numbers or taped number types that have
160  // not yet been implemented.
161 
166 
175  static void
176  enable_taping(const types::tape_index tape_index,
177  const bool keep_independent_values);
178 
192  static void
193  enable_taping(const types::tape_index tape_index,
194  const bool keep_independent_values,
195  const types::tape_buffer_sizes obufsize,
196  const types::tape_buffer_sizes lbufsize,
197  const types::tape_buffer_sizes vbufsize,
198  const types::tape_buffer_sizes tbufsize);
199 
208  static void
209  disable_taping(const types::tape_index active_tape_index,
210  const bool write_tapes_to_file);
211 
219  static void
220  print_tape_stats(std::ostream & stream,
221  const types::tape_index tape_index);
222 
224 
229 
240  static ScalarType
241  value(const types::tape_index active_tape_index,
242  const std::vector<ScalarType> &independent_variables);
243 
257  static void
258  gradient(const types::tape_index active_tape_index,
259  const std::vector<ScalarType> &independent_variables,
260  Vector<ScalarType> & gradient);
261 
275  static void
276  hessian(const types::tape_index active_tape_index,
277  const std::vector<ScalarType> &independent_variables,
278  FullMatrix<ScalarType> & hessian);
279 
281 
286 
299  static void
300  values(const types::tape_index active_tape_index,
301  const unsigned int n_dependent_variables,
302  const std::vector<ScalarType> &independent_variables,
303  Vector<ScalarType> & values);
304 
323  static void
324  jacobian(const types::tape_index active_tape_index,
325  const unsigned int n_dependent_variables,
326  const std::vector<ScalarType> &independent_variables,
327  FullMatrix<ScalarType> & jacobian);
328 
330  };
331 
332 
333 
351  template <typename ADNumberType, typename ScalarType, typename T = void>
353  {
354  // This dummy class definition safely supports compilation
355  // against taped numbers or tapeless number types that have
356  // not yet been implemented.
357 
362 
380  static void
381  initialize_global_environment(const unsigned int n_independent_variables);
382 
384 
389 
398  static ScalarType
399  value(const std::vector<ADNumberType> &dependent_variables);
400 
414  static void
415  gradient(const std::vector<ADNumberType> &independent_variables,
416  const std::vector<ADNumberType> &dependent_variables,
417  Vector<ScalarType> & gradient);
418 
432  static void
433  hessian(const std::vector<ADNumberType> &independent_variables,
434  const std::vector<ADNumberType> &dependent_variables,
435  FullMatrix<ScalarType> & hessian);
436 
438 
443 
453  static void
454  values(const std::vector<ADNumberType> &dependent_variables,
455  Vector<ScalarType> & values);
456 
474  static void
475  jacobian(const std::vector<ADNumberType> &independent_variables,
476  const std::vector<ADNumberType> &dependent_variables,
477  FullMatrix<ScalarType> & jacobian);
478 
480  };
481 
482  } // namespace AD
483 } // namespace Differentiation
484 
485 
486 
487 /* --------------------- inline and template functions --------------------- */
488 
489 
490 #ifndef DOXYGEN
491 
492 namespace Differentiation
493 {
494  namespace AD
495  {
496  // ------------- TapedDrivers -------------
497 
498 
499  template <typename ADNumberType, typename ScalarType, typename T>
500  void
502  const types::tape_index,
503  const bool)
504  {
506  }
507 
508 
509  template <typename ADNumberType, typename ScalarType, typename T>
510  void
512  const types::tape_index,
513  const bool,
518  {
520  }
521 
522 
523  template <typename ADNumberType, typename ScalarType, typename T>
524  void
526  const types::tape_index,
527  const bool)
528  {
530  }
531 
532 
533  template <typename ADNumberType, typename ScalarType, typename T>
534  void
536  std::ostream &,
537  const types::tape_index)
538  {
540  }
541 
542 
543  template <typename ADNumberType, typename ScalarType, typename T>
544  ScalarType
546  const types::tape_index,
547  const std::vector<ScalarType> &)
548  {
550  return ScalarType(0.0);
551  }
552 
553 
554  template <typename ADNumberType, typename ScalarType, typename T>
555  void
557  const types::tape_index,
558  const std::vector<ScalarType> &,
559  Vector<ScalarType> &)
560  {
562  }
563 
564 
565  template <typename ADNumberType, typename ScalarType, typename T>
566  void
568  const types::tape_index,
569  const std::vector<ScalarType> &,
571  {
573  }
574 
575 
576  template <typename ADNumberType, typename ScalarType, typename T>
577  void
579  const types::tape_index,
580  const unsigned int,
581  const std::vector<ScalarType> &,
582  Vector<ScalarType> &)
583  {
585  }
586 
587 
588  template <typename ADNumberType, typename ScalarType, typename T>
589  void
591  const types::tape_index,
592  const unsigned int,
593  const std::vector<ScalarType> &,
595  {
597  }
598 
599 
600 # ifdef DEAL_II_WITH_ADOLC
601 
609  template <typename ADNumberType>
610  struct TapedDrivers<
611  ADNumberType,
612  double,
613  typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
614  NumberTypes::adolc_taped>::type>
615  {
616  using scalar_type = double;
617 
618  // === Taping ===
619 
620  static void
621  enable_taping(const types::tape_index tape_index,
622  const bool keep_independent_values)
623  {
624  trace_on(tape_index, keep_independent_values);
625  }
626 
627  static void
628  enable_taping(const types::tape_index tape_index,
629  const bool keep_independent_values,
630  const types::tape_buffer_sizes obufsize,
631  const types::tape_buffer_sizes lbufsize,
632  const types::tape_buffer_sizes vbufsize,
633  const types::tape_buffer_sizes tbufsize)
634  {
635  trace_on(tape_index,
636  keep_independent_values,
637  obufsize,
638  lbufsize,
639  vbufsize,
640  tbufsize);
641  }
642 
643  static void
644  disable_taping(const types::tape_index active_tape_index,
645  const bool write_tapes_to_file)
646  {
647  if (write_tapes_to_file)
648  trace_off(active_tape_index); // Slow
649  else
650  trace_off(); // Fast(er)
651  }
652 
653  static void
654  print_tape_stats(std::ostream &stream, const types::tape_index tape_index)
655  {
656  // See ADOL-C manual section 2.1
657  // and adolc/taping.h
658  std::vector<std::size_t> counts(STAT_SIZE);
659  ::tapestats(tape_index, counts.data());
660  Assert(counts.size() >= 18, ExcInternalError());
661  stream
662  << "Tape index: " << tape_index << "\n"
663  << "Number of independent variables: " << counts[0] << "\n"
664  << "Number of dependent variables: " << counts[1] << "\n"
665  << "Max number of live, active variables: " << counts[2] << "\n"
666  << "Size of taylor stack (number of overwrites): " << counts[3]
667  << "\n"
668  << "Operations buffer size: " << counts[4] << "\n"
669  << "Total number of recorded operations: " << counts[5] << "\n"
670  << "Operations file written or not: " << counts[6] << "\n"
671  << "Overall number of locations: " << counts[7] << "\n"
672  << "Locations file written or not: " << counts[8] << "\n"
673  << "Overall number of values: " << counts[9] << "\n"
674  << "Values file written or not: " << counts[10] << "\n"
675  << "Locations buffer size: " << counts[11] << "\n"
676  << "Values buffer size: " << counts[12] << "\n"
677  << "Taylor buffer size: " << counts[13] << "\n"
678  << "Number of eq_*_prod for sparsity pattern: " << counts[14] << "\n"
679  << "Use of 'min_op', deferred to 'abs_op' for piecewise calculations: "
680  << counts[15] << "\n"
681  << "Number of 'abs' calls that can switch branch: " << counts[16]
682  << "\n"
683  << "Number of parameters (doubles) interchangable without retaping: "
684  << counts[17] << "\n"
685  << std::flush;
686  }
687 
688 
689  // === Scalar drivers ===
690 
691  static scalar_type
692  value(const types::tape_index active_tape_index,
693  const std::vector<scalar_type> &independent_variables)
694  {
695  scalar_type value = 0.0;
696 
697  ::function(active_tape_index,
698  1, // Only one dependent variable
699  independent_variables.size(),
700  const_cast<double *>(independent_variables.data()),
701  &value);
702 
703  return value;
704  }
705 
706  static void
707  gradient(const types::tape_index active_tape_index,
708  const std::vector<scalar_type> &independent_variables,
709  Vector<scalar_type> & gradient)
710  {
711  Assert(
715  1));
716  Assert(gradient.size() == independent_variables.size(),
717  ExcDimensionMismatch(gradient.size(),
718  independent_variables.size()));
719 
720  // Note: ADOL-C's ::gradient function expects a *double as the last
721  // parameter. Here we take advantage of the fact that the data in the
722  // Vector class is aligned (e.g. stored as an Array)
723  ::gradient(active_tape_index,
724  independent_variables.size(),
725  const_cast<scalar_type *>(independent_variables.data()),
726  &gradient[0]);
727  }
728 
729  static void
730  hessian(const types::tape_index active_tape_index,
731  const std::vector<scalar_type> &independent_variables,
732  FullMatrix<scalar_type> & hessian)
733  {
734  Assert(
738  2));
739  Assert(hessian.m() == independent_variables.size(),
740  ExcDimensionMismatch(hessian.m(), independent_variables.size()));
741  Assert(hessian.n() == independent_variables.size(),
742  ExcDimensionMismatch(hessian.n(), independent_variables.size()));
743 
744  const unsigned int n_independent_variables =
745  independent_variables.size();
746  std::vector<scalar_type *> H(n_independent_variables);
747  for (unsigned int i = 0; i < n_independent_variables; ++i)
748  H[i] = &(hessian[i][0]);
749 
750  ::hessian(active_tape_index,
751  n_independent_variables,
752  const_cast<scalar_type *>(independent_variables.data()),
753  H.data());
754 
755  // ADOL-C builds only the lower-triangular part of the
756  // symmetric Hessian, so we should copy the relevant
757  // entries into the upper triangular part.
758  for (unsigned int i = 0; i < n_independent_variables; i++)
759  for (unsigned int j = 0; j < i; j++)
760  hessian[j][i] = hessian[i][j]; // Symmetry
761  }
762 
763  // === Vector drivers ===
764 
765  static void
766  values(const types::tape_index active_tape_index,
767  const unsigned int n_dependent_variables,
768  const std::vector<scalar_type> &independent_variables,
769  Vector<scalar_type> & values)
770  {
771  Assert(values.size() == n_dependent_variables,
772  ExcDimensionMismatch(values.size(), n_dependent_variables));
773 
774  // Note: ADOL-C's ::function function expects a *double as the last
775  // parameter. Here we take advantage of the fact that the data in the
776  // Vector class is aligned (e.g. stored as an Array)
777  ::function(active_tape_index,
778  n_dependent_variables,
779  independent_variables.size(),
780  const_cast<scalar_type *>(independent_variables.data()),
781  &values[0]);
782  }
783 
784  static void
785  jacobian(const types::tape_index active_tape_index,
786  const unsigned int n_dependent_variables,
787  const std::vector<scalar_type> &independent_variables,
788  FullMatrix<scalar_type> & jacobian)
789  {
790  Assert(
794  1));
795  Assert(jacobian.m() == n_dependent_variables,
796  ExcDimensionMismatch(jacobian.m(), n_dependent_variables));
797  Assert(jacobian.n() == independent_variables.size(),
798  ExcDimensionMismatch(jacobian.n(),
799  independent_variables.size()));
800 
801  std::vector<scalar_type *> J(n_dependent_variables);
802  for (unsigned int i = 0; i < n_dependent_variables; ++i)
803  J[i] = &(jacobian[i][0]);
804 
805  ::jacobian(active_tape_index,
806  n_dependent_variables,
807  independent_variables.size(),
808  independent_variables.data(),
809  J.data());
810  }
811  };
812 
813 # else // DEAL_II_WITH_ADOLC
814 
823  template <typename ADNumberType>
824  struct TapedDrivers<
825  ADNumberType,
826  double,
827  typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
828  NumberTypes::adolc_taped>::type>
829  {
830  using scalar_type = double;
831 
832  // === Taping ===
833 
834  static void
835  enable_taping(const types::tape_index, const bool)
836  {
837  AssertThrow(false, ExcRequiresAdolC());
838  }
839 
840  static void
841  enable_taping(const types::tape_index,
842  const bool,
847  {
848  AssertThrow(false, ExcRequiresAdolC());
849  }
850 
851  static void
852  disable_taping(const types::tape_index, const bool)
853  {
854  AssertThrow(false, ExcRequiresAdolC());
855  }
856 
857  static void
858  print_tape_stats(std::ostream &, const types::tape_index)
859  {
860  AssertThrow(false, ExcRequiresAdolC());
861  }
862 
863 
864  // === Scalar drivers ===
865 
866  static scalar_type
867  value(const types::tape_index, const std::vector<scalar_type> &)
868  {
869  AssertThrow(false, ExcRequiresAdolC());
870  return 0.0;
871  }
872 
873  static void
874  gradient(const types::tape_index,
875  const std::vector<scalar_type> &,
876  Vector<scalar_type> &)
877  {
878  AssertThrow(false, ExcRequiresAdolC());
879  }
880 
881  static void
882  hessian(const types::tape_index,
883  const std::vector<scalar_type> &,
885  {
886  AssertThrow(false, ExcRequiresAdolC());
887  }
888 
889  // === Vector drivers ===
890 
891  static void
892  values(const types::tape_index,
893  const unsigned int,
894  const std::vector<scalar_type> &,
895  Vector<scalar_type> &)
896  {
897  AssertThrow(false, ExcRequiresAdolC());
898  }
899 
900  static void
901  jacobian(const types::tape_index,
902  const unsigned int,
903  const std::vector<scalar_type> &,
905  {
906  AssertThrow(false, ExcRequiresAdolC());
907  }
908  };
909 
910 # endif // DEAL_II_WITH_ADOLC
911 
912 
921  template <typename ADNumberType>
922  struct TapedDrivers<
923  ADNumberType,
924  float,
925  typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
926  NumberTypes::adolc_taped>::type>
927  {
928  using scalar_type = float;
929 
930  // === Taping ===
931 
932  static void
933  enable_taping(const types::tape_index tape_index,
934  const bool keep_independent_values)
935  {
936  // ADOL-C only supports 'double', not 'float', so we can forward to
937  // the 'double' implementation of this function
939  tape_index, keep_independent_values);
940  }
941 
942  static void
943  enable_taping(const types::tape_index tape_index,
944  const bool keep_independent_values,
945  const types::tape_buffer_sizes obufsize,
946  const types::tape_buffer_sizes lbufsize,
947  const types::tape_buffer_sizes vbufsize,
948  const types::tape_buffer_sizes tbufsize)
949  {
950  // ADOL-C only supports 'double', not 'float', so we can forward to
951  // the 'double' implementation of this function
953  tape_index,
954  keep_independent_values,
955  obufsize,
956  lbufsize,
957  vbufsize,
958  tbufsize);
959  }
960 
961  static void
962  disable_taping(const types::tape_index active_tape_index,
963  const bool write_tapes_to_file)
964  {
965  // ADOL-C only supports 'double', not 'float', so we can forward to
966  // the 'double' implementation of this function
968  write_tapes_to_file);
969  }
970 
971  static void
972  print_tape_stats(std::ostream &stream, const types::tape_index tape_index)
973  {
974  // ADOL-C only supports 'double', not 'float', so we can forward to
975  // the 'double' implementation of this function
977  tape_index);
978  }
979 
980  // === Scalar drivers ===
981 
982  static scalar_type
983  value(const types::tape_index active_tape_index,
984  const std::vector<scalar_type> &independent_variables)
985  {
986  // ADOL-C only supports 'double', not 'float', so we can forward to
987  // the 'double' implementation of this function
989  active_tape_index, vector_float_to_double(independent_variables));
990  }
991 
992  static void
993  gradient(const types::tape_index active_tape_index,
994  const std::vector<scalar_type> &independent_variables,
995  Vector<scalar_type> & gradient)
996  {
997  Vector<double> gradient_double(gradient.size());
998  // ADOL-C only supports 'double', not 'float', so we can forward to
999  // the 'double' implementation of this function
1001  vector_float_to_double(
1002  independent_variables),
1003  gradient_double);
1004  gradient = gradient_double;
1005  }
1006 
1007  static void
1008  hessian(const types::tape_index active_tape_index,
1009  const std::vector<scalar_type> &independent_variables,
1010  FullMatrix<scalar_type> & hessian)
1011  {
1012  FullMatrix<double> hessian_double(hessian.m(), hessian.n());
1013  // ADOL-C only supports 'double', not 'float', so we can forward to
1014  // the 'double' implementation of this function
1016  vector_float_to_double(
1017  independent_variables),
1018  hessian_double);
1019  hessian = hessian_double;
1020  }
1021 
1022  // === Vector drivers ===
1023 
1024  static void
1025  values(const types::tape_index active_tape_index,
1026  const unsigned int n_dependent_variables,
1027  const std::vector<scalar_type> &independent_variables,
1028  Vector<scalar_type> & values)
1029  {
1030  Vector<double> values_double(values.size());
1031  // ADOL-C only supports 'double', not 'float', so we can forward to
1032  // the 'double' implementation of this function
1034  n_dependent_variables,
1035  vector_float_to_double(
1036  independent_variables),
1037  values_double);
1038  values = values_double;
1039  }
1040 
1041  static void
1042  jacobian(const types::tape_index active_tape_index,
1043  const unsigned int n_dependent_variables,
1044  const std::vector<scalar_type> &independent_variables,
1045  FullMatrix<scalar_type> & jacobian)
1046  {
1047  FullMatrix<double> jacobian_double(jacobian.m(), jacobian.n());
1048  // ADOL-C only supports 'double', not 'float', so we can forward to
1049  // the 'double' implementation of this function
1051  n_dependent_variables,
1052  vector_float_to_double(
1053  independent_variables),
1054  jacobian_double);
1055  jacobian = jacobian_double;
1056  }
1057 
1058  private:
1062  static std::vector<double>
1063  vector_float_to_double(const std::vector<float> &in)
1064  {
1065  std::vector<double> out(in.size());
1066  std::copy(in.begin(), in.end(), out.begin());
1067  return out;
1068  }
1069  };
1070 
1071 
1072  // ------------- TapelessDrivers -------------
1073 
1074 
1075  template <typename ADNumberType, typename ScalarType, typename T>
1076  void
1078  const unsigned int)
1079  {
1081  }
1082 
1083 
1084  template <typename ADNumberType, typename ScalarType, typename T>
1085  ScalarType
1087  const std::vector<ADNumberType> &)
1088  {
1090  return ScalarType(0.0);
1091  }
1092 
1093 
1094  template <typename ADNumberType, typename ScalarType, typename T>
1095  void
1097  const std::vector<ADNumberType> &,
1098  const std::vector<ADNumberType> &,
1099  Vector<ScalarType> &)
1100  {
1102  }
1103 
1104 
1105  template <typename ADNumberType, typename ScalarType, typename T>
1106  void
1108  const std::vector<ADNumberType> &,
1109  const std::vector<ADNumberType> &,
1111  {
1113  }
1114 
1115 
1116  template <typename ADNumberType, typename ScalarType, typename T>
1117  void
1119  const std::vector<ADNumberType> &,
1120  Vector<ScalarType> &)
1121  {
1123  }
1124 
1125 
1126  template <typename ADNumberType, typename ScalarType, typename T>
1127  void
1129  const std::vector<ADNumberType> &,
1130  const std::vector<ADNumberType> &,
1132  {
1134  }
1135 
1136 
1137  namespace internal
1138  {
1143  template <typename ADNumberType>
1144  static
1145  typename std::enable_if<!(ADNumberTraits<ADNumberType>::type_code ==
1149  reverse_mode_dependent_variable_activation(ADNumberType &)
1150  {}
1151 
1152 # ifdef DEAL_II_TRILINOS_WITH_SACADO
1153 
1154 
1163  template <typename ADNumberType>
1164  static typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
1168  reverse_mode_dependent_variable_activation(
1169  ADNumberType &dependent_variable)
1170  {
1171  // Compute all gradients (adjoints) for this
1172  // reverse-mode Sacado dependent variable.
1173  // For reverse-mode Sacado numbers it is necessary to broadcast to
1174  // all independent variables that it is time to compute gradients.
1175  // For one dependent variable one would just need to call
1176  // ADNumberType::Gradcomp(), but since we have a more
1177  // generic implementation for vectors of dependent variables
1178  // (vector mode) we default to the complex case.
1179  ADNumberType::Outvar_Gradcomp(dependent_variable);
1180  }
1181 
1182 # endif
1183 
1184 
1189  template <typename ADNumberType>
1190  static
1191  typename std::enable_if<!(ADNumberTraits<ADNumberType>::type_code ==
1193  configure_tapeless_mode(const unsigned int)
1194  {}
1195 
1196 
1197 # ifdef DEAL_II_WITH_ADOLC
1198 
1199 
1207  template <typename ADNumberType>
1208  static typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
1210  configure_tapeless_mode(const unsigned int n_directional_derivatives)
1211  {
1212 # ifdef DEAL_II_ADOLC_WITH_TAPELESS_REFCOUNTING
1213  // See ADOL-C manual section 7.1
1214  //
1215  // NOTE: It is critical that this is done for tapeless mode BEFORE
1216  // any adtl::adouble are created. If this is not done, then we see
1217  // this scary warning:
1218  //
1219  // "
1220  // ADOL-C Warning: Tapeless: Setting numDir could change memory
1221  // allocation of derivatives in existing adoubles and may lead to
1222  // erroneous results or memory corruption
1223  // "
1224  //
1225  // So we use this dummy function to configure this setting before
1226  // we create and initialize our class data
1227  const std::size_t n_live_variables = adtl::refcounter::getNumLiveVar();
1228  if (n_live_variables == 0)
1229  {
1230  adtl::setNumDir(n_directional_derivatives);
1231  }
1232  else
1233  {
1234  // So there are some live active variables floating around. Here we
1235  // check if we ask to increase the number of computable
1236  // directional derivatives. If this really is necessary then it's
1237  // absolutely vital that there exist no live variables before doing
1238  // so.
1239  const std::size_t n_set_directional_derivatives = adtl::getNumDir();
1240  if (n_directional_derivatives > n_set_directional_derivatives)
1241  AssertThrow(
1242  n_live_variables == 0,
1243  ExcMessage(
1244  "There are currently " +
1245  Utilities::to_string(n_live_variables) +
1246  " live "
1247  "adtl::adouble variables in existence. They currently "
1248  "assume " +
1249  Utilities::to_string(n_set_directional_derivatives) +
1250  " directional derivatives "
1251  "but you wish to increase this to " +
1252  Utilities::to_string(n_directional_derivatives) +
1253  ". \n"
1254  "To safely change (or more specifically in this case, "
1255  "increase) the number of directional derivatives, there "
1256  "must be no tapeless doubles in local/global scope."));
1257  }
1258 # else
1259  // If ADOL-C is not configured with tapeless number reference counting
1260  // then there is no way that we can guarentee that the following call
1261  // is safe. No comment... :-/
1262  adtl::setNumDir(n_directional_derivatives);
1263 # endif
1264  }
1265 
1266 # else // DEAL_II_WITH_ADOLC
1267 
1268  template <typename ADNumberType>
1269  static typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
1271  configure_tapeless_mode(const unsigned int n_directional_derivatives)
1272  {
1273  AssertThrow(false, ExcRequiresAdolC());
1274  }
1275 
1276 # endif
1277 
1278  } // namespace internal
1279 
1280 
1286  template <typename ADNumberType, typename ScalarType>
1287  struct TapelessDrivers<
1288  ADNumberType,
1289  ScalarType,
1290  typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
1291  NumberTypes::sacado_rad ||
1292  ADNumberTraits<ADNumberType>::type_code ==
1293  NumberTypes::sacado_rad_dfad>::type>
1294  {
1295  // === Configuration ===
1296 
1297  static void
1298  initialize_global_environment(const unsigned int n_independent_variables)
1299  {
1300  internal::configure_tapeless_mode<ADNumberType>(
1301  n_independent_variables);
1302  }
1303 
1304  // === Scalar drivers ===
1305 
1306  static ScalarType
1307  value(const std::vector<ADNumberType> &dependent_variables)
1308  {
1309  Assert(dependent_variables.size() == 1,
1310  ExcDimensionMismatch(dependent_variables.size(), 1));
1312  dependent_variables[0]);
1313  }
1314 
1315  static void
1316  gradient(const std::vector<ADNumberType> &independent_variables,
1317  const std::vector<ADNumberType> &dependent_variables,
1318  Vector<ScalarType> & gradient)
1319  {
1320  Assert(
1324  1));
1325  Assert(dependent_variables.size() == 1,
1326  ExcDimensionMismatch(dependent_variables.size(), 1));
1327  Assert(gradient.size() == independent_variables.size(),
1328  ExcDimensionMismatch(gradient.size(),
1329  independent_variables.size()));
1330 
1331  // In reverse mode, the gradients are computed from the
1332  // independent variables (i.e. the adjoint)
1333  internal::reverse_mode_dependent_variable_activation(
1334  const_cast<ADNumberType &>(dependent_variables[0]));
1335  const std::size_t n_independent_variables =
1336  independent_variables.size();
1337  for (unsigned int i = 0; i < n_independent_variables; i++)
1340  independent_variables[i],
1341  0 /*This number doesn't really matter*/));
1342  }
1343 
1344  static void
1345  hessian(const std::vector<ADNumberType> &independent_variables,
1346  const std::vector<ADNumberType> &dependent_variables,
1347  FullMatrix<ScalarType> & hessian)
1348  {
1349  Assert(
1353  2));
1354  Assert(dependent_variables.size() == 1,
1355  ExcDimensionMismatch(dependent_variables.size(), 1));
1356  Assert(hessian.m() == independent_variables.size(),
1357  ExcDimensionMismatch(hessian.m(), independent_variables.size()));
1358  Assert(hessian.n() == independent_variables.size(),
1359  ExcDimensionMismatch(hessian.n(), independent_variables.size()));
1360 
1361  // In reverse mode, the gradients are computed from the
1362  // independent variables (i.e. the adjoint)
1363  internal::reverse_mode_dependent_variable_activation(
1364  const_cast<ADNumberType &>(dependent_variables[0]));
1365  const std::size_t n_independent_variables =
1366  independent_variables.size();
1367  for (unsigned int i = 0; i < n_independent_variables; i++)
1368  {
1369  using derivative_type =
1371  const derivative_type gradient_i =
1373  independent_variables[i], i);
1374 
1375  for (unsigned int j = 0; j <= i; ++j) // Symmetry
1376  {
1377  // Extract higher-order directional derivatives. Depending on
1378  // the AD number type, the result may be another AD number or a
1379  // floating point value.
1380  const ScalarType hessian_ij =
1383  gradient_i, j));
1384  hessian[i][j] = hessian_ij;
1385  if (i != j)
1386  hessian[j][i] = hessian_ij; // Symmetry
1387  }
1388  }
1389  }
1390 
1391  // === Vector drivers ===
1392 
1393  static void
1394  values(const std::vector<ADNumberType> &dependent_variables,
1395  Vector<ScalarType> & values)
1396  {
1397  Assert(values.size() == dependent_variables.size(),
1398  ExcDimensionMismatch(values.size(), dependent_variables.size()));
1399 
1400  const std::size_t n_dependent_variables = dependent_variables.size();
1401  for (unsigned int i = 0; i < n_dependent_variables; i++)
1403  dependent_variables[i]);
1404  }
1405 
1406  static void
1407  jacobian(const std::vector<ADNumberType> &independent_variables,
1408  const std::vector<ADNumberType> &dependent_variables,
1409  FullMatrix<ScalarType> & jacobian)
1410  {
1411  Assert(
1415  1));
1416  Assert(jacobian.m() == dependent_variables.size(),
1417  ExcDimensionMismatch(jacobian.m(), dependent_variables.size()));
1418  Assert(jacobian.n() == independent_variables.size(),
1419  ExcDimensionMismatch(jacobian.n(),
1420  independent_variables.size()));
1421 
1422  const std::size_t n_independent_variables =
1423  independent_variables.size();
1424  const std::size_t n_dependent_variables = dependent_variables.size();
1425 
1426  // In reverse mode, the gradients are computed from the
1427  // independent variables (i.e. the adjoint).
1428  // For a demonstration of why this accumulation process is
1429  // required, see the unit tests
1430  // sacado/basic_01b.cc and sacado/basic_02b.cc
1431  // Here we also take into consideration the derivative type:
1432  // The Sacado number may be of the nested variety, in which
1433  // case the effect of the accumulation process on the
1434  // sensitivities of the nested number need to be accounted for.
1435  using accumulation_type =
1437  std::vector<accumulation_type> rad_accumulation(
1438  n_independent_variables,
1440 
1441  for (unsigned int i = 0; i < n_dependent_variables; i++)
1442  {
1443  internal::reverse_mode_dependent_variable_activation(
1444  const_cast<ADNumberType &>(dependent_variables[i]));
1445  for (unsigned int j = 0; j < n_independent_variables; j++)
1446  {
1447  const accumulation_type df_i_dx_j =
1449  independent_variables[j],
1450  i /*This number doesn't really matter*/) -
1451  rad_accumulation[j];
1452  jacobian[i][j] =
1454  rad_accumulation[j] += df_i_dx_j;
1455  }
1456  }
1457  }
1458  };
1459 
1460 
1466  template <typename ADNumberType, typename ScalarType>
1467  struct TapelessDrivers<
1468  ADNumberType,
1469  ScalarType,
1470  typename std::enable_if<ADNumberTraits<ADNumberType>::type_code ==
1471  NumberTypes::adolc_tapeless ||
1472  ADNumberTraits<ADNumberType>::type_code ==
1473  NumberTypes::sacado_dfad ||
1474  ADNumberTraits<ADNumberType>::type_code ==
1475  NumberTypes::sacado_dfad_dfad>::type>
1476  {
1477  // === Configuration ===
1478 
1479  static void
1480  initialize_global_environment(const unsigned int n_independent_variables)
1481  {
1482  internal::configure_tapeless_mode<ADNumberType>(
1483  n_independent_variables);
1484  }
1485 
1486  // === Scalar drivers ===
1487 
1488  static ScalarType
1489  value(const std::vector<ADNumberType> &dependent_variables)
1490  {
1491  Assert(dependent_variables.size() == 1,
1492  ExcDimensionMismatch(dependent_variables.size(), 1));
1494  dependent_variables[0]);
1495  }
1496 
1497  static void
1498  gradient(const std::vector<ADNumberType> &independent_variables,
1499  const std::vector<ADNumberType> &dependent_variables,
1500  Vector<ScalarType> & gradient)
1501  {
1502  Assert(
1506  1));
1507  Assert(dependent_variables.size() == 1,
1508  ExcDimensionMismatch(dependent_variables.size(), 1));
1509  Assert(gradient.size() == independent_variables.size(),
1510  ExcDimensionMismatch(gradient.size(),
1511  independent_variables.size()));
1512 
1513  // In forward mode, the gradients are computed from the
1514  // dependent variables
1515  const std::size_t n_independent_variables =
1516  independent_variables.size();
1517  for (unsigned int i = 0; i < n_independent_variables; i++)
1520  dependent_variables[0], i));
1521  }
1522 
1523  static void
1524  hessian(const std::vector<ADNumberType> &independent_variables,
1525  const std::vector<ADNumberType> &dependent_variables,
1526  FullMatrix<ScalarType> & hessian)
1527  {
1528  Assert(
1532  2));
1533  Assert(dependent_variables.size() == 1,
1534  ExcDimensionMismatch(dependent_variables.size(), 1));
1535  Assert(hessian.m() == independent_variables.size(),
1536  ExcDimensionMismatch(hessian.m(), independent_variables.size()));
1537  Assert(hessian.n() == independent_variables.size(),
1538  ExcDimensionMismatch(hessian.n(), independent_variables.size()));
1539 
1540  // In forward mode, the gradients are computed from the
1541  // dependent variables
1542  const std::size_t n_independent_variables =
1543  independent_variables.size();
1544  for (unsigned int i = 0; i < n_independent_variables; i++)
1545  {
1546  using derivative_type =
1548  const derivative_type gradient_i =
1550  dependent_variables[0], i);
1551 
1552  for (unsigned int j = 0; j <= i; ++j) // Symmetry
1553  {
1554  // Extract higher-order directional derivatives. Depending on
1555  // the AD number type, the result may be another AD number or a
1556  // floating point value.
1557  const ScalarType hessian_ij =
1560  gradient_i, j));
1561  hessian[i][j] = hessian_ij;
1562  if (i != j)
1563  hessian[j][i] = hessian_ij; // Symmetry
1564  }
1565  }
1566  }
1567 
1568  // === Vector drivers ===
1569 
1570  static void
1571  values(const std::vector<ADNumberType> &dependent_variables,
1572  Vector<ScalarType> & values)
1573  {
1574  Assert(values.size() == dependent_variables.size(),
1575  ExcDimensionMismatch(values.size(), dependent_variables.size()));
1576 
1577  const std::size_t n_dependent_variables = dependent_variables.size();
1578  for (unsigned int i = 0; i < n_dependent_variables; i++)
1580  dependent_variables[i]);
1581  }
1582 
1583  static void
1584  jacobian(const std::vector<ADNumberType> &independent_variables,
1585  const std::vector<ADNumberType> &dependent_variables,
1586  FullMatrix<ScalarType> & jacobian)
1587  {
1588  Assert(
1592  1));
1593  Assert(jacobian.m() == dependent_variables.size(),
1594  ExcDimensionMismatch(jacobian.m(), dependent_variables.size()));
1595  Assert(jacobian.n() == independent_variables.size(),
1596  ExcDimensionMismatch(jacobian.n(),
1597  independent_variables.size()));
1598 
1599  const std::size_t n_independent_variables =
1600  independent_variables.size();
1601  const std::size_t n_dependent_variables = dependent_variables.size();
1602 
1603  // In forward mode, the gradients are computed from the
1604  // dependent variables
1605  for (unsigned int i = 0; i < n_dependent_variables; i++)
1606  for (unsigned int j = 0; j < n_independent_variables; j++)
1607  jacobian[i][j] = internal::NumberType<ScalarType>::value(
1609  dependent_variables[i], j));
1610  }
1611  };
1612 
1613  } // namespace AD
1614 } // namespace Differentiation
1615 
1616 
1617 #endif // DOXYGEN
1618 
1619 DEAL_II_NAMESPACE_CLOSE
1620 
1621 
1622 #endif
static::ExceptionBase & ExcRequiresADNumberSpecialization()
#define DeclException2(Exception2, type1, type2, outsequence)
Definition: exceptions.h:420
static ScalarType value(const std::vector< ADNumberType > &dependent_variables)
static::ExceptionBase & ExcSupportedDerivativeLevels(std::size_t arg1, std::size_t arg2)
static void enable_taping(const types::tape_index tape_index, const bool keep_independent_values)
static void jacobian(const types::tape_index active_tape_index, const unsigned int n_dependent_variables, const std::vector< ScalarType > &independent_variables, FullMatrix< ScalarType > &jacobian)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
std::string to_string(const number value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:105
static void print_tape_stats(std::ostream &stream, const types::tape_index tape_index)
static::ExceptionBase & ExcMessage(std::string arg1)
size_type n() const
Definition: types.h:31
static void hessian(const std::vector< ADNumberType > &independent_variables, const std::vector< ADNumberType > &dependent_variables, FullMatrix< ScalarType > &hessian)
#define Assert(cond, exc)
Definition: exceptions.h:1227
static::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:397
static void hessian(const types::tape_index active_tape_index, const std::vector< ScalarType > &independent_variables, FullMatrix< ScalarType > &hessian)
static void jacobian(const std::vector< ADNumberType > &independent_variables, const std::vector< ADNumberType > &dependent_variables, FullMatrix< ScalarType > &jacobian)
static void initialize_global_environment(const unsigned int n_independent_variables)
static::ExceptionBase & ExcRequiresAdolC()
size_type m() const
static const types::tape_index max_tape_index
Definition: ad_drivers.h:131
static void values(const std::vector< ADNumberType > &dependent_variables, Vector< ScalarType > &values)
static ScalarType value(const types::tape_index active_tape_index, const std::vector< ScalarType > &independent_variables)
static void values(const types::tape_index active_tape_index, const unsigned int n_dependent_variables, const std::vector< ScalarType > &independent_variables, Vector< ScalarType > &values)
static const types::tape_index invalid_tape_index
Definition: ad_drivers.h:120
static void disable_taping(const types::tape_index active_tape_index, const bool write_tapes_to_file)
unsigned short tape_index
Definition: ad_drivers.h:104
static void gradient(const std::vector< ADNumberType > &independent_variables, const std::vector< ADNumberType > &dependent_variables, Vector< ScalarType > &gradient)
unsigned int tape_buffer_sizes
Definition: ad_drivers.h:109
static::ExceptionBase & ExcInternalError()
static void gradient(const types::tape_index active_tape_index, const std::vector< ScalarType > &independent_variables, Vector< ScalarType > &gradient)