Automatic differentiation

Last updated

In mathematics and computer algebra, automatic differentiation (auto-differentiation, autodiff, or AD), also called algorithmic differentiation, computational differentiation, [1] [2] is a set of techniques to evaluate the partial derivative of a function specified by a computer program.

Contents

Automatic differentiation exploits the fact that every computer calculation, no matter how complicated, executes a sequence of elementary arithmetic operations (addition, subtraction, multiplication, division, etc.) and elementary functions (exp, log, sin, cos, etc.). By applying the chain rule repeatedly to these operations, partial derivatives of arbitrary order can be computed automatically, accurately to working precision, and using at most a small constant factor of more arithmetic operations than the original program.

Difference from other differentiation methods

Figure 1: How automatic differentiation relates to symbolic differentiation AutomaticDifferentiationNutshell.png
Figure 1: How automatic differentiation relates to symbolic differentiation

Automatic differentiation is distinct from symbolic differentiation and numerical differentiation. Symbolic differentiation faces the difficulty of converting a computer program into a single mathematical expression and can lead to inefficient code. Numerical differentiation (the method of finite differences) can introduce round-off errors in the discretization process and cancellation. Both of these classical methods have problems with calculating higher derivatives, where complexity and errors increase. Finally, both of these classical methods are slow at computing partial derivatives of a function with respect to many inputs, as is needed for gradient-based optimization algorithms. Automatic differentiation solves all of these problems.

Applications

Automatic differentiation is particularly important in the field of machine learning. For example, it allows one to implement backpropagation in a neural network without a manually-computed derivative.

Forward and reverse accumulation

Chain rule of partial derivatives of composite functions

Fundamental to automatic differentiation is the decomposition of differentials provided by the chain rule of partial derivatives of composite functions. For the simple composition the chain rule gives

Two types of automatic differentiation

Usually, two distinct modes of automatic differentiation are presented.

Forward accumulation specifies that one traverses the chain rule from inside to outside (that is, first compute and then and at last ), while reverse accumulation has the traversal from outside to inside (first compute and then and at last ). More succinctly,

The value of the partial derivative, called seed, is propagated forward or backward and is initially or . Forward accumulation evaluates the function and calculates the derivative with respect to one independent variable in one pass. For each independent variable a separate pass is therefore necessary in which the derivative with respect to that independent variable is set to one () and of all others to zero (). In contrast, reverse accumulation requires the evaluated partial functions for the partial derivatives. Reverse accumulation therefore evaluates the function first and calculates the derivatives with respect to all independent variables in an additional pass.

Which of these two types should be used depends on the sweep count. The computational complexity of one sweep is proportional to the complexity of the original code.

Backpropagation of errors in multilayer perceptrons, a technique used in machine learning, is a special case of reverse accumulation. [2]

Forward accumulation was introduced by R.E. Wengert in 1964. [3] According to Andreas Griewank, reverse accumulation has been suggested since the late 1960s, but the inventor is unknown. [4] Seppo Linnainmaa published reverse accumulation in 1976. [5]

Forward accumulation

Forward accumulation ForwardAD.png
Forward accumulation

In forward accumulation AD, one first fixes the independent variable with respect to which differentiation is performed and computes the derivative of each sub-expression recursively. In a pen-and-paper calculation, this involves repeatedly substituting the derivative of the inner functions in the chain rule: This can be generalized to multiple variables as a matrix product of Jacobians.

Compared to reverse accumulation, forward accumulation is natural and easy to implement as the flow of derivative information coincides with the order of evaluation. Each variable is augmented with its derivative (stored as a numerical value, not a symbolic expression), as denoted by the dot. The derivatives are then computed in sync with the evaluation steps and combined with other derivatives via the chain rule.

Using the chain rule, if has predecessors in the computational graph:

Figure 2: Example of forward accumulation with computational graph ForwardAccumulationAutomaticDifferentiation.png
Figure 2: Example of forward accumulation with computational graph

As an example, consider the function: For clarity, the individual sub-expressions have been labeled with the variables .

