deal.II version GIT relicensing-6839-g338455934c 2026-10-02 12:10:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
symengine_number_visitor_internal.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2020 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_differentiation_sd_symengine_number_visitor_internal_h
14#define dealii_differentiation_sd_symengine_number_visitor_internal_h
15
16#include <deal.II/base/config.h>
17
18#ifdef DEAL_II_WITH_SYMENGINE
19
21# include <deal.II/base/numbers.h>
22
25
26# include <boost/serialization/split_member.hpp>
27
28# include <symengine/basic.h>
29# include <symengine/dict.h>
30# include <symengine/symengine_exception.h>
31# include <symengine/symengine_rcp.h>
32# include <symengine/visitor.h>
33
34#endif // DEAL_II_WITH_SYMENGINE
35
37
38#ifdef DEAL_II_WITH_SYMENGINE
39namespace Differentiation
40{
41 namespace SD
42 {
43 namespace internal
44 {
52 template <typename ReturnType, typename ExpressionType>
54 {
56 std::vector<std::pair<SD::Expression, SD::Expression>>;
57
58 public:
59 /*
60 * Constructor.
61 */
63
64 /*
65 * Destructor.
66 */
67 virtual ~CSEDictionaryVisitor() = default;
68
81 void
82 init(const types::symbol_vector &dependent_functions);
83
121 void
122 call(ReturnType *output_values,
123 const types::symbol_vector &independent_symbols,
124 const ReturnType *substitution_values);
125
131 template <class Archive>
132 void
133 save(Archive &archive, const unsigned int version) const;
134
140 template <class Archive>
141 void
142 load(Archive &archive, const unsigned int version);
143
144# ifdef DOXYGEN
150 template <class Archive>
151 void
152 serialize(Archive &archive, const unsigned int version);
153# else
154 // This macro defines the serialize() method that is compatible with
155 // the templated save() and load() method that have been implemented.
156 BOOST_SERIALIZATION_SPLIT_MEMBER()
157# endif
158
164 template <typename StreamType>
165 void
166 print(StreamType &stream) const;
167
171 bool
172 executed() const;
173
178 unsigned int
180
184 unsigned int
186
187 protected:
199 void
200 init(const SymEngine::vec_basic &dependent_functions);
201
226 void
227 call(ReturnType *output_values,
228 const SymEngine::vec_basic &independent_symbols,
229 const ReturnType *substitution_values);
230
231 private:
232 // Note: It would be more efficient to store this data in native
233 // SymEngine types, as it would prevent some copying of the data
234 // structures. However, this makes serialization more difficult,
235 // so we use our own serializable types instead, and lose a bit
236 // of efficiency.
237
242
247 };
248
249
250
260 template <typename ReturnType, typename ExpressionType>
262 : public SymEngine::BaseVisitor<
263 DictionarySubstitutionVisitor<ReturnType, ExpressionType>>
264 {
265 public:
266 /*
267 * Constructor.
268 */
270
271 /*
272 * Destructor.
273 */
274 virtual ~DictionarySubstitutionVisitor() override = default;
275
299 void
301 const Expression &dependent_function,
302 const bool use_cse = false);
303
326 // The following definition is required due to base class CRTP.
327 void
328 init(const SymEngine::vec_basic &independent_symbols,
329 const SymEngine::Basic &dependent_function,
330 const bool use_cse = false);
331
352 void
355 const bool use_cse = false);
356
357
379 // The following definition is required due to base class CRTP.
380 void
381 init(const SymEngine::vec_basic &independent_symbols,
382 const SymEngine::vec_basic &dependent_functions,
383 const bool use_cse = false);
384
417 void
418 call(ReturnType *output_values, const ReturnType *substitution_values);
419
436 // The following definition is required due to base class CRTP.
437 ReturnType
438 call(const std::vector<ReturnType> &substitution_values);
439
445 template <class Archive>
446 void
447 save(Archive &archive, const unsigned int version) const;
448
454 template <class Archive>
455 void
456 load(Archive &archive, const unsigned int version);
457
458# ifdef DOXYGEN
464 template <class Archive>
465 void
466 serialize(Archive &archive, const unsigned int version);
467# else
468 // This macro defines the serialize() method that is compatible with
469 // the templated save() and load() method that have been implemented.
470 BOOST_SERIALIZATION_SPLIT_MEMBER()
471# endif
472
486 template <typename StreamType>
487 void
488 print(StreamType &stream,
489 const bool print_independent_symbols = false,
490 const bool print_dependent_functions = false,
491 const bool print_cse_reductions = false) const;
492
493# ifndef DOXYGEN
494 // The following definitions are required due to base class CRTP.
495 // Since these are not used, and therefore not important to
496 // understand, we'll define them in the most concise manner possible.
497 // We also won't bother to document their existence, since they cannot
498 // be used.
499# define IMPLEMENT_DSV_BVISIT(Argument) \
500 void bvisit(const Argument &) \
501 { \
502 AssertThrow(false, ExcNotImplemented()); \
503 }
504
505 IMPLEMENT_DSV_BVISIT(SymEngine::Basic)
506 IMPLEMENT_DSV_BVISIT(SymEngine::Symbol)
507 IMPLEMENT_DSV_BVISIT(SymEngine::Constant)
508 IMPLEMENT_DSV_BVISIT(SymEngine::Integer)
509 IMPLEMENT_DSV_BVISIT(SymEngine::Rational)
510 IMPLEMENT_DSV_BVISIT(SymEngine::RealDouble)
511 IMPLEMENT_DSV_BVISIT(SymEngine::ComplexDouble)
512 IMPLEMENT_DSV_BVISIT(SymEngine::Add)
513 IMPLEMENT_DSV_BVISIT(SymEngine::Mul)
514 IMPLEMENT_DSV_BVISIT(SymEngine::Pow)
515 IMPLEMENT_DSV_BVISIT(SymEngine::Log)
516 IMPLEMENT_DSV_BVISIT(SymEngine::Sin)
517 IMPLEMENT_DSV_BVISIT(SymEngine::Cos)
518 IMPLEMENT_DSV_BVISIT(SymEngine::Tan)
519 IMPLEMENT_DSV_BVISIT(SymEngine::Csc)
520 IMPLEMENT_DSV_BVISIT(SymEngine::Sec)
521 IMPLEMENT_DSV_BVISIT(SymEngine::Cot)
522 IMPLEMENT_DSV_BVISIT(SymEngine::ASin)
523 IMPLEMENT_DSV_BVISIT(SymEngine::ACos)
524 IMPLEMENT_DSV_BVISIT(SymEngine::ATan)
525 IMPLEMENT_DSV_BVISIT(SymEngine::ATan2)
526 IMPLEMENT_DSV_BVISIT(SymEngine::ACsc)
527 IMPLEMENT_DSV_BVISIT(SymEngine::ASec)
528 IMPLEMENT_DSV_BVISIT(SymEngine::ACot)
529 IMPLEMENT_DSV_BVISIT(SymEngine::Sinh)
530 IMPLEMENT_DSV_BVISIT(SymEngine::Cosh)
531 IMPLEMENT_DSV_BVISIT(SymEngine::Tanh)
532 IMPLEMENT_DSV_BVISIT(SymEngine::Csch)
533 IMPLEMENT_DSV_BVISIT(SymEngine::Sech)
534 IMPLEMENT_DSV_BVISIT(SymEngine::Coth)
535 IMPLEMENT_DSV_BVISIT(SymEngine::ASinh)
536 IMPLEMENT_DSV_BVISIT(SymEngine::ACosh)
537 IMPLEMENT_DSV_BVISIT(SymEngine::ATanh)
538 IMPLEMENT_DSV_BVISIT(SymEngine::ACsch)
539 IMPLEMENT_DSV_BVISIT(SymEngine::ACoth)
540 IMPLEMENT_DSV_BVISIT(SymEngine::ASech)
541 IMPLEMENT_DSV_BVISIT(SymEngine::Abs)
542 IMPLEMENT_DSV_BVISIT(SymEngine::Gamma)
543 IMPLEMENT_DSV_BVISIT(SymEngine::LogGamma)
544 IMPLEMENT_DSV_BVISIT(SymEngine::Erf)
545 IMPLEMENT_DSV_BVISIT(SymEngine::Erfc)
546 IMPLEMENT_DSV_BVISIT(SymEngine::Max)
547 IMPLEMENT_DSV_BVISIT(SymEngine::Min)
548
549# undef IMPLEMENT_DSV_BVISIT
550# endif // DOXYGEN
551
552 private:
553 // Note: It would be more efficient to store this data in native
554 // SymEngine types, as it would prevent some copying of the data
555 // structures. However, this makes serialization more difficult,
556 // so we use our own serializable types instead, and lose a bit
557 // of efficiency.
558
563
568
575 };
576
577
578
579 /* ------------------ inline and template functions ------------------ */
580
581
582# ifndef DOXYGEN
583
584 /* -------------- CommonSubexpressionEliminationVisitor -------------- */
585
586
587 template <typename ReturnType, typename ExpressionType>
588 void
590 const SD::types::symbol_vector &dependent_functions)
591 {
593 dependent_functions));
594 }
595
596
597
598 template <typename ReturnType, typename ExpressionType>
599 void
601 const SymEngine::vec_basic &dependent_functions)
602 {
603 // After the next call, the data stored in replacements is structured
604 // as follows:
605 //
606 // replacements[i] := [f, f(x)]
607 // replacements[i].first = intermediate function label "f"
608 // replacements[i].second = intermediate function definition "f(x)"
609 //
610 // It is to be evaluated top down (i.e. index 0 to
611 // replacements.size()), with the results going back into the
612 // substitution map for the next levels. So for each "i", "x" are the
613 // superset of the input values and the previously evaluated [f_0(x),
614 // f_1(x), ..., f_{i-1}(x)].
615 //
616 // The final result is a set of reduced expressions
617 // that must be computed after the replacement
618 // values have been computed.
619 SymEngine::vec_pair se_replacements;
620 SymEngine::vec_basic se_reduced_exprs;
621 SymEngine::cse(se_replacements, se_reduced_exprs, dependent_functions);
622
623 intermediate_symbols_exprs =
625 se_replacements);
627 se_reduced_exprs);
628 }
629
630
631
632 template <typename ReturnType, typename ExpressionType>
633 void
635 ReturnType *output_values,
636 const SD::types::symbol_vector &independent_symbols,
637 const ReturnType *substitution_values)
638 {
639 call(output_values,
641 independent_symbols),
642 substitution_values);
643 }
644
645
646
647 template <typename ReturnType, typename ExpressionType>
648 void
650 ReturnType *output_values,
651 const SymEngine::vec_basic &independent_symbols,
652 const ReturnType *substitution_values)
653 {
654 Assert(n_reduced_expressions() > 0, ExcInternalError());
655
656 // First we add the input values into the substitution map...
657 SymEngine::map_basic_basic substitution_value_map;
658 for (unsigned i = 0; i < independent_symbols.size(); ++i)
659 substitution_value_map[independent_symbols[i]] =
660 static_cast<const SymEngine::RCP<const SymEngine::Basic> &>(
661 ExpressionType(substitution_values[i]));
662
663 // ... followed by any intermediate evaluations due to the application
664 // of CSE. These are fed directly back into the substitution map...
665 for (const auto &expression : intermediate_symbols_exprs)
666 {
667 const SymEngine::RCP<const SymEngine::Basic> &cse_symbol =
668 expression.first;
669 const SymEngine::RCP<const SymEngine::Basic> &cse_expr =
670 expression.second;
671 Assert(substitution_value_map.find(cse_symbol) ==
672 substitution_value_map.end(),
674 "Reduced symbol already appears in substitution map. "
675 "Is there a clash between the reduced symbol name and "
676 "the symbol used for an independent variable?"));
677 substitution_value_map[cse_symbol] =
678 static_cast<const SymEngine::RCP<const SymEngine::Basic> &>(
679 ExpressionType(ExpressionType(cse_expr)
680 .template substitute_and_evaluate<ReturnType>(
681 substitution_value_map)));
682 }
683
684 // ... followed by the final reduced expressions
685 for (unsigned i = 0; i < reduced_exprs.size(); ++i)
686 output_values[i] = ExpressionType(reduced_exprs[i])
687 .template substitute_and_evaluate<ReturnType>(
688 substitution_value_map);
689 }
690
691
692
693 template <typename ReturnType, typename ExpressionType>
694 template <class Archive>
695 void
697 Archive &ar,
698 const unsigned int /*version*/) const
699 {
700 // The reduced expressions depend on the intermediate expressions,
701 // so we serialize the latter before the former.
702 ar &intermediate_symbols_exprs;
703 ar &reduced_exprs;
704 }
705
706
707
708 template <typename ReturnType, typename ExpressionType>
709 template <class Archive>
710 void
712 Archive &ar,
713 const unsigned int /*version*/)
714 {
715 Assert(intermediate_symbols_exprs.empty(), ExcInternalError());
716 Assert(reduced_exprs.empty(), ExcInternalError());
717
718 // The reduced expressions depend on the intermediate expressions,
719 // so we deserialize the latter before the former.
720 ar &intermediate_symbols_exprs;
721 ar &reduced_exprs;
722 }
723
724
725
726 template <typename ReturnType, typename ExpressionType>
727 template <typename StreamType>
728 void
730 StreamType &stream) const
731 {
732 stream << "Common subexpression elimination: \n";
733 stream << " Intermediate reduced expressions: \n";
734 for (unsigned i = 0; i < intermediate_symbols_exprs.size(); ++i)
735 {
736 const SymEngine::RCP<const SymEngine::Basic> &cse_symbol =
737 intermediate_symbols_exprs[i].first;
738 const SymEngine::RCP<const SymEngine::Basic> &cse_expr =
739 intermediate_symbols_exprs[i].second;
740 stream << " " << i << ": " << cse_symbol << " = " << cse_expr
741 << '\n';
742 }
743
744 stream << " Final reduced expressions for dependent variables: \n";
745 for (unsigned i = 0; i < reduced_exprs.size(); ++i)
746 stream << " " << i << ": " << reduced_exprs[i] << '\n';
747
748 stream << std::flush;
749 }
750
751
752
753 template <typename ReturnType, typename ExpressionType>
754 bool
756 {
757 // For dictionary substitution, the CSE algorithm moves
758 // ownership of the dependent function expression definition
759 // to the entries in reduced_exprs. So its size thus determines
760 // whether CSE has been executed or not.
761 return (n_reduced_expressions() > 0) ||
762 (n_intermediate_expressions() > 0);
763 }
764
765
766
767 template <typename ReturnType, typename ExpressionType>
768 unsigned int
769 CSEDictionaryVisitor<ReturnType,
770 ExpressionType>::n_intermediate_expressions() const
771 {
772 return intermediate_symbols_exprs.size();
773 }
774
775
776
777 template <typename ReturnType, typename ExpressionType>
778 unsigned int
780 const
781 {
782 return reduced_exprs.size();
783 }
784
785
786
787 /* ------------------ DictionarySubstitutionVisitor ------------------ */
788
789
790 template <typename ReturnType, typename ExpressionType>
791 void
793 const types::symbol_vector &inputs,
794 const SD::Expression &output,
795 const bool use_cse)
796 {
797 init(inputs, types::symbol_vector{output}, use_cse);
798 }
799
800
801
802 template <typename ReturnType, typename ExpressionType>
803 void
805 const SymEngine::vec_basic &inputs,
806 const SymEngine::Basic &output,
807 const bool use_cse)
808 {
810 SD::Expression(output.rcp_from_this()),
811 use_cse);
812 }
813
814
815
816 template <typename ReturnType, typename ExpressionType>
817 void
819 const SymEngine::vec_basic &inputs,
820 const SymEngine::vec_basic &outputs,
821 const bool use_cse)
822 {
825 use_cse);
826 }
827
828
829
830 template <typename ReturnType, typename ExpressionType>
831 void
833 const types::symbol_vector &inputs,
834 const types::symbol_vector &outputs,
835 const bool use_cse)
836 {
837 independent_symbols.clear();
838 dependent_functions.clear();
839
840 independent_symbols = inputs;
841
842 // Perform common subexpression elimination if requested
843 // Note: After this is done, the results produced by
844 // dependent_functions and cse.reduced_exprs should be
845 // the same. We could keep the former so that we can print
846 // out the original expressions if we wish to do so.
847 if (use_cse == false)
848 dependent_functions = outputs;
849 else
850 {
851 cse.init(outputs);
852 }
853 }
854
855
856
857 template <typename ReturnType, typename ExpressionType>
858 ReturnType
860 const std::vector<ReturnType> &substitution_values)
861 {
862 Assert(
863 dependent_functions.size() == 1,
865 "Cannot use this call function when more than one symbolic expression is to be evaluated."));
866 Assert(
867 substitution_values.size() == independent_symbols.size(),
869 "Input substitution vector does not match size of symbol vector."));
870
871 ReturnType out = ::internal::NumberType<ReturnType>::value(0.0);
872 call(&out, substitution_values.data());
873 return out;
874 }
875
876
877
878 template <typename ReturnType, typename ExpressionType>
879 void
881 ReturnType *output_values,
882 const ReturnType *substitution_values)
883 {
884 // Check to see if CSE has been performed
885 if (cse.executed())
886 {
887 cse.call(output_values, independent_symbols, substitution_values);
888 }
889 else
890 {
891 // Build a substitution map.
892 SymEngine::map_basic_basic substitution_value_map;
893 for (unsigned i = 0; i < independent_symbols.size(); ++i)
894 substitution_value_map[independent_symbols[i]] =
895 static_cast<const SymEngine::RCP<const SymEngine::Basic> &>(
896 ExpressionType(substitution_values[i]));
897
898 // Since we don't know how to definitively evaluate the
899 // input number type, we create a generic Expression
900 // with the given symbolic expression and ask it to perform
901 // substitution and evaluation for us.
902 Assert(dependent_functions.size() > 0, ExcInternalError());
903 for (unsigned i = 0; i < dependent_functions.size(); ++i)
904 output_values[i] =
905 ExpressionType(dependent_functions[i])
906 .template substitute_and_evaluate<ReturnType>(
907 substitution_value_map);
908 }
909 }
910
911
912
913 template <typename ReturnType, typename ExpressionType>
914 template <class Archive>
915 void
917 Archive &ar,
918 const unsigned int version) const
919 {
920 // Add some dynamic information to determine if CSE has been used,
921 // without relying on the CSE class when deserializing.
922 // const bool used_cse = cse.executed();
923 // ar &used_cse;
924
925 // CSE and dependent variables both require the independent
926 // symbols, so we serialize them first. The dependent variables
927 // might depend on the outcome of CSE, so we have to serialize
928 // them last.
929 ar &independent_symbols;
930 cse.save(ar, version);
931 ar &dependent_functions;
932 }
933
934
935
936 template <typename ReturnType, typename ExpressionType>
937 template <class Archive>
938 void
940 Archive &ar,
941 const unsigned int version)
942 {
943 Assert(cse.executed() == false, ExcInternalError());
944 Assert(cse.n_intermediate_expressions() == 0, ExcInternalError());
945 Assert(cse.n_reduced_expressions() == 0, ExcInternalError());
946
947 // CSE and dependent variables both require the independent
948 // symbols, so we deserialize them first. The dependent variables
949 // might depend on the outcome of CSE, so we have to deserialize
950 // them last.
951 ar &independent_symbols;
952 cse.load(ar, version);
953 ar &dependent_functions;
954 }
955
956
957
958 template <typename ReturnType, typename ExpressionType>
959 template <typename StreamType>
960 void
962 StreamType &stream,
963 const bool print_independent_symbols,
964 const bool print_dependent_functions,
965 const bool print_cse_reductions) const
966 {
967 if (print_independent_symbols)
968 {
969 stream << "Independent variables: \n";
970 for (unsigned i = 0; i < independent_symbols.size(); ++i)
971 stream << " " << i << ": " << independent_symbols[i] << '\n';
972
973 stream << std::flush;
974 }
975
976 // Check to see if CSE has been performed
977 if (print_cse_reductions && cse.executed())
978 {
979 cse.print(stream);
980 }
981 else
982 {
983 Assert(dependent_functions.size() > 0, ExcInternalError());
984
985 if (print_dependent_functions)
986 {
987 stream << "Dependent variables: \n";
988 for (unsigned i = 0; i < dependent_functions.size(); ++i)
989 stream << " " << i << dependent_functions[i] << '\n';
990
991 stream << std::flush;
992 }
993 }
994 }
995
996# endif // DOXYGEN
997
998 } // namespace internal
999 } // namespace SD
1000} // namespace Differentiation
1001
1002#endif // DEAL_II_WITH_SYMENGINE
1003
1005
1006#endif // dealii_differentiation_sd_symengine_number_visitor_internal_h
void serialize(Archive &archive, const unsigned int version)
void call(ReturnType *output_values, const SymEngine::vec_basic &independent_symbols, const ReturnType *substitution_values)
void call(ReturnType *output_values, const types::symbol_vector &independent_symbols, const ReturnType *substitution_values)
void save(Archive &archive, const unsigned int version) const
void init(const SymEngine::vec_basic &dependent_functions)
void init(const types::symbol_vector &dependent_functions)
void load(Archive &archive, const unsigned int version)
std::vector< std::pair< SD::Expression, SD::Expression > > symbol_vector_pair
ReturnType call(const std::vector< ReturnType > &substitution_values)
void init(const types::symbol_vector &independent_symbols, const Expression &dependent_function, const bool use_cse=false)
void init(const SymEngine::vec_basic &independent_symbols, const SymEngine::Basic &dependent_function, const bool use_cse=false)
void init(const types::symbol_vector &independent_symbols, const types::symbol_vector &dependent_functions, const bool use_cse=false)
void call(ReturnType *output_values, const ReturnType *substitution_values)
void print(StreamType &stream, const bool print_independent_symbols=false, const bool print_dependent_functions=false, const bool print_cse_reductions=false) const
void save(Archive &archive, const unsigned int version) const
void serialize(Archive &archive, const unsigned int version)
void init(const SymEngine::vec_basic &independent_symbols, const SymEngine::vec_basic &dependent_functions, const bool use_cse=false)
void load(Archive &archive, const unsigned int version)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
std::vector< std::pair< Expression, Expression > > convert_basic_pair_vector_to_expression_pair_vector(const SymEngine::vec_pair &symbol_value_vector)
SD::types::symbol_vector convert_basic_vector_to_expression_vector(const SymEngine::vec_basic &symbol_vector)
SymEngine::vec_basic convert_expression_vector_to_basic_vector(const SD::types::symbol_vector &symbol_vector)
std::vector< SD::Expression > symbol_vector
static constexpr const T & value(const T &t)
Definition numbers.h:662