The very basics

(by Francesco Biscani and Dario Izzo)

The generalized dual number (gdual) is the main class of pyaudi. You should regard it as a new type that can be used instead of float. It represents a function rather than “just” a value. The difference is that it contains all derivatives of the function at a value. Arithmetic operators +,-,,/ and * work on a gdual. Mathematical functions like sin, cos etc., also work on a gdual, but you must use the ones in the pyaudi module.

Importing stuff

[1]:
from pyaudi import gdual_double as gdual
from pyaudi import sin, cos, tan

Define some variables (gduals)

We define three variables named “x”, “y” and “z” and assign to them the value 0. We also set the maximum derivative order to 3. The three variables are generalized dual numbers and thus any computation on them will result in a gdual (essentially the Taylor expansion around the values x=0, y=0, z=0 of the performed computation)

[2]:
x,y,z = [gdual(0.,_,3) for _ in "xyz"]

Create a function of these variables

Standard arithmetics can be performed on the gduals using the functions imported and the operators +,-,*,/, **

[3]:
f = x*x+2*tan(x/(y+1))-sin(z)
print(f) # This prints the python representation of the Taylor expansion of f (all derivatives up to three are there represented discounted by the factorial factor)
f # This, in a notebook, prints the expression in LaTex format
2*dx+2*dx*dy**2+dx**2+0.666667*dx**3-dz+0.166667*dz**3-2*dx*dy
[3]:
\[ 2{dx}+2{dx}{dy}^{2}+{dx}^{2}+0.666667{dx}^{3}-{dz}+0.166667{dz}^{3}-2{dx}{dy}+\mathcal{O}\left(4\right) \]

Extracting the derivatives

We can now extract the values of the derivatives of \(f\left(x,y,z\right)\) with respect to \(x,y,z\) in the point \(\left(0,0,0\right)\). For instance, this is the value of \(\frac{\partial f}{\partial z}\):

[4]:
f.get_derivative([0,0,1])
[4]:
-1.0

or, using the dictionary interface

[5]:
f.get_derivative({"dz": 1})
[5]:
-1.0

This is \(\frac{\partial^2 f}{\partial x \partial y}\):

[6]:
f.get_derivative({"dx":1, "dy":1})
[6]:
-2.0

Changing the point

By re-defining \(x,y,z\), we can compute the derivatives in a different point. For instance, for the computation of the derivatives in the point \(\left(1,2,3\right)\), we write:

[7]:
x = gdual(1.,"x",3)
y = gdual(2.,"y",3)
z = gdual(3.,"z",3)
f = x*x+2*tan(x/(y+1))-sin(z)
f
[7]:
\[ 0.0925294{dy}^{2}+0.07056{dz}^{2}+2.74659{dx}+0.0375972{dx}^{3}-0.248865{dy}-0.164999{dz}^{3}-0.0950441{dx}^{2}{dy}+1.55139+0.133785{dx}{dy}^{2}+1.08617{dx}^{2}-0.0354271{dy}^{3}+0.989992{dz}-0.306312{dx}{dy}+\mathcal{O}\left(4\right) \]

Now \(\frac{\partial f}{\partial z}\):

[8]:
f.get_derivative([0,0,1])
[8]:
0.9899924966004454

We can verifiy the correctness by manually computing the derivative:

[9]:
-cos(3.)
[9]:
0.9899924966004454

Encapsulating f in a function call

We may write all of the above in a more elegant fashion by just defining f as a function and then calling it with gduals, rather than floats

[10]:
def f(x,y,z):
    return x*x+2*tan(x/(y+1))-sin(z)
[11]:
x = gdual(1.,"x",3)
y = gdual(2.,"y",3)
z = gdual(3.,"z",3)
print(f(x,y,z)) #Call with gduals
f(1.,2.,3.)     #Call with floats
0.0925294*dy**2+0.07056*dz**2+2.74659*dx+0.0375972*dx**3-0.248865*dy-0.164999*dz**3-0.0950441*dx**2*dy+1.55139+0.133785*dx*dy**2+1.08617*dx**2-0.0354271*dy**3+0.989992*dz-0.306312*dx*dy
[11]:
1.5513870909612837