The choice of the independent variable to which differentiation is performed affects the seed values 1 and 2. Given interest in the derivative of this function with respect to x1, the seed values should be set to:

With the seed values set, the values propagate using the chain rule as shown. Figure 2 shows a pictorial depiction of this process as a computational graph.

Operations to compute valueOperations to compute derivative
(seed)
(seed)

To compute the gradient of this example function, which requires not only but also , an additional sweep is performed over the computational graph using the seed values .

Implementation

Pseudocode

Forward accumulation calculates the function and the derivative (but only for one independent variable each) in one pass. The associated method call expects the expression Z to be derived with regard to a variable V. The method returns a pair of the evaluated function and its derivative. The method traverses the expression tree recursively until a variable is reached. If the derivative with respect to this variable is requested, its derivative is 1, 0 otherwise. Then the partial function as well as the partial derivative are evaluated. [6]

tuple<float,float>evaluateAndDerive(ExpressionZ,VariableV){ifisVariable(Z)if(Z=V)return{valueOf(Z),1};elsereturn{valueOf(Z),0};elseif(Z=A+B){a,a'}=evaluateAndDerive(A,V);{b,b'}=evaluateAndDerive(B,V);return{a+b,a'+b'};elseif(Z=A-B){a,a'}=evaluateAndDerive(A,V);{b,b'}=evaluateAndDerive(B,V);return{a-b,a'-b'};elseif(Z=A*B){a,a'}=evaluateAndDerive(A,V);{b,b'}=evaluateAndDerive(B,V);return{a*b,b*a'+a*b'};}
C++
#include<iostream>structValueAndPartial{floatvalue,partial;};structVariable;structExpression{virtualValueAndPartialevaluateAndDerive(Variable*variable)=0;};structVariable:publicExpression{floatvalue;Variable(floatvalue):value(value){}ValueAndPartialevaluateAndDerive(Variable*variable){floatpartial=(this==variable)?1.0f:0.0f;return{value,partial};}};structPlus:publicExpression{Expression*a,*b;Plus(Expression*a,Expression*b):a(a),b(b){}ValueAndPartialevaluateAndDerive(Variable*variable){auto[valueA,partialA]=a->evaluateAndDerive(variable);auto[valueB,partialB]=b->evaluateAndDerive(variable);return{valueA+valueB,partialA+partialB};}};structMultiply:publicExpression{Expression*a,*b;Multiply(Expression*a,Expression*b):a(a),b(b){}ValueAndPartialevaluateAndDerive(Variable*variable){auto[valueA,partialA]=a->evaluateAndDerive(variable);auto[valueB,partialB]=b->evaluateAndDerive(variable);return{valueA*valueB,valueB*partialA+valueA*partialB};}};intmain(){// Example: Finding the partials of z = x * (x + y) + y * y at (x, y) = (2, 3)Variablex(2),y(3);Plusp1(&x,&y);Multiplym1(&x,&p1);Multiplym2(&y,&y);Plusz(&m1,&m2);floatxPartial=z.evaluateAndDerive(&x).partial;floatyPartial=z.evaluateAndDerive(&y).partial;std::cout<<"∂z/∂x = "<<xPartial<<", "<<"∂z/∂y = "<<yPartial<<std::endl;// Output: ∂z/∂x = 7, ∂z/∂y = 8return0;}

Reverse accumulation

Reverse accumulation AutoDiff.webp
Reverse accumulation

In reverse accumulation AD, the dependent variable to be differentiated is fixed and the derivative is computed with respect to each sub-expression recursively. In a pen-and-paper calculation, the derivative of the outer functions is repeatedly substituted in the chain rule:

In reverse accumulation, the quantity of interest is the adjoint, denoted with a bar ; it is a derivative of a chosen dependent variable with respect to a subexpression :

Using the chain rule, if has successors in the computational graph:

