The expressions in CoDiPack help to perform operations on the statement level instead of the operator level. In an operator overloading framework, the general design of the overloads is like
where is the computation type and the operator. Expressions change this concept such that the return type of the operator is no longer the result itself but a structure that contains information about the operator. An example with two different types and and an expression context is
The final return value is now an expression which contains information about the operation, which is indicated by the suffix .
This allows us to move from the differentiation of single elemental operations to the differentiation of full statements with right hand sides that are composed of multiple elemental operations.
On the equation
the pure operator overloading approach would create three intermediate statements
The expression template approach creates one large expression that is represented by the structure
It can be used to evaluate the result and access additional information about the operations. In CoDiPack the functions for each expression are kept rather small. The only directly callable method is getValue
which evaluates the expression. For all other operations the traversal logic classes have to be used.
The current most used expression implementation are:
Custom operations on expressions in CoDiPack are all implemented with the TraversalLogic or CompileTimeTraversalLogic classes. The first one enables custom logic in a runtime context. Variables and results can be stored in the implementation. The CompileTimeTraversalLogic allows for the computation of results in a compile time context. Both classes contain functions for the visit of links, nodes and leaves in the expression graph. The terms are explained in the following picture:
Nodes in the traversal logic are all intermediate operations and leaves are the values on which the operations are evalauted. Links indicate the relations between nodes. Child nodes serve as arguments to their respective parent node.
The graph is traversed with a depth-first search (DFS). On each node the first link is visited, followed by the child of the link. Recursively, the child visits all links and so on. The above example would have the following walk:
Information can be provided or stored in the implementation or propagated via the arguments of the function calls. The default implementation forwards all arguments from the initial call.
Specializations for some common use cases are available:
The CompileTimeTraversalLogic works in the same way as the TraversalLogic and results mostly in the same order of calls. The only difference is that binary expressions perform a reduction of the results from the two children.