Reverse accumulation traverses the chain rule from outside to inside, or in the case of the computational graph in Figure 3, from top to bottom. The example function is scalar-valued, and thus there is only one seed for the derivative computation, and only one sweep of the computational graph is needed to calculate the (two-component) gradient. This is only half the work when compared to forward accumulation, but reverse accumulation requires the storage of the intermediate variables wi as well as the instructions that produced them in a data structure known as a "tape" or a Wengert list [7] (however, Wengert published forward accumulation, not reverse accumulation [3] ), which may consume significant memory if the computational graph is large. This can be mitigated to some extent by storing only a subset of the intermediate variables and then reconstructing the necessary work variables by repeating the evaluations, a technique known as rematerialization. Checkpointing is also used to save intermediary states.

Figure 3: Example of reverse accumulation with computational graph ReverseaccumulationAD.png
Figure 3: Example of reverse accumulation with computational graph

The operations to compute the derivative using reverse accumulation are shown in the table below (note the reversed order):

Operations to compute derivative

The data flow graph of a computation can be manipulated to calculate the gradient of its original calculation. This is done by adding an adjoint node for each primal node, connected by adjoint edges which parallel the primal edges but flow in the opposite direction. The nodes in the adjoint graph represent multiplication by the derivatives of the functions calculated by the nodes in the primal. For instance, addition in the primal causes fanout in the adjoint; fanout in the primal causes addition in the adjoint; [lower-alpha 1] a unary function y = f(x) in the primal causes = ȳf′(x) in the adjoint; etc.

Implementation

Pseudo code

Reverse accumulation requires two passes: In the forward pass, the function is evaluated first and the partial results are cached. In the reverse pass, the partial derivatives are calculated and the previously derived value is backpropagated. The corresponding method call expects the expression Z to be derived and seed with the derived value of the parent expression. For the top expression, Z derived with regard to Z, this is 1. The method traverses the expression tree recursively until a variable is reached and adds the current seed value to the derivative expression. [8] [9]

voidderive(ExpressionZ,floatseed){ifisVariable(Z)partialDerivativeOf(Z)+=seed;elseif(Z=A+B)derive(A,seed);derive(B,seed);elseif(Z=A-B)derive(A,seed);derive(B,-seed);elseif(Z=A*B)derive(A,valueOf(B)*seed);derive(B,valueOf(A)*seed);}
C++
#include<iostream>structExpression{floatvalue;virtualvoidevaluate()=0;virtualvoidderive(floatseed)=0;};structVariable:publicExpression{floatpartial;Variable(floatvalue){this->value=value;partial=0.0f;}voidevaluate(){}voidderive(floatseed){partial+=seed;}};structPlus:publicExpression{Expression*a,*b;Plus(Expression*a,Expression*b):a(a),b(b){}voidevaluate(){a->evaluate();b->evaluate();value=a->value+b->value;}voidderive(floatseed){a->derive(seed);b->derive(seed);}};structMultiply:publicExpression{Expression*a,*b;Multiply(Expression*a,Expression*b):a(a),b(b){}voidevaluate(){a->evaluate();b->evaluate();value=a->value*b->value;}voidderive(floatseed){a->derive(b->value*seed);b->derive(a->value*seed);}};intmain(){// Example: Finding the partials of z = x * (x + y) + y * y at (x, y) = (2, 3)Variablex(2),y(3);Plusp1(&x,&y);Multiplym1(&x,&p1);Multiplym2(&y,&y);Plusz(&m1,&m2);z.evaluate();std::cout<<"z = "<<z.value<<std::endl;// Output: z = 19z.derive(1);std::cout<<"∂z/∂x = "<<x.partial<<", "<<"∂z/∂y = "<<y.partial<<std::endl;// Output: ∂z/∂x = 7, ∂z/∂y = 8return0;}

Beyond forward and reverse accumulation

Forward and reverse accumulation are just two (extreme) ways of traversing the chain rule. The problem of computing a full Jacobian of f : RnRm with a minimum number of arithmetic operations is known as the optimal Jacobian accumulation (OJA) problem, which is NP-complete. [10] Central to this proof is the idea that algebraic dependencies may exist between the local partials that label the edges of the graph. In particular, two or more edge labels may be recognized as equal. The complexity of the problem is still open if it is assumed that all edge labels are unique and algebraically independent.

Automatic differentiation using dual numbers

Forward mode automatic differentiation is accomplished by augmenting the algebra of real numbers and obtaining a new arithmetic. An additional component is added to every number to represent the derivative of a function at the number, and all arithmetic operators are extended for the augmented algebra. The augmented algebra is the algebra of dual numbers.

Replace every number with the number , where is a real number, but is an abstract number with the property (an infinitesimal; see Smooth infinitesimal analysis ). Using only this, regular arithmetic gives using .

Now, polynomials can be calculated in this augmented arithmetic. If , then where denotes the derivative of with respect to its first argument, and , called a seed, can be chosen arbitrarily.

The new arithmetic consists of ordered pairs, elements written , with ordinary arithmetics on the first component, and first order differentiation arithmetic on the second component, as described above. Extending the above results on polynomials to analytic functions gives a list of the basic arithmetic and some standard functions for the new arithmetic: and in general for the primitive function , where and are the derivatives of with respect to its first and second arguments, respectively.

When a binary basic arithmetic operation is applied to mixed arguments—the pair and the real number —the real number is first lifted to . The derivative of a function at the point is now found by calculating using the above arithmetic, which gives as the result.

Implementation

An example implementation based on the dual number approach follows.

Pseudo code

Dual plus(Dual A, Dual B) {   return {     realPartOf(A) + realPartOf(B),     infinitesimalPartOf(A) + infinitesimalPartOf(B)   }; } Dual minus(Dual A, Dual B) {   return {     realPartOf(A) - realPartOf(B),     infinitesimalPartOf(A) - infinitesimalPartOf(B)   }; } Dual multiply(Dual A, Dual B) {   return {     realPartOf(A) * realPartOf(B),     realPartOf(B) * infinitesimalPartOf(A) + realPartOf(A) * infinitesimalPartOf(B)   }; } X = {x, 0}; Y = {y, 0}; Epsilon = {0, 1}; xPartial = infinitesimalPartOf(f(X + Epsilon, Y)); yPartial = infinitesimalPartOf(f(X, Y + Epsilon));

C++

#include<iostream>structDual{floatrealPart,infinitesimalPart;Dual(floatrealPart,floatinfinitesimalPart=0):realPart(realPart),infinitesimalPart(infinitesimalPart){}Dualoperator+(Dualother){returnDual(realPart+other.realPart,infinitesimalPart+other.infinitesimalPart);}Dualoperator*(Dualother){returnDual(realPart*other.realPart,other.realPart*infinitesimalPart+realPart*other.infinitesimalPart);}};// Example: Finding the partials of z = x * (x + y) + y * y at (x, y) = (2, 3)Dualf(Dualx,Dualy){returnx*(x+y)+y*y;}intmain(){Dualx=Dual(2);Dualy=Dual(3);Dualepsilon=Dual(0,1);Duala=f(x+epsilon,y);Dualb=f(x,y+epsilon);std::cout<<"∂z/∂x = "<<a.infinitesimalPart<<", "<<"∂z/∂y = "<<b.infinitesimalPart<<std::endl;// Output: ∂z/∂x = 7, ∂z/∂y = 8return0;}

Vector arguments and functions

Multivariate functions can be handled with the same efficiency and mechanisms as univariate functions by adopting a directional derivative operator. That is, if it is sufficient to compute , the directional derivative of at in the direction may be calculated as using the same arithmetic as above. If all the elements of are desired, then function evaluations are required. Note that in many optimization applications, the directional derivative is indeed sufficient.

High order and many variables

The above arithmetic can be generalized to calculate second order and higher derivatives of multivariate functions. However, the arithmetic rules quickly grow complicated: complexity is quadratic in the highest derivative degree. Instead, truncated Taylor polynomial algebra can be used. The resulting arithmetic, defined on generalized dual numbers, allows efficient computation using functions as if they were a data type. Once the Taylor polynomial of a function is known, the derivatives are easily extracted.

Implementation

Forward-mode AD is implemented by a nonstandard interpretation of the program in which real numbers are replaced by dual numbers, constants are lifted to dual numbers with a zero epsilon coefficient, and the numeric primitives are lifted to operate on dual numbers. This nonstandard interpretation is generally implemented using one of two strategies: source code transformation or operator overloading.

Source code transformation (SCT)

Figure 4: Example of how source code transformation could work SourceTransformationAutomaticDifferentiation.png
Figure 4: Example of how source code transformation could work

The source code for a function is replaced by an automatically generated source code that includes statements for calculating the derivatives interleaved with the original instructions.

Source code transformation can be implemented for all programming languages, and it is also easier for the compiler to do compile time optimizations. However, the implementation of the AD tool itself is more difficult and the build system is more complex.

Operator overloading (OO)

Figure 5: Example of how operator overloading could work OperatorOverloadingAutomaticDifferentiation.png
Figure 5: Example of how operator overloading could work

Operator overloading is a possibility for source code written in a language supporting it. Objects for real numbers and elementary mathematical operations must be overloaded to cater for the augmented arithmetic depicted above. This requires no change in the form or sequence of operations in the original source code for the function to be differentiated, but often requires changes in basic data types for numbers and vectors to support overloading and often also involves the insertion of special flagging operations. Due to the inherent operator overloading overhead on each loop, this approach usually demonstrates weaker speed performance.

Operator overloading and source code transformation

Overloaded Operators can be used to extract the valuation graph, followed by automatic generation of the AD-version of the primal function at run-time. Unlike the classic OO AAD, such AD-function does not change from one iteration to the next one. Hence there is any OO or tape interpretation run-time overhead per Xi sample.

With the AD-function being generated at runtime, it can be optimised to take into account the current state of the program and precompute certain values. In addition, it can be generated in a way to consistently utilize native CPU vectorization to process 4(8)-double chunks of user data (AVX2\AVX512 speed up x4-x8). With multithreading added into account, such approach can lead to a final acceleration of order 8 × #Cores compared to the traditional AAD tools. A reference implementation is available on GitHub. [11]

See also

Notes

  1. In terms of weight matrices, the adjoint is the transpose. Addition is the covector , since and fanout is the vector since

Related Research Articles

In calculus, the chain rule is a formula that expresses the derivative of the composition of two differentiable functions f and g in terms of the derivatives of f and g. More precisely, if is the function such that for every x, then the chain rule is, in Lagrange's notation, or, equivalently,

In quantum mechanics, the Hamiltonian of a system is an operator corresponding to the total energy of that system, including both kinetic energy and potential energy. Its spectrum, the system's energy spectrum or its set of energy eigenvalues, is the set of possible outcomes obtainable from a measurement of the system's total energy. Due to its close relation to the energy spectrum and time-evolution of a system, it is of fundamental importance in most formulations of quantum theory.

<span class="mw-page-title-main">Dirac delta function</span> Generalized function whose value is zero everywhere except at zero

In mathematical analysis, the Dirac delta function, also known as the unit impulse, is a generalized function on the real numbers, whose value is zero everywhere except at zero, and whose integral over the entire real line is equal to one. Since there is no function having this property, modelling the delta "function" rigorously involves the use of limits or, as is common in mathematics, measure theory and the theory of distributions.

In algebra, the dual numbers are a hypercomplex number system first introduced in the 19th century. They are expressions of the form a + , where a and b are real numbers, and ε is a symbol taken to satisfy with .

<span class="mw-page-title-main">Cauchy's integral formula</span> Provides integral formulas for all derivatives of a holomorphic function

In mathematics, Cauchy's integral formula, named after Augustin-Louis Cauchy, is a central statement in complex analysis. It expresses the fact that a holomorphic function defined on a disk is completely determined by its values on the boundary of the disk, and it provides integral formulas for all derivatives of a holomorphic function. Cauchy's formula shows that, in complex analysis, "differentiation is equivalent to integration": complex differentiation, like integration, behaves well under uniform limits – a result that does not hold in real analysis.

In continuum mechanics, the infinitesimal strain theory is a mathematical approach to the description of the deformation of a solid body in which the displacements of the material particles are assumed to be much smaller than any relevant dimension of the body; so that its geometry and the constitutive properties of the material at each point of space can be assumed to be unchanged by the deformation.

<span class="mw-page-title-main">Product rule</span> Formula for the derivative of a product

In calculus, the product rule is a formula used to find the derivatives of products of two or more functions. For two functions, it may be stated in Lagrange's notation as or in Leibniz's notation as

<span class="mw-page-title-main">Helmholtz free energy</span> Thermodynamic potential

In thermodynamics, the Helmholtz free energy is a thermodynamic potential that measures the useful work obtainable from a closed thermodynamic system at a constant temperature (isothermal). The change in the Helmholtz energy during a process is equal to the maximum amount of work that the system can perform in a thermodynamic process in which temperature is held constant. At constant temperature, the Helmholtz free energy is minimized at equilibrium.

An operator is a function over a space of physical states onto another space of states. The simplest example of the utility of operators is the study of symmetry. Because of this, they are useful tools in classical mechanics. Operators are even more important in quantum mechanics, where they form an intrinsic part of the formulation of the theory.

In mathematics, the Hodge star operator or Hodge star is a linear map defined on the exterior algebra of a finite-dimensional oriented vector space endowed with a nondegenerate symmetric bilinear form. Applying the operator to an element of the algebra produces the Hodge dual of the element. This map was introduced by W. V. D. Hodge.

In mathematics, the covariant derivative is a way of specifying a derivative along tangent vectors of a manifold. Alternatively, the covariant derivative is a way of introducing and working with a connection on a manifold by means of a differential operator, to be contrasted with the approach given by a principal connection on the frame bundle – see affine connection. In the special case of a manifold isometrically embedded into a higher-dimensional Euclidean space, the covariant derivative can be viewed as the orthogonal projection of the Euclidean directional derivative onto the manifold's tangent space. In this case the Euclidean derivative is broken into two parts, the extrinsic normal component and the intrinsic covariant derivative component.

<span class="mw-page-title-main">Path integral formulation</span> Formulation of quantum mechanics

The path integral formulation is a description in quantum mechanics that generalizes the stationary action principle of classical mechanics. It replaces the classical notion of a single, unique classical trajectory for a system with a sum, or functional integral, over an infinity of quantum-mechanically possible trajectories to compute a quantum amplitude.

A directional derivative is a concept in multivariable calculus that measures the rate at which a function changes in a particular direction at a given point.

In mathematics, differential refers to several related notions derived from the early days of calculus, put on a rigorous footing, such as infinitesimal differences and the derivatives of functions.

In probability theory and related fields, Malliavin calculus is a set of mathematical techniques and ideas that extend the mathematical field of calculus of variations from deterministic functions to stochastic processes. In particular, it allows the computation of derivatives of random variables. Malliavin calculus is also called the stochastic calculus of variations. P. Malliavin first initiated the calculus on infinite dimensional space. Then, the significant contributors such as S. Kusuoka, D. Stroock, J-M. Bismut, Shinzo Watanabe, I. Shigekawa, and so on finally completed the foundations.

In mathematics, the total derivative of a function f at a point is the best linear approximation near this point of the function with respect to its arguments. Unlike partial derivatives, the total derivative approximates the function with respect to all of its arguments, not just a single one. In many situations, this is the same as considering all partial derivatives simultaneously. The term "total derivative" is primarily used when f is a function of several variables, because when f is a function of a single variable, the total derivative is the same as the ordinary derivative of the function.

In quantum mechanics, the momentum operator is the operator associated with the linear momentum. The momentum operator is, in the position representation, an example of a differential operator. For the case of one particle in one spatial dimension, the definition is: where ħ is the reduced Planck constant, i the imaginary unit, x is the spatial coordinate, and a partial derivative is used instead of a total derivative since the wave function is also a function of time. The "hat" indicates an operator. The "application" of the operator on a differentiable wave function is as follows:

In mathematics, the Fréchet derivative is a derivative defined on normed spaces. Named after Maurice Fréchet, it is commonly used to generalize the derivative of a real-valued function of a single real variable to the case of a vector-valued function of multiple real variables, and to define the functional derivative used widely in the calculus of variations.

In mathematics, Weyl's lemma, named after Hermann Weyl, states that every weak solution of Laplace's equation is a smooth solution. This contrasts with the wave equation, for example, which has weak solutions that are not smooth solutions. Weyl's lemma is a special case of elliptic or hypoelliptic regularity.

In calculus, the differential represents the principal part of the change in a function with respect to changes in the independent variable. The differential is defined by where is the derivative of f with respect to , and is an additional real variable. The notation is such that the equation

References

  1. Neidinger, Richard D. (2010). "Introduction to Automatic Differentiation and MATLAB Object-Oriented Programming" (PDF). SIAM Review. 52 (3): 545–563. CiteSeerX   10.1.1.362.6580 . doi:10.1137/080743627. S2CID   17134969.
  2. 1 2 Baydin, Atilim Gunes; Pearlmutter, Barak; Radul, Alexey Andreyevich; Siskind, Jeffrey (2018). "Automatic differentiation in machine learning: a survey". Journal of Machine Learning Research. 18: 1–43.
  3. 1 2 R.E. Wengert (1964). "A simple automatic derivative evaluation program". Comm. ACM. 7 (8): 463–464. doi: 10.1145/355586.364791 . S2CID   24039274.
  4. Griewank, Andreas (2012). "Who Invented the Reverse Mode of Differentiation?" (PDF). Optimization Stories, Documenta Matematica. Extra Volume ISMP: 389–400. doi:10.4171/dms/6/38. ISBN   978-3-936609-58-5.
  5. Linnainmaa, Seppo (1976). "Taylor Expansion of the Accumulated Rounding Error". BIT Numerical Mathematics. 16 (2): 146–160. doi:10.1007/BF01931367. S2CID   122357351.
  6. Maximilian E. Schüle, Maximilian Springer, Alfons Kemper, Thomas Neumann (2022). "LLVM code optimisation for automatic differentiation". Proceedings of the Sixth Workshop on Data Management for End-To-End Machine Learning. pp. 1–4. doi:10.1145/3533028.3533302. ISBN   9781450393751. S2CID   248853034.{{cite book}}: CS1 maint: multiple names: authors list (link)
  7. Bartholomew-Biggs, Michael; Brown, Steven; Christianson, Bruce; Dixon, Laurence (2000). "Automatic differentiation of algorithms". Journal of Computational and Applied Mathematics. 124 (1–2): 171–190. Bibcode:2000JCoAM.124..171B. doi:10.1016/S0377-0427(00)00422-2. hdl: 2299/3010 .
  8. Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper, Thomas Neumann, Stephan Günnemann (2021). "In-Database Machine Learning with SQL on GPUs". 33rd International Conference on Scientific and Statistical Database Management. pp. 25–36. doi:10.1145/3468791.3468840. ISBN   9781450384131. S2CID   235386969.{{cite book}}: CS1 maint: multiple names: authors list (link)
  9. Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper, Thomas Neumann, Stephan Günnemann (2022). "Recursive SQL and GPU-support for in-database machine learning". Distributed and Parallel Databases. 40 (2–3): 205–259. doi: 10.1007/s10619-022-07417-7 . S2CID   250412395.{{cite journal}}: CS1 maint: multiple names: authors list (link)
  10. Naumann, Uwe (April 2008). "Optimal Jacobian accumulation is NP-complete". Mathematical Programming. 112 (2): 427–441. CiteSeerX   10.1.1.320.5665 . doi:10.1007/s10107-006-0042-z. S2CID   30219572.
  11. "AADC Prototype Library". June 22, 2022 via GitHub.

Further reading