# Lagrangian mechanics

> Marcos Duarte
> Laboratory of Biomechanics and Motor Control ([http://demotu.org/](http://demotu.org/))
> Federal University of ABC, Brazil

## The development of the laws of motion of bodies

> *"The theoretical development of the laws of motion of bodies is a problem of such interest and importance that it has engaged the attention of all the most eminent mathematicians since the invention of dynamics as a mathematical science by Galileo, and especially since the wonderful extension which was given to that science by Newton.
> Among the successors of those illustrious men, Lagrange has perhaps done more than any other analyst to give extent and harmony to such deductive researches, by showing that the most varied consequences respecting the motions of systems of bodies may be derived from one radical formula; the beauty of the methods so suiting the dignity of the results as to make of his great work a kind of scientific poem."*
— Hamilton, 1834 (apud Taylor, 2005).

The [Lagrangian mechanics](https://en.wikipedia.org/wiki/Lagrangian_mechanics) is a formulation of [classical mechanics](https://en.wikipedia.org/wiki/Classical_mechanics) where the [equations of motion](https://en.wikipedia.org/wiki/Equations_of_motion) are obtained from the [kinetic](https://en.wikipedia.org/wiki/Kinetic_energy) and [potential](https://en.wikipedia.org/wiki/Potential_energy) energy of the system (scalar quantities) represented in [generalized coordinates](https://en.wikipedia.org/wiki/Generalized_coordinates) instead of using [Newton's laws of motion](https://en.wikipedia.org/wiki/Newton's_laws_of_motion) to deduce the equations of motion from the [forces](https://en.wikipedia.org/wiki/Force) on the system ([vector](https://en.wikipedia.org/wiki/Euclidean_vector) quantities) represented in [Cartesian coordinates](https://en.wikipedia.org/wiki/Cartesian_coordinate_system). Lagrangian mechanics was introduced by [Joseph-Louis Lagrange](https://en.wikipedia.org/wiki/Joseph-Louis_Lagrange) in the late 18th century.

Let's deduce the Lagrange equations, but first let's review the basics of the Newtonian approach.

## Description of motion

One can describe the motion of a particle by specifying its position with respect to a frame of reference in the three-dimensional space as a function of time:

\begin{equation}
x(t),\, y(t),\, z(t) \quad \equiv \quad x_i(t) \quad i=1,\dotsc,3
\label{eq1}
\end{equation}

A system of $N$ particles will require $3N$ equations to describe their motion.

The basic problem in classical mechanics is to find ways to determine functions such as these, also known as equations of motion, capable of specifying the position of objects over time, for any mechanical situation. Assuming as known the meaning of $x_i(t)$, one can define the components of velocity, $v_i$, and acceleration, $a_i$, at time $t$, as:

\begin{equation} \begin{array}{rcl}
v_i(t) = \dfrac{\mathrm d x_i(t)}{\mathrm d t} = \dot{x}_i(t) \\
a_i(t) = \dfrac{\mathrm d^2 x_i(t)}{\mathrm d t^2} = \dot{v}_i(t)
\label{eq3}
\end{array} \end{equation}

Where we used the Newton's notation for differentiation (also called the dot notation), a dot over the dependent variable. Of note, [Joseph Louis Lagrange](https://pt.wikipedia.org/wiki/Joseph-Louis_Lagrange) introduced the prime mark to denote a derivative: $x'(t)$. Read more about the different notations for differentiation at [Wikipedia](https://en.wikipedia.org/wiki/Notation_for_differentiation).

### Laws of motion

The [Newton's laws of motion](https://en.wikipedia.org/wiki/Newton's_laws_of_motion) laid the foundation for classical mechanics. They describe the relationship between the motion of a body and the possible forces acting upon it.

In Newtonian mechanics, the body's linear momentum is defined as:

\begin{equation}
\mathbf{p} = m\mathbf{v}
\label{eq5}
\end{equation}

If the mass of the body is constant, remember that Newton's second law can be expressed by:

\begin{equation}
\mathbf{F} = \frac{\mathrm d \mathbf{p}}{\mathrm d t}=\frac{\mathrm d \big(m\mathbf{v}\big)}{\mathrm d t} = m\mathbf{a}
\label{eq6}
\end{equation}

Using Newton's second law, to determine the position of the body we will have to solve the following second order ordinary differential equation:

\begin{equation}
\frac{\mathrm d^2 x_i(t)}{\mathrm d t^2} = \frac{\mathbf{F}}{m}
\label{eqa7}
\end{equation}

Which has the general solution:

\begin{equation}
\mathbf{x}(t) = \int\!\bigg(\int\frac{\mathbf{F}}{m} \mathrm{d}t\bigg)\mathrm{d}t
\label{eq8}
\end{equation}

### Mechanical energy

A related physical quantity is the mechanical energy, which is the sum of kinetic and potential energies.
The kinetic energy, $T$ of a particle is given by:

\begin{equation}
T = \frac{1}{2}mv^2
\label{eq9}
\end{equation}

The kinetic energy of a particle can be expressed in terms of its linear momentum:

\begin{equation}
T = \frac{p^2}{2m}
\label{eq10}
\end{equation}

And for a given coordinate of the particle's motion, its linear momentum can be obtained from its kinetic energy by:

\begin{equation}
p_i = \frac{\partial T}{\partial v_i}
\label{eq11}
\end{equation}

The potential energy, $V$ is the stored energy of a particle and its formulation is dependent on the force acting on the particle. For example, for a conservative force dependent solely on the particle position, such as due to the gravitational field near the Earth  surface or due to a linear spring, force and potential energy are related by:

\begin{equation}
\mathbf{F} = - \frac{\partial \mathbf{V}}{\partial x}
\label{eq12}
\end{equation}

## Lagrange's equation in Cartesian Coordinates

The Lagrangian mechanics can be formulated independent of the Newtonian mechanics and Cartesian coordinates; in fact  Joseph-Louis Lagrange this new formalism from the [principle of least action](http://nbviewer.jupyter.org/github/BMClab/bmc/blob/master/notebooks/principle_of_least_action.ipynb).

For simplicity, let's first deduce the Lagrange's equation for a particle in Cartesian Coordinates and from Newton's second law.

Because we want to deduce the laws of motion based on the mechanical energy of the particle, one can see that the time derivative of the expression for the linear momentum as a function of the kinetic energy, cf. Eq. (\ref{eq11}), is equal to the force acting on the particle and we can substitute the force in Newton's second law by this term:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\bigg(\frac{\partial T}{\partial \dot x}\bigg) = m\ddot x
\label{eq13}
\end{equation}

We saw that a conservative force can also be expressed in terms of the potential energy of the particle, cf. Eq. (\ref{eq12}); substituting the right side of the equation above by this expression, we have:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\bigg(\frac{\partial T}{\partial \dot x}\bigg) = -\frac{\partial V}{\partial x}
\label{eq14}
\end{equation}

Using the fact that:

\begin{equation}
\frac{\partial T}{\partial x} = 0 \quad and \quad \frac{\partial V}{\partial \dot x} = 0
\label{eq15}
\end{equation}

We can write:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\bigg(\frac{\partial (T-V)}{\partial \dot x}\bigg) - \frac{\partial (T-V)}{\partial x} = 0
\label{eq16}
\end{equation}

Defining the Lagrange or Lagrangian function, $\mathcal{L}$, as the difference between the kinetic and potential energy in the system:

\begin{equation}
\mathcal{L}(x,\dot x, t) = T - V
\label{eq17}
\end{equation}

We have the Lagrange's equation in Cartesian Coordinates for a conservative force acting on a particle:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\bigg(\frac{\partial \mathcal{L}}{\partial \dot x}\bigg) - \frac{\partial \mathcal{L}}{\partial x} = 0
\label{eq18}
\end{equation}

Once all derivatives of the Lagrangian function are calculated, this equation will be the equation of motion for the particle. If there are $N$ independent particles in a three-dimensional space, there will be $3N$ equations for the system.
The set of equations above for a system are known as Euler–Lagrange equations, or Lagrange's equations of the second kind.

Let's see some simple examples of the Lagrange's equation in Cartesian Coordinates just to consolidate what was deduced above. The real application of Lagrangian mechanics is in generalized coordinates, what will see after these examples.

### Example 1: Particle moving under the influence of a conservative force

Let's deduce the equation of motion using the Lagrangian mechanics for a particle with mass $m$ moving in the 3D space under the influence of a [conservative force](https://en.wikipedia.org/wiki/Conservative_force).
The Lagrangian $(\mathcal{L} = T - V)$ of the particle is:

\begin{equation}
\mathcal{L}(x,y,z,\dot x,\dot y,\dot z, t) = \frac{1}{2}m(\dot x^2(t) + \dot y^2(t) + \dot z^2(t)) - V(x(t),y(t),z(t))
\end{equation}

The equations of motion for the particle are found by applying the Lagrange's equation for each coordinate.

For the x coordinate:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\left( {\frac{\partial \mathcal{L}}{\partial \dot{x}}}
\right) = \frac{\partial \mathcal{L}}{\partial x }
\end{equation}

And the derivatives of $\mathcal{L}$ are given by:

\begin{equation} \begin{array}{rcl}
&\dfrac{\partial \mathcal{L}}{\partial x} &=& -\dfrac{\partial \mathbf{V}}{\partial x} \\
&\dfrac{\partial \mathcal{L}}{\partial \dot{x}} &=& m\dot{x} \\
&\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{x}}} \right) &=& m\ddot{x}
\end{array} \end{equation}

hence:

\begin{equation}
m\ddot{x} = -\frac{\partial \mathbf{V}}{\partial x}
\end{equation}

and similarly for the $y$ and $z$ coordinates.

For instance, if the conservative force is due to the gravitational field near Earth's surface $(\mathbf{V}=[0, mgy, 0])$, the Lagrange's equations (the equations of motion) are:

\begin{equation} \begin{array}{rcl}
m\ddot{x} &=& -\dfrac{\partial (0)}{\partial x} = 0 \\
m\ddot{y} &=& -\dfrac{\partial (mgy)}{\partial y} = -mg \\
m\ddot{z} &=& -\dfrac{\partial (0)}{\partial z} = 0
\end{array} \end{equation}

### Example 2: Ideal mass-spring system

Consider a system with a mass $m$ attached to an ideal spring (massless) with spring constant $k$ at the horizontal direction $x$. If the system is perturbed (a force is momentarily applied to the mass), we know the mass will oscillate around the rest position of the spring. Let's deduce the equation of motion for this system using the Lagrangian mechanics.
The Lagrangian $(\mathcal{L} = T - V)$ of the system is:

\begin{equation}
\mathcal{L}(x,\dot x, t) = \frac{1}{2}m\dot x^2(t) - \frac{1}{2}kx^2(t)
\end{equation}

The derivatives of $L$ are given by:

\begin{equation} \begin{array}{rcl}
&\dfrac{\partial \mathcal{L}}{\partial x} &=& -kx \\
&\dfrac{\partial \mathcal{L}}{\partial \dot{x}} &=& m\dot{x} \\
&\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{x}}} \right) &=& m\ddot{x}
\end{array} \end{equation}

And the Lagrange's equation (the equation of motion) is:

\begin{equation}
m\ddot{x} + kx = 0
\end{equation}

## Generalized coordinates

The direct application of Newton's laws to mechanical systems results in a set of equations of motion in terms of Cartesian coordinates of each of the particles that make up the system. In many cases, this is not the most convenient coordinate system to solve the problem or describe the movement of the system. For example, in problems involving many particles, it may be convenient to choose a system that includes the coordinate of the center of mass. Another example is a serial chain of rigid links, such as a member of the human body or from a robot manipulator, it may be simpler to describe the positions of each link by the angles between links.

Coordinate systems such as these are referred as [generalized coordinates](https://en.wikipedia.org/wiki/Generalized_coordinates). Generalized coordinates uniquely specify the positions of the particles in a system. Although there may be several generalized coordinates to describe a system, usually a judicious choice of generalized coordinates provides the minimum number of independent coordinates that define the configuration of a system (which is the number of degrees of freedom of the system), turning the problem simpler to solve.

Being a little more technical, according to [Wikipedia](https://en.wikipedia.org/wiki/Configuration_space_(physics)):
"In classical mechanics, the parameters that define the configuration of a system are called generalized coordinates, and the vector space defined by these coordinates is called the configuration space of the physical system. It is often the case that these parameters satisfy mathematical constraints, such that the set of actual configurations of the system is a manifold in the space of generalized coordinates. This manifold is called the configuration manifold of the system."

In problems where it is desired to use generalized coordinates, one can write Newton's equations of motion in terms of Cartesian coordinates and then transform them into generalized coordinates. However, it would be desirable and convenient to have a general method that would directly establish the equations of motion in terms of a set of convenient generalized coordinates. In addition, general methods for writing, and perhaps solving, the equations of motion in terms of any coordinate system would also be desirable. The [Lagrangian mechanics](https://en.wikipedia.org/wiki/Lagrangian_mechanics) is such a method.

### Lagrange's equation in generalized coordinates

We have deduced the Lagrange's equation in Cartesian Coordinates from the Newton's law just because it was a simple form of getting to the final equations. But, by no means the Lagrangian Mechanics should be viewed as a consequence of Newton's laws and specific to Cartesian Coordinates. The Lagrangian Mechanics could be deduced completely independent of Newton's law.

The Lagrange's equation can be expressed in terms of generalized coordinates what makes the Lagrangian formalism even more powerful. In fact, we will have the same equation as we deduced before (the only explicit difference will be that we will use $q_i$ instead of the Cartesian coordinate).
See [this notebook](http://nbviewer.jupyter.org/github/BMClab/bmc/blob/master/notebooks/lagrangian_mechanics_generalized.ipynb) for a deduction of the Lagrange's equation in generalized coordinates.

Defining the Lagrange or Lagrangian function of a system with $N$ generalized coordinates:

\begin{equation}
\mathcal{L} \equiv \mathcal{L}(q_1,\dotsc,q_{N} ,\dot{q}_1,\dotsc,\dot{q}_{N} ) = T - V
\label{eq46}
\end{equation}

We have the Lagrange's equation:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\left( {\frac{\partial \mathcal{L}}{\partial \dot{q}_i }}
\right)-\frac{\partial \mathcal{L}}{\partial q_i } = Q_{NCi} \quad i=1,\dotsc,N
\label{eq47}
\end{equation}

Where $Q_{NCi}$ is the generalized force due to a non-conservative force, any force that can't be expressed in terms of a potential.

Once all derivatives of the Lagrangian function are calculated, this equation will be the equation of motion for each particle. If there are $N$ generalized coordinates to define the configuration of a system, there will be $N$ equations for the system.

The set of equations above for a system are known as Euler–Lagrange equations, or Lagrange's equations of the second kind.

### Example 3: Simple pendulum under the influence of gravity

Consider a pendulum with a massless rod of length $d$ and a mass $m$ at the extremity swinging in a plane forming the angle $\theta$ with vertical, $g=10 m/s^2$ and the origin of the coordinate system at the point of the pendulum suspension.

Although the pendulum moves at the plane, it only has one degree of freedom, which can be described by the angle $\theta$, the generalized coordinate. It is not difficult to find the equation of motion using Newton's law, but let's find it using the Lagrangian mechanics.

The kinetic energy is:

\begin{equation}
T = \frac{1}{2}md^2\dot\theta^2
\end{equation}

And the potential energy is:

\begin{equation}
V = -mgd\cos\theta
\end{equation}

The Lagrangian function is:

\begin{equation}
\mathcal{L}(\theta, \dot\theta, t) = \frac{1}{2}md^2\dot\theta^2(t) + mgd\cos\theta(t)
\end{equation}

And the derivatives are given by:

\begin{equation} \begin{array}{rcl}
&\dfrac{\partial \mathcal{L}}{\partial \theta} &=& -mgd\sin\theta \\
&\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}} &=& md^2\dot{\theta} \\
&\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}}} \right) &=& md^2\ddot{\theta}
\end{array} \end{equation}

Finally, the Lagrange's equation (the equation of motion) is:

\begin{equation}
md^2\ddot\theta + mgd\sin\theta = 0
\end{equation}

#### Example 3a: Numerical solution of the equation of motion for the simple pendulum

A classical approach to solve the equation of motion for the simple pendulum is to consider the motion for small angles where $\sin\theta \approx \theta$ and the differential equation is linearized to $d\ddot\theta + g\theta = 0$. This equation has an analytical solution of the type $\theta(t) = A \sin(\omega t + \phi)$, where $\omega = \sqrt{g/d}$ and $A$ and $\phi$ are constants related to the initial position and velocity.
Let's solve the differential equation for the pendulum numerically using the [Euler’s method](https://nbviewer.jupyter.org/github/demotu/BMC/blob/master/notebooks/OrdinaryDifferentialEquation.ipynb#Euler-method).
Remember that we have to (1) transform the second-order ODE into two coupled first-order ODEs, (2) approximate the derivative of each variable by its discrete first order difference and (3) write equation to calculate the variable in a recursive way, updating its value with an equation based on the first order difference.
In Python:

```python
import numpy as np

def pendulum_euler(T, y0, v0, h):
    """
    Two coupled first-order ODEs for the pendulum (y=theta and v is a new variable):
        dydt = v
        dvdt = -g/d * y
    Two equations to update the values of the variables based on first-order difference:
        y[n+1] = y[n] + h*v[n]
        v[n+1] = v[n] + h*dvdt[n]
    """

    N = int(np.ceil(T/h))
    y, v = np.zeros(N), np.zeros(N)
    y[0], v[0] = y0, v0
    d = 2    # length of the pendulum in m
    g = 10   # acceleration of gravity in m/s2

    for i in range(N-1):
        y[i+1] = y[i] + h*v[i]
        v[i+1] = v[i] + h*(-g/d*y[i])
    t = np.linspace(0, T, N, endpoint=False)

    return t, y, v
```

```python
%matplotlib notebook
import matplotlib.pyplot as plt

def plot(t, y, v):
    """
    Plot data
    """
    fig, ax = plt.subplots(1, 1, figsize=(8, 3))
    ax.plot(t, y, 'b', label='Position')
    ax.plot(t, v, 'r', label='Velocity')
    ax.legend()
    plt.show()
```

```python
T, y0, v0, h = 10, 45*np.pi/180, 0, .001
t, theta, vtheta = pendulum_euler(T, y0, v0, h)
plot(t, theta, vtheta)
```

    <IPython.core.display.Javascript object>

### Example 4: Simple pendulum on moving cart

Consider a simple pendulum with massless rod of length $d$ and mass $m$ at the extremity of the rod forming an angle $\theta$ with the vertical direction under the action of gravity and $g=10 m/s^2$. The pendulum swings freely from a cart with mass $M$ that moves at the horizontal direction pushed by a force $F_x$.
Let's use the Lagrangian mechanics to derive the equations of motion for the system.

From the figure on the right, because of the constraints introduced by the constant length of the rod and the motion the cart can perform, good generalized coordinates to describe the configuration of the system are $x$ and $\theta$. Let's use these coordinates.

The positions of the cart (c) and of the pendulum tip (p) are:

$ x_c = x $
$ y_c = 0 $

$ x_p = x + d \sin \theta $
$ y_p = -d \cos \theta $

The velocities of the cart and of the pendulum are:

$ \dot{x}_c = \dot{x} $
$ \dot{y}_c = 0 $

$ \dot{x}_p = \dot{x} + d \dot{\theta} \cos \theta $
$ \dot{y}_p = d \dot{\theta} \sin \theta $

The kinetic energy of the system is:

\begin{equation}
T = \frac{1}{2} M \big(\dot x_c^2 + \dot y_c^2 \big) + \frac{1}{2}m\,\big( \dot x_p^2 + \dot y_p^2 \big)
\end{equation}

And the potential energy of the system is:

\begin{equation}
V = M g y_c + m g y_p
\end{equation}

The Lagrangian function is:

\begin{equation}\begin{array}{rcl}
\mathcal{L} &=& \frac{1}{2} M \dot x^2 + \frac{1}{2}m\,\bigg[ \big(\dot{x} + d \dot{\theta} \cos\theta \big)^2 + \big(d \dot{\theta} \sin \theta \big)^2 \bigg] + m g d \cos \theta \\
&=& \frac{1}{2} (M+m) \dot x^2 + m\dot{x}d\dot{\theta}\cos\theta + \frac{1}{2}md^2\dot{\theta}^2 + m g d \cos \theta
\end{array}\end{equation}

The derivatives w.r.t. $x$ are:

\begin{equation}\begin{array}{rcl}
\dfrac{\partial \mathcal{L}}{\partial x} &=& 0 \\
\dfrac{\partial \mathcal{L}}{\partial \dot{x}} &=& (M+m) \dot{x} + m d \dot{\theta} \cos \theta \\
\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{x}}} \right) &=& (M+m)\ddot{x} + m d \ddot{\theta} \cos \theta - m d \dot{\theta}^2 \sin \theta
\end{array}\end{equation}

The derivatives w.r.t. $\theta$ are:

\begin{equation}\begin{array}{rcl}
\dfrac{\partial \mathcal{L}}{\partial \theta} &=& -m\dot{x}d\dot{\theta}\sin\theta - mgd\sin\theta \\
\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}} &=& m\dot{x}d\cos\theta + md^2\dot{\theta} \\
\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}}} \right) &=& m\ddot{x}d\cos\theta - m\dot{x}d\dot{\theta}\sin\theta + md^2\ddot{\theta}
\end{array}\end{equation}

Finally, the Lagrange's equations (the equations of motion) are:

\begin{equation}\begin{array}{rcl}
(M+m)\ddot{x} + md\big(\ddot{\theta} \cos\theta - \dot{\theta}^2 \sin\theta\big) = F_x \\
\ddot{\theta} + \dfrac{\ddot{x}}{d}\cos\theta + \dfrac{g}{d}\sin\theta = 0
\end{array}\end{equation}

### Example 5: Double pendulum under the influence of gravity

Consider a double pendulum (one pendulum attached to another) with massless rods of length $d_1$ and $d_2$ and masses $m_1$ and $m_2$ at the extremities of each rod swinging in a plane forming the angles $\theta_1$ and $\theta_2$ with vertical and $g=10 m/s^2$.
This case could be solved using Newtonian mechanics, but it's not simple (e.g., see [this link](http://www.myphysicslab.com/pendulum/double-pendulum/double-pendulum-en.html)). Instead, let's use the Lagrangian mechanics to derive the equations of motion for each pendulum.
The system has two degrees of freedom and we need two generalized coordinates ($\theta_1, \theta_2$) to describe the system's configuration.

The position of masses $m_1$ and $m_2$ are:

$x_1 = d_1\sin\theta_1$
$y_1 = -d_1\cos\theta_1$

$x_2 = d_1\sin\theta_1 + d_2\sin\theta_2$
$y_2 = -d_1\cos\theta_1 - d_2\cos\theta_2$

The kinetic and potential energies of the system are:

$ T = \frac{1}{2}m_1(\dot x_1^2 + \dot y_1^2) + \frac{1}{2}m_2(\dot x_2^2 + \dot y_2^2) $

$ V = m_1gy_1 + m_2gy_2 $

Let's use Sympy to help us; in fact we could solve this problem entirely in Sympy, see [Lagrange’s method in Sympy](http://docs.sympy.org/latest/modules/physics/mechanics/lagrange.html), but for now let's do just the derivatives.

Let's import Sympy libraries and define some variables:

```python
from sympy import Symbol, symbols, cos, sin, Matrix, simplify, Eq, latex
from sympy.physics.mechanics import dynamicsymbols, mlatex, init_vprinting
init_vprinting()
from IPython.display import display, Math

t = Symbol('t')
d1, d2, m1, m2, g = symbols('d1 d2 m1 m2 g', positive=True)
a1, a2 = dynamicsymbols('theta1 theta2')
```

The positions and velocities of masses $m_1$ and $m_2$ are:

```python
x1, y1 = d1*sin(a1), -d1*cos(a1)
x2, y2 = d1*sin(a1) + d2*sin(a2), -d1*cos(a1) - d2*cos(a2)
x1d, y1d = x1.diff(t), y1.diff(t)
x2d, y2d = x2.diff(t), y2.diff(t)

display(Math(r'x_1=' + mlatex(x1) + r'\quad \text{and} \quad \dot{x}_1=' + mlatex(x1d)))
display(Math(r'y_1=' + mlatex(y1) + r'\quad \text{and} \quad \dot{y}_1=' + mlatex(y1d)))
display(Math(r'x_2=' + mlatex(x2) + r'\quad \text{and} \quad \dot{x}_2=' + mlatex(x2d)))
display(Math(r'y_2=' + mlatex(y2) + r'\quad \text{and} \quad \dot{y}_2=' + mlatex(y2d)))
```

$\displaystyle x_1=d_{1} \operatorname{sin}\left(\theta_{1}\right)\quad \text{and} \quad \dot{x}_1=d_{1} \operatorname{cos}\left(\theta_{1}\right) \dot{\theta}_{1}$

$\displaystyle y_1=- d_{1} \operatorname{cos}\left(\theta_{1}\right)\quad \text{and} \quad \dot{y}_1=d_{1} \operatorname{sin}\left(\theta_{1}\right) \dot{\theta}_{1}$

$\displaystyle x_2=d_{1} \operatorname{sin}\left(\theta_{1}\right) + d_{2} \operatorname{sin}\left(\theta_{2}\right)\quad \text{and} \quad \dot{x}_2=d_{1} \operatorname{cos}\left(\theta_{1}\right) \dot{\theta}_{1} + d_{2} \operatorname{cos}\left(\theta_{2}\right) \dot{\theta}_{2}$

$\displaystyle y_2=- d_{1} \operatorname{cos}\left(\theta_{1}\right) - d_{2} \operatorname{cos}\left(\theta_{2}\right)\quad \text{and} \quad \dot{y}_2=d_{1} \operatorname{sin}\left(\theta_{1}\right) \dot{\theta}_{1} + d_{2} \operatorname{sin}\left(\theta_{2}\right) \dot{\theta}_{2}$

The kinetic and potential energies of the system are:

```python
T = m1*(x1d**2 + y1d**2)/2 + m2*(x2d**2 + y2d**2)/2
V = m1*g*y1 + m2*g*y2

display(Math(r'T=' + mlatex(simplify(T))))
display(Math(r'V=' + mlatex(simplify(V))))
```

$\displaystyle T=\frac{d_{1}^{2} m_{1} \dot{\theta}_{1}^{2}}{2} + \frac{m_{2} \left(d_{1}^{2} \dot{\theta}_{1}^{2} + 2 d_{1} d_{2} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + d_{2}^{2} \dot{\theta}_{2}^{2}\right)}{2}$

$\displaystyle V=- g \left(d_{1} m_{1} \operatorname{cos}\left(\theta_{1}\right) + d_{1} m_{2} \operatorname{cos}\left(\theta_{1}\right) + d_{2} m_{2} \operatorname{cos}\left(\theta_{2}\right)\right)$

The Lagrangian function is:

```python
L = T - V

display(Math(r'\mathcal{L}=' + mlatex(simplify(L))))
```

$\displaystyle \mathcal{L}=\frac{d_{1}^{2} m_{1} \dot{\theta}_{1}^{2}}{2} + d_{1} g m_{1} \operatorname{cos}\left(\theta_{1}\right) + g m_{2} \left(d_{1} \operatorname{cos}\left(\theta_{1}\right) + d_{2} \operatorname{cos}\left(\theta_{2}\right)\right) + \frac{m_{2} \left(d_{1}^{2} \dot{\theta}_{1}^{2} + 2 d_{1} d_{2} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + d_{2}^{2} \dot{\theta}_{2}^{2}\right)}{2}$

And the derivatives are (let's write a function to automate this process):

```python
def lagrange_terms(L, *q, show=True):
    """Calculate terms of Lagrange equations given the Lagrangian and q's.
    """
    Lterms = []
    for qi in q:
        dLdqi = simplify(L.diff(qi))
        Lterms.append(dLdqi)
        dLdqdi = simplify(L.diff(qi.diff(t)))
        Lterms.append(dLdqdi)
        dtdLdqdi = simplify(dLdqdi.diff(t))
        Lterms.append(dtdLdqdi)
        if show:
            display(Math(r'w.r.t.\;%s:'%latex(qi.func)))
            display(Math(r'\dfrac{\partial\mathcal{L}}{\partial %s}='
                         %latex(qi.func) + mlatex(dLdqi)))
            display(Math(r'\dfrac{\partial\mathcal{L}}{\partial\dot{%s}}='
                         %latex(qi.func) + mlatex(dLdqdi)))
            display(Math(r'\dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{'+
                         r'\partial\mathcal{L}}{\partial\dot{%s}}}\right)='
                         %latex(qi.func) + mlatex(dtdLdqdi)))
    return Lterms
```

```python
Lterms = lagrange_terms(L, a1, a2)
```

$\displaystyle w.r.t.\;\theta_{1}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \theta_{1}}=- d_{1} \left(d_{2} m_{2} \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + g m_{1} \operatorname{sin}\left(\theta_{1}\right) + g m_{2} \operatorname{sin}\left(\theta_{1}\right)\right)$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{1}}}=d_{1} \left(d_{1} m_{1} \dot{\theta}_{1} + m_{2} \left(d_{1} \dot{\theta}_{1} + d_{2} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{2}\right)\right)$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{1}}}}\right)=d_{1} \left(d_{1} m_{1} \ddot{\theta}_{1} + m_{2} \left(d_{1} \ddot{\theta}_{1} - d_{2} \left(\dot{\theta}_{1} - \dot{\theta}_{2}\right) \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{2} + d_{2} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \ddot{\theta}_{2}\right)\right)$

$\displaystyle w.r.t.\;\theta_{2}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \theta_{2}}=d_{2} m_{2} \left(d_{1} \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} - g \operatorname{sin}\left(\theta_{2}\right)\right)$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{2}}}=d_{2} m_{2} \left(d_{1} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} + d_{2} \dot{\theta}_{2}\right)$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{2}}}}\right)=d_{2} m_{2} \left(- d_{1} \left(\dot{\theta}_{1} - \dot{\theta}_{2}\right) \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} + d_{1} \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \ddot{\theta}_{1} + d_{2} \ddot{\theta}_{2}\right)$

Finally, the Lagrange's equations (the equations of motion) are:

```python
for i in range(int(len(Lterms)/3)):
    display(Eq(simplify(Lterms[3*i+2]-Lterms[3*i]), 0))
```

The motion of a double pendulum is very interesting; most of times it presents a chaotic behavior, see for example [https://www.myphysicslab.com/pendulum/double-pendulum-en.html](https://www.myphysicslab.com/pendulum/double-pendulum-en.html).

#### Example 5a: Numerical solution of the equation of motion for the double pendulum

In order to solve numerically the ODEs for the double pendulum we will transform each equation above into two first ODEs. As the two variables $\ddot{\theta_1}$ and $\ddot{\theta_2}$ appear in both equations, first we have to rearrange the equations to find expressions for $\ddot{\theta_1}$ and $\ddot{\theta_2}$. Second, we define two variables and then we can use these equations as we did for the single pendulum.
But we should avoid to use the Euler's method because of the non-negligible error in the numerical integration in this case; more accurate methods such as [Runge-Kutta](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods) should be employed.

### Example 6: Double compound pendulum under the influence of gravity

Consider the double compound pendulum shown on the the right with length $d$ and mass $m$ of each rod swinging in a plane forming the angles $\theta_1$ and $\theta_2$ with vertical and $g=10 m/s^2$.
The system has two degrees of freedom and we need two generalized coordinates ($\theta_1, \theta_2$) to describe the system's configuration.

Let's use the Lagrangian mechanics to derive the equations of motion for each pendulum.

To calculate the potential and kinetic energy of the system, we will need to calculate the position and velocity of each pendulum. Now each pendulum is a rod with distributed mass and we will have to calculate the moment of rotational inertia of the rod. In this case, the kinetic energy of each pendulum will be given as the kinetic energy due to rotation of the pendulum plus the kinetic energy due to the speed of the center of mass of the pendulum, such that the total kinetic energy of the system is:

$ T = \overbrace{\underbrace{\frac{1}{2}I_{cm}\dot\theta_1^2}_{\text{rotation}} + \underbrace{\frac{1}{2}m(\dot x_{1,cm}^2 + \dot y_{1,cm}^2)}_{\text{translation}}}^{\text{pendulum 1}} + \overbrace{\underbrace{\frac{1}{2}I_{cm}\dot\theta_2^2}_{\text{rotation}} + \underbrace{\frac{1}{2}m(\dot x_{2,cm}^2 + \dot y_{2,cm}^2)}_{\text{translation}}}^{\text{pendulum 2}} $

And the potential energy of the system is:

$ V = mg\big(y_{1,cm} + y_{2,cm}\big) $

Let's use Sympy once again.

The position and velocity of the center of mass of the rods $1$ and $2$ are:

```python
d, m, g = symbols('d m g', positive=True)
a1, a2 = dynamicsymbols('theta1 theta2')
I = m*d*d/12

x1, y1 = d*sin(a1)/2, -d*cos(a1)/2
x2, y2 = d*sin(a1) + d*sin(a2)/2, -d*cos(a1) - d*cos(a2)/2
x1d, y1d = x1.diff(t), y1.diff(t)
x2d, y2d = x2.diff(t), y2.diff(t)

display(Math(r'x_1=' + mlatex(x1) + r'\quad \text{and} \quad \dot{x}_1=' + mlatex(x1d)))
display(Math(r'y_1=' + mlatex(y1) + r'\quad \text{and} \quad \dot{y}_1=' + mlatex(y1d)))
display(Math(r'x_2=' + mlatex(x2) + r'\quad \text{and} \quad \dot{x}_2=' + mlatex(x2d)))
display(Math(r'y_2=' + mlatex(y2) + r'\quad \text{and} \quad \dot{y}_2=' + mlatex(y2d)))
```

$\displaystyle x_1=\frac{d \operatorname{sin}\left(\theta_{1}\right)}{2}\quad \text{and} \quad \dot{x}_1=\frac{d \operatorname{cos}\left(\theta_{1}\right) \dot{\theta}_{1}}{2}$

$\displaystyle y_1=- \frac{d \operatorname{cos}\left(\theta_{1}\right)}{2}\quad \text{and} \quad \dot{y}_1=\frac{d \operatorname{sin}\left(\theta_{1}\right) \dot{\theta}_{1}}{2}$

$\displaystyle x_2=d \operatorname{sin}\left(\theta_{1}\right) + \frac{d \operatorname{sin}\left(\theta_{2}\right)}{2}\quad \text{and} \quad \dot{x}_2=d \operatorname{cos}\left(\theta_{1}\right) \dot{\theta}_{1} + \frac{d \operatorname{cos}\left(\theta_{2}\right) \dot{\theta}_{2}}{2}$

$\displaystyle y_2=- d \operatorname{cos}\left(\theta_{1}\right) - \frac{d \operatorname{cos}\left(\theta_{2}\right)}{2}\quad \text{and} \quad \dot{y}_2=d \operatorname{sin}\left(\theta_{1}\right) \dot{\theta}_{1} + \frac{d \operatorname{sin}\left(\theta_{2}\right) \dot{\theta}_{2}}{2}$

The kinetic and potential energies of the system are:

```python
T = I/2*(a1.diff(t))**2 + m*(x1d**2+y1d**2)/2 + I/2*(a2.diff(t))**2 + m*(x2d**2+y2d**2)/2
V = m*g*y1 + m*g*y2

display(Math(r'T=' + mlatex(simplify(T))))
display(Math(r'V=' + mlatex(simplify(V))))
```

$\displaystyle T=\frac{d^{2} m \left(3 \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + 4 \dot{\theta}_{1}^{2} + \dot{\theta}_{2}^{2}\right)}{6}$

$\displaystyle V=- \frac{d g m \left(3 \operatorname{cos}\left(\theta_{1}\right) + \operatorname{cos}\left(\theta_{2}\right)\right)}{2}$

The Lagrangian function is:

```python
L = T - V

display(Math(r'\mathcal{L}=' + mlatex(simplify(L))))
```

$\displaystyle \mathcal{L}=\frac{d m \left(3 d \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + 4 d \dot{\theta}_{1}^{2} + d \dot{\theta}_{2}^{2} + 9 g \operatorname{cos}\left(\theta_{1}\right) + 3 g \operatorname{cos}\left(\theta_{2}\right)\right)}{6}$

And the derivatives are (let's write a function to automate this process):

```python
Lterms = lagrange_terms(L, a1, a2)
```

$\displaystyle w.r.t.\;\theta_{1}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \theta_{1}}=- \frac{d m \left(d \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} + 3 g \operatorname{sin}\left(\theta_{1}\right)\right)}{2}$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{1}}}=\frac{d^{2} m \left(6 \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{2} + 16 \dot{\theta}_{1}\right)}{12}$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{1}}}}\right)=\frac{d^{2} m \left(- 3 \left(\dot{\theta}_{1} - \dot{\theta}_{2}\right) \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{2} + 3 \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \ddot{\theta}_{2} + 8 \ddot{\theta}_{1}\right)}{6}$

$\displaystyle w.r.t.\;\theta_{2}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \theta_{2}}=\frac{d m \left(d \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} \dot{\theta}_{2} - g \operatorname{sin}\left(\theta_{2}\right)\right)}{2}$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{2}}}=\frac{d^{2} m \left(6 \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} + 4 \dot{\theta}_{2}\right)}{12}$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\theta_{2}}}}\right)=\frac{d^{2} m \left(- 3 \left(\dot{\theta}_{1} - \dot{\theta}_{2}\right) \operatorname{sin}\left(\theta_{1} - \theta_{2}\right) \dot{\theta}_{1} + 3 \operatorname{cos}\left(\theta_{1} - \theta_{2}\right) \ddot{\theta}_{1} + 2 \ddot{\theta}_{2}\right)}{6}$

Finally, the Lagrange's equations (the equations of motion) are:

```python
for i in range(int(len(Lterms)/3)):
    display(Eq(simplify(Lterms[3*i+2]-Lterms[3*i]), 0))
```

### Example 7: Two masses and two springs under the influence of gravity

Consider a system composed by two masses and two springs attached in series with massless springs under gravity and a force on $m_2$ and $g=10 m/s^2$ as shown in the figure.
The system has two degrees of freedom and we need two generalized coordinates to describe the system's configuration, for example, ${y_1, y_2}$, the positions of masses $m_1, m_2$ w.r.t. the ceiling (the origin).

(We could also have used ${z_1, z_2}$, the position of mass $m_1$ w.r.t. the ceiling and the position of mass $m_2$ w.r.t. the mass $m_1$. But the first set of coordinates would be the only choice if we were solving the problem using Newtonian mechanics because we can only apply Newton's second law to an inertial frame of reference.)

The kinetic energy of the system is:

$ T = \frac{1}{2}m_1\dot y_1^2 + \frac{1}{2}m_2\dot y_2^2 $

The potential energy of the system is:

$ V = \frac{1}{2}k_1 (y_1-\ell_1)^2 + \frac{1}{2}k_2 ((y_2-y_1)-\ell_2)^2 - m_1gy_1 - m_2g y_2 $

Where $\ell_1, \ell_2$ are the resting lengths (constants) of the two springs because the elastic potential energy is proportional to the deformation of the spring. For simplicity, let's ignore the resting position of each spring, so:

$ V = \frac{1}{2}k_1 y_1^2 + \frac{1}{2}k_2 (y_2-y_1)^2 - m_1gy_1 - m_2g y_2 $

Sympy is our friend:

```python
from sympy import Symbol, symbols, cos, sin, Matrix, simplify, Eq, latex
from sympy.physics.mechanics import dynamicsymbols, mlatex, init_vprinting
init_vprinting()
from IPython.display import display, Math

t = Symbol('t')
m1, m2, g, k1, k2 = symbols('m1 m2 g k1 k2', positive=True)
y1, y2, F = dynamicsymbols('y1 y2 F')
```

The Lagrangian function is:

```python
y1d, y2d = y1.diff(t), y2.diff(t)

T = (m1*y1d**2)/2 + (m2*y2d**2)/2
V = (k1*y1**2)/2 + (k2*(y2-y1)**2)/2 - m1*g*y1 - m2*g*y2

#display(Math(r'T=' + mlatex(simplify(T))))
#display(Math(r'V=' + mlatex(simplify(V))))

L = T - V

display(Math(r'\mathcal{L}=' + mlatex(simplify(L))))
```

$\displaystyle \mathcal{L}=g m_{1} y_{1} + g m_{2} y_{2} - \frac{k_{1} y^{2}_{1}}{2} - \frac{k_{2} \left(y_{1} - y_{2}\right)^{2}}{2} + \frac{m_{1} \dot{y}_{1}^{2}}{2} + \frac{m_{2} \dot{y}_{2}^{2}}{2}$

And the derivatives are (using the function we wrote before to automate this process):

```python
Lterms = lagrange_terms(L, y1, y2)
```

$\displaystyle w.r.t.\;\operatorname{y_{1}}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \operatorname{y_{1}}}=g m_{1} - k_{1} y_{1} - k_{2} \left(y_{1} - y_{2}\right)$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{y_{1}}}}=m_{1} \dot{y}_{1}$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{y_{1}}}}}\right)=m_{1} \ddot{y}_{1}$

$\displaystyle w.r.t.\;\operatorname{y_{2}}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \operatorname{y_{2}}}=g m_{2} + k_{2} \left(y_{1} - y_{2}\right)$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{y_{2}}}}=m_{2} \dot{y}_{2}$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{y_{2}}}}}\right)=m_{2} \ddot{y}_{2}$

Finally, the Lagrange's equations (the equations of motion) are:

```python
display(Eq(simplify(Lterms[2]-Lterms[0]), 0))
display(Eq(simplify(Lterms[5]-Lterms[3]), F))
```

**Same problem, but with the other set of coordinates**

Using ${z_1, z_2}$ as the position of mass $m_1$ w.r.t. the ceiling and the position of mass $m_2$ w.r.t. the mass $m_1$, the solution is:

```python
z1, z2 = dynamicsymbols('z1 z2')
z1d, z2d = z1.diff(t), z2.diff(t)

T = (m1*z1d**2)/2 + (m2*(z1d + z2d)**2)/2
V = (k1*z1**2)/2 + (k2*z2**2)/2 - m1*g*z1 - m2*g*(z1+z2)

display(Math(r'T=' + mlatex(simplify(T))))
display(Math(r'V=' + mlatex(simplify(V))))

L = T - V

display(Math(r'\mathcal{L}=' + mlatex(simplify(L))))
```

$\displaystyle T=\frac{m_{1} \dot{z}_{1}^{2}}{2} + \frac{m_{2} \left(\dot{z}_{1} + \dot{z}_{2}\right)^{2}}{2}$

$\displaystyle V=- g m_{1} z_{1} - g m_{2} \left(z_{1} + z_{2}\right) + \frac{k_{1} z^{2}_{1}}{2} + \frac{k_{2} z^{2}_{2}}{2}$

$\displaystyle \mathcal{L}=g m_{1} z_{1} + g m_{2} \left(z_{1} + z_{2}\right) - \frac{k_{1} z^{2}_{1}}{2} - \frac{k_{2} z^{2}_{2}}{2} + \frac{m_{1} \dot{z}_{1}^{2}}{2} + \frac{m_{2} \left(\dot{z}_{1} + \dot{z}_{2}\right)^{2}}{2}$

```python
Lterms = lagrange_terms(L, z1, z2)
```

$\displaystyle w.r.t.\;\operatorname{z_{1}}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \operatorname{z_{1}}}=g m_{1} + g m_{2} - k_{1} z_{1}$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{z_{1}}}}=m_{1} \dot{z}_{1} + m_{2} \left(\dot{z}_{1} + \dot{z}_{2}\right)$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{z_{1}}}}}\right)=m_{1} \ddot{z}_{1} + m_{2} \left(\ddot{z}_{1} + \ddot{z}_{2}\right)$

$\displaystyle w.r.t.\;\operatorname{z_{2}}:$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial \operatorname{z_{2}}}=g m_{2} - k_{2} z_{2}$

$\displaystyle \dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{z_{2}}}}=m_{2} \left(\dot{z}_{1} + \dot{z}_{2}\right)$

$\displaystyle \dfrac{\mathrm d}{\mathrm{dt}}\left({\dfrac{\partial\mathcal{L}}{\partial\dot{\operatorname{z_{2}}}}}\right)=m_{2} \left(\ddot{z}_{1} + \ddot{z}_{2}\right)$

Finally, the Lagrange's equations (the equations of motion) are:

```python
display(Eq(simplify(Lterms[2]-Lterms[0]), F))
display(Eq(simplify(Lterms[5]-Lterms[3]), F))
```

The solutions using the two sets of coordinates seem different; the reader is invited to verify that in fact they are the same (remember that $y_1 = z_1,\, y_2 = z_1+z_2,\, \ddot{y}_2 = \ddot{z}_1+\ddot{z}_2$).

**Same problem, but considering the spring resting length**
And here is the solution considering the rest length of the spring:

```python
t = Symbol('t')
m1, m2, g, k1, k2 = symbols('m1 m2 g k1 k2', positive=True)
y1, y2, l1, l2, F = dynamicsymbols('y1 y2 ell1 ell2 F')

y1d, y2d = y1.diff(t), y2.diff(t)
T = (m1*y1d**2)/2 + (m2*y2d**2)/2
V = (k1*(y1-l1)**2)/2 + (k2*((y2-y1)-l2)**2)/2 - m1*g*y1 - m2*g*y2

L = T - V
display(Math(r'\mathcal{L}=' + mlatex(simplify(L))))

Lterms = lagrange_terms(L, y1, y2, show=False)

display(Eq(simplify(Lterms[2]-Lterms[0]), 0))
display(Eq(simplify(Lterms[5]-Lterms[3]), F))
```

## Non-conservative forces

The dissipation energy of a non-conservative system with a non-conservative force (e.g., the viscous force from a damper, which is proportional to velocity) can be expressed as:

\begin{equation}
D_i = \frac{1}{2}C \, \dot{q}_i^2
\label{dissipation}
\end{equation}

And the Lagrange's equation can be extended to include such non-conservative force in the following way:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\left( {\frac{\partial \mathcal{L}}{\partial \dot{q}_i }}
\right)-\frac{\partial \mathcal{L}}{\partial q_i } + \frac{\partial D_i}{\partial \dot{q}_i }= 0
\label{lagrange_dissip}
\end{equation}

### Example 8: Mass-spring-damper system

Consider a mass-spring-damper system with an external force acting on the mass.
The massless spring has a stiffness coefficient $k$ and length at rest $x_0$.
The massless damper has a damping coefficient $b$.
For simplicity, consider that the system starts at the resting position of the spring ($x=0$ at $x_0$).
The system has one degree of freedom and we need only one generalized coordinate ($x$) to describe the system's configuration.

Let's use the Lagrangian mechanics to derive the equations of motion for the system.

The kinetic energy of the system is:

\begin{equation}
T = \frac{1}{2} m \dot x^2
\end{equation}

The potential energy of the system is:

\begin{equation}
V = \frac{1}{2} k x^2
\end{equation}

The Lagrangian function is:

\begin{equation}
\mathcal{L} = \frac{1}{2} m \dot x^2 - \frac{1}{2} k x^2
\end{equation}

The dissipation energy of the system is:

\begin{equation}
D = \frac{1}{2} b \dot x^2
\end{equation}

Calculating all the terms in the Lagrange's equation for a dissipative process (cf. Eq. (\ref{lagrange_dissip})), the classical equation for a mass-spring-damper system can be found:

\begin{equation}
m\ddot{x} + b\dot{x} + kx = F(t)
\end{equation}

### Example 9: Mass-spring-damper system with gravity

Consider a mass-spring-damper system under the action of the gravitational force ($g=10 m/s^2$) and an external force acting on the mass.
The massless spring has a stiffness coefficient $k$ and length at rest $y_0$.
The massless damper has a damping coefficient $b$.
The gravitational force acts downwards and it is negative (see figure).
For simplicity, consider that the system starts at the resting position of the spring ($y=0$ at $y_0$).
The system has one degree of freedom and we need only one generalized coordinate ($y$) to describe the system's configuration.

Let's use the Lagrangian mechanics to derive the equations of motion for the system.

The kinetic energy of the system is:

\begin{equation}
T = \frac{1}{2} m \dot y^2
\end{equation}

The potential energy of the system is:

\begin{equation}
V = \frac{1}{2} k y^2 + m g y
\end{equation}

The Lagrangian function is:

\begin{equation}
\mathcal{L} = \frac{1}{2} m \dot y^2 - \frac{1}{2} k y^2 - m g y
\end{equation}

The dissipation energy of the system is:

\begin{equation}
D = \frac{1}{2} b \dot y^2
\end{equation}

The derivatives of the Lagrangian w.r.t. $y$ and $t$ are:

\begin{equation}\begin{array}{rcl}
\dfrac{\partial \mathcal{L}}{\partial y} &=& -ky - mg \\
\dfrac{\partial \mathcal{L}}{\partial \dot{y}} &=& m \dot{y} \\
\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{y}}} \right) &=& m\ddot{y}
\end{array}\end{equation}

The derivative of the dissipation energy w.r.t. $\dot{y}$ is:

\begin{equation}
\frac{\partial D_i}{\partial \dot{y}_i } = b \dot y
\end{equation}

Substituting all these terms in the Lagrange's equation:

\begin{equation}
\frac{\mathrm d }{\mathrm d t}\left( {\frac{\partial \mathcal{L}}{\partial \dot{q}_i }}
\right)-\frac{\partial \mathcal{L}}{\partial q_i } + \frac{\partial D_i}{\partial \dot{q}_i } = Q_{NCi}
\label{lagrange_dissip2}
\end{equation}

Results in:

\begin{equation}
m\ddot{y} + b\dot{y} + ky + mg = F_0 \cos(\omega t)
\end{equation}

#### Example 9a: Numerical solution of the equation of motion for mass-spring-damper system

Let's solve the differential equation for the pendulum numerically using the [Euler’s method](https://nbviewer.jupyter.org/github/demotu/BMC/blob/master/notebooks/OrdinaryDifferentialEquation.ipynb#Euler-method).
Remember that we have to (1) transform the second-order ODE into two coupled first-order ODEs, (2) approximate the derivative of each variable by its discrete first order difference and (3) write equation to calculate the variable in a recursive way, updating its value with an equation based on the first order difference.
In Python:

```python
import numpy as np

def msdg_euler(T, y0, v0, h):
    """
    Two coupled first-order ODEs for the pendulum (v is a new variable):
        dydt = v
        dvdt = (F0*np.cos(omega*t) - b*v - k*y - m*g)/m
    Two equations to update the values of the variables based on first-order difference:
        y[n+1] = y[n] + h*v[n]
        v[n+1] = v[n] + h*dvdt[n]
    """

    N = int(np.ceil(T/h))
    y, v = np.zeros(N), np.zeros(N)
    y[0], v[0] = y0, v0
    m = 1    # mass, kg
    k = 100  # spring coefficient, N/m
    b = 2    # damping coefficient, N/m/s
    F0 = 1   # external force, N
    w = 1    # angular frequency, Hz
    g = 10   # acceleration of gravity, m/s2

    t = np.linspace(0, T, N, endpoint=False)

    F = F0*np.cos(w*t)

    for i in range(N-1):
        y[i+1] = y[i] + h*v[i]
        v[i+1] = v[i] + h*((F[i] - b*v[i] - k*y[i] - m*g)/m)

    return t, y, v
```

```python
%matplotlib notebook
import matplotlib.pyplot as plt

def plot(t, y, v):
    """
    Plot data
    """
    fig, ax1 = plt.subplots(1, 1, figsize=(8, 3))
    ax1.set_title('Simulation of mass-spring-damper system under gravity and external force')
    ax1.plot(t, y, 'b', label='Position')
    ax1.set_xlabel('Time (s)')
    ax1.set_ylabel('Position (m)', color='b')
    ax1.tick_params('y', colors='b')
    ax2 = ax1.twinx()
    ax2.plot(t, v, 'r', label='Velocity')
    ax2.set_ylabel('Velocity (m/s)', color='r')
    ax2.tick_params('y', colors='r')
    plt.tight_layout()
    plt.show()
```

```python
T, y0, v0, h = 10, .1, 0, .01
t, y, v = msdg_euler(T, y0, v0, h)
plot(t, y, v)
```

    <IPython.core.display.Javascript object>

## Forces of constraint

The fact the Lagrangian formalism uses generalized coordinates means that in a system with constraints we typically have fewer coordinates (in turn, fewer equations of motion) and we don't need to worry about forces of constraint that we would have to consider in the Newtonian formalism.
However, when we do want to determine a force of constraint, using Lagrangian formalism in fact will be disadvantageous! Let's see now one way of determining a force of constraint using Lagrangian formalism. The trick is to postpone the consideration that there is a constraint in the system; this will increase the number of generalized coordinates but will allow the determination of a force of constraint.
Let's exemplify this approach determining the tension at the rod in the simple pendulum under the influence of gravity we saw earlier.

### Example 10: Force of constraint in a simple pendulum under the influence of gravity

Consider a pendulum with a massless rod of length $d$ and a mass $m$ at the extremity swinging in a plane forming the angle $\theta$ with vertical and $g=10 m/s^2$.

Although the pendulum moves at the plane, it only has one degree of freedom, which can be described by the angle $\theta$, the generalized coordinate. But because we want to determine the force of constraint tension at the rod, let's also consider for now the variable $r$ for the 'varying' length of the rod (instead of the constant $d$).

In this case, the kinetic energy of the system will be:

\begin{equation}
T = \frac{1}{2}mr^2\dot\theta^2 + \frac{1}{2}m\dot r^2
\end{equation}

And for the potential energy we will also have to consider the constraining potential, $V_r(r(t))$:

\begin{equation}
V = -mgr\cos\theta + V_r(r(t))
\end{equation}

The Lagrangian function is:

\begin{equation}
\mathcal{L}(\theta, \dot\theta, t) = \frac{1}{2}m(\dot r^2(t) + r^2(t)\,\dot\theta^2(t)) + mgr(t)\cos\theta(t) - V_r(r(t))
\end{equation}

The derivatives w.r.t. $\theta$ are:

\begin{equation} \begin{array}{rcl}
&\dfrac{\partial \mathcal{L}}{\partial \theta} &=& -mgr\sin\theta \\
&\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}} &=& mr^2\dot{\theta} \\
&\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{\theta}}} \right) &=& 2mr\dot{r}\dot{\theta} + mr^2\ddot{\theta}
\end{array} \end{equation}

The derivatives w.r.t. $r$ are:

\begin{equation} \begin{array}{rcl}
&\dfrac{\partial \mathcal{L}}{\partial r} &=& mr \dot\theta^2 + mg\cos\theta - \dot{V}_r(r) \\
&\dfrac{\partial \mathcal{L}}{\partial \dot{r}} &=& m\dot r \\
&\dfrac{\mathrm d }{\mathrm d t}\left( {\dfrac{\partial \mathcal{L}}{\partial \dot{r}}} \right) &=& m\ddot{r}
\end{array} \end{equation}

The Lagrange's equations (the equations of motion) are:

\begin{equation} \begin{array}{rcl}
&2mr\dot{r}\dot{\theta} + mr^2\ddot{\theta} + mgr\sin\theta &=& 0 \\
&m\ddot{r} - mr \dot\theta^2 - mg\cos\theta + \dot{V}_r(r) &=& 0 \\
\end{array} \end{equation}

Now, we will apply the constraint condition, $r(t)=d$. This means that $\dot{r}=\ddot{r}=0$.

With this constraint applied, the first Lagrange's equation is the equation for the simple pendulum:

\begin{equation}
md^2\ddot{\theta} + mgd\sin\theta = 0
\end{equation}

The second equation yields:

\begin{equation}
-\dfrac{\mathrm d V_r}{\mathrm d r}\bigg{\rvert}_{r=d} = - md \dot\theta^2 - mg\cos\theta
\end{equation}

But the tension force, $F_T$, is by definition equal to the gradient of the constraining potential, so:

\begin{equation}
F_T = - md \dot\theta^2 - mg\cos\theta
\end{equation}

As expected, the tension at the rod is proportional to the centripetal and the gravitational forces.

## Considerations on the Lagrangian mechanics

The Lagrangian mechanics does not constitute a new theory in classical mechanics; the results of a Lagrangian or Newtonian analysis must be the same for any mechanical system, only the method used to obtain the results is different.

We are accustomed to think of mechanical systems in terms of vector quantities such as force, velocity, angular momentum, torque, etc., but in the Lagrangian formalism the equations of motion are obtained entirely in terms of the kinetic and potential energies (scalar operations) in the configuration space. Another important aspect of the force vs. energy analogy is that in situations where it is not possible to make explicit all the forces acting on the body, it is still possible to obtain expressions for the kinetic and potential energies.

In fact, the concept of force does not enter into Lagrangian mechanics. This is an important property of the method. Since energy is a scalar quantity, the Lagrangian function for a system is invariant for coordinate transformations. Therefore, it is possible to move from a certain configuration space (in which the equations of motion can be somewhat complicated) to a space that can be chosen to allow maximum simplification of the problem.

## Convention to report the equations of motion

There is an elegant form to display the equations of motion using generalized coordinates $q_i$ grouping the terms proportional to common quantities in matrices, see for example, Craig (2005, page 180), Pandy (2001), and Zatsiorsky (2002, page 383):

\begin{equation}
\quad M(q_i)\ddot{q_i} + C(q_i,\dot{q}_i) + G(q_i) = Q(q_i,\dot{q}_i)
\label{}
\end{equation}

Where, for a system with $N$ generalized coordinates:
- $M$ is the inertia matrix ($NxN$);
- $\ddot{q_i}$ is a matrix ($Nx1$ of generalized accelerations);
- $C$ is a matrix ($Nx1$) of [centripetal](http://en.wikipedia.org/wiki/Centripetal_force) and [Coriolis](http://en.wikipedia.org/wiki/Coriolis_effect) generalized forces;
- $G$ is a matrix ($Nx1$) of  gravitational generalized forces;
- $Q$ is a matrix ($Nx1$) of external generalized forces.

The reader is invited to express the equations of motion from the examples above in this form.

## Problems

1. Derive the Lagrange's equation (the equation of motion) for a mass-spring system where the spring is attached to the ceiling and the mass in hanging in the vertical.

2. Derive the Lagrange's equation for an inverted pendulum in the vertical.

3. Derive the Lagrange's equation for the following system:

4. Derive the Lagrange's equation for a spring pendulum, a simple pendulum where a mass $m$ is attached to a massless spring with spring constant $k$ and length at rest $d_0$.

5. Derive the Lagrange's equation for the system shown below.

6. Derive the Lagrange's equation for the following Atwood machine (consider that $m_1 > m_2$, i.e., the pulley will rotate counter-clockwise, and that moving down is in the positive direction):

7. Write computer programs (in Python!) to solve numerically the equations of motion from the problems above.

## References

- Marion JB (1970) [Classical Dynamics of particles and systems](https://books.google.com.br/books?id=Ss43BQAAQBAJ), 2nd ed., Academic Press.
- Synge JL (1949) [Principles of Mechanics](https://books.google.com.br/books?id=qsYfENCRG5QC), 2nd ed., McGraw-hill.
- Taylor J (2005) [Classical Mechanics](https://archive.org/details/JohnTaylorClassicalMechanics). University Science Books.


# Lecture 3: Digital Audio Signals
Audio Processing, MED4, Aalborg University, 2019

By Jesper Kjær Nielsen (jkn@create.aau.dk), Audio Analysis Lab, Aalborg University.

Last edited: 2019-02-18

Central aspects of the course
- [x] What is sound?
- [x] How is sound generated?
- [ ] **How is sound turned into signals (i.e., data) on a computer?**
- [ ] How can we analyse these signals (i.e. extract information from them)?
- [ ] How can we modify these signals?

Table of Contents

1  Sampling and reconstruction

1.1  Continuous-time signal

1.2  Sampling

1.3  Discrete-time signal

1.4  Reconstruction

1.5  Summary

1.6  Active 5 minutes break

2  Aliasing

2.1  Discrete-time sinusoid

2.2  Aliasing

2.3  Nyquist's sampling theorem

2.4  Anti-aliasing filter

2.5  Aliasing also occours in videos and images

2.6  Summary

3  Binary numbers

3.1  The decimal numer systems

3.2  The binary number system

3.3  Summary

3.4  Active five minutes break

4  Quantisation

4.1  Example: Storing $\pi$ on a computer

4.2  The need for quantisation

4.3  Uniform quantisation

4.4  Quantisation error

4.5  Dynamic range

4.6  Summary

## Sampling and reconstruction
In the next 20 minutes, you will learn
- What a **continuous-time** signal is.
- What a **discrete-time** signal is.
- How you turn a continuous-time signal into a discrete-time signal (i.e., **sampling**).
- How you turn a discrete-time signal into a continuous-time signal (i.e., **reconstruction**).

In the figure below, the following is happening:
1. An audio signal $p_\text{i}(t)$ is propagating through the air as **pressure variations**.
2. The microphone picks up the pressure variations and turns them into **voltage variations** $v_\text{i}(t)$.
3. The voltage is converted into a series of numbers $x_n$ via **sampling**.
4. A voltage signal $v_\text{o}(t)$ is **reconstructed** from the series of numbers $x_n$.
5. A loudspeakers converts the voltage variations into pressure variations $p_\text{o}(t)$.

### Continuous-time signal
A **continuous-time** signal is characterised by
- **time**: the signal has a value $x(t)$ for every possible time $t$
- **amplitude**: the signal value $x(t)$ can take on any value from a continuum of numbers (such as the real numbers).

A continuous-time signal is often also referred to as an **analog signal**.

Informally: You draw a continuous-time signal without lifting your pen from the paper.

### Sampling
- Storing a continuous-time signal on a computer requires an infinite amount of memory!
- Solution: We only measure the value of a continuous-time signal every $T_\text{s}$ seconds. This is called **sampling**.
- A very important quantity is
$$
    f_\text{s} = 1/T_\text{s}
$$
where
 - $f_\text{s}$ is the **sampling frequency** (measured in Hz) and describes how many times per second the continuous-time signal is sampled
 - $T_\text{s}$ is the **sampling time** (measured in seconds).

We can illustrate sampling by a person controlling a contact (see figure below):
1. When $T_\text{s}$ seconds has passed, the contact is pushed and released immidiately.
2. At that exact time instant, the value of the continuous-time signal $x(T_\text{s})$ is stored on the computer.
3. After another $T_\text{s}$ seconds, the contact is again pushed and released immidiately so that $x(2T_\text{s})$ is now stored.
4. If we keep pushing/relasing the contact every $T_\text{s}$ seconds, we will after $n$ times store the signal value $x(t_n)$ where
$$
    t_n = nT_\text{s} = n/f_\text{s}\ .
$$
The scaler $n$ is often referred to as the **sampling index**.

Note that people often write $x(t_n)$ as
$$
    x(t_n) = x_n = x[n] = x(n)\ .
$$

### Discrete-time signal
A **discrete-time** signal is characterised by
- **time**: the signal only has a value $x_n$ at certain times, i.e., $t_n=nT_\text{s}$ for $n=\cdots,-3,-2,1,0,1,2,3,\cdots$. Therefore, the $x$-axis is often the sampling index $n$ instead of time.
- **amplitude**: the signal value $x_n$ can take on any value from a continuum of numbers (such as the real numbers).

A discrete-time signal is sometimes also referred to as a **digital signal** (although we will use this term for something slightly different later).

Informally: A discrete-time signal is a series of time-ordered numbers.

### Reconstruction
If we want to play back a discrete-time signal on, e.g., a loudspeaker, we have to convert the discrete-time signal back into a continuous-time signal.
- Converting a discrete-time signal $x_n$ into a continuous-time signal $x(t)$ is called **reconstruction**.
- Reconstruction is performed using two components:
 - **hold circuit**: holds a value $x_n$ for $T_\text{s}$ seconds. This will create a **staircase** signal.
 - **post filter**: smooth out the discountinuities in the staircase signal by using a low-pass filter with a cut-off frequency of $f_\text{s}/2$ Hz.

---
Note that we will talk much more about filtering in the next lectures.

### Summary
1. A **continuous-time** signal $x(t)$ can be drawn without lifting the pen from the paper.
2. A **discrete-time** signal $x_n = x(t_n)$ is a series of time-ordered numbers.
3. **Sampling** converts a $x(t)$ into $x_n$ by measuring the value of $x(t)$ at the times
$$
    t_n = nT_\text{s} = n/f_\text{s}
$$
where
 - $n$ is the **sampling index**
 - $T_\text{s}$ is the **sampling time**
 - $f_\text{s}=1/T_\text{s}$ is the **sampling frequency**
4. **Reconstruction** converts $x_n$ into $x(t)$ by first creating a staircase signal from $x_n$ and then by filtering this staircase signal with a low-pass filter.

### Active 5 minutes break
Assume that we have a continuous-time signal given by
$$
    x(t) = \sin(2\pi f t)
$$
where the frequency of the sinusoid is $f=1$ Hz.
1. Sketch the signal from $t=-0.5$ s to $t= 2$ s.

We now sample the signal with a sampling frequency of $f_\text{s}=4$ Hz.
2. What is the sampling time $T_\text{s}$?
3. Sketch the sampled signal $x_n$ (in a new plot with the time index $n$ on the $x$-axis).
4. Sketch the staircase signal $x_\text{sc}(t)$ from $x_n$.
5. Repeat 2. to 4. with a sampling frequency of $f_\text{s}=1$ Hz.

## Aliasing
In the next 20 minutes, you will learn
- How we write a discrete-time sinusoid.
- What aliasing is.
- How we can avoid aliasing by selecting the sampling frequency $f_\text{s}$.
- What an anti-aliasing filter is and why we need it.

### Discrete-time sinusoid
As we have seen in the first two lectures, a continuous-time sinusoid can be written as
$$
    x(t) = A\cos(\Omega t+\psi)
$$
where
- $A\geq 0$ is an amplitude
- $\Omega=2\pi f$ is a frequency measured in rad/s
- $\psi$ is the inial phase.

Let us now sample this signal with a sampling frequency of $f_\text{s}$ Hz. We then get the discrete-time sinusoid
\begin{align}
    x_n &= x(t_n) = x(n/f_\text{s}) = A\cos(\Omega n/f_\text{s}+\psi) = A\cos((2\pi f/f_\text{s}) n+\psi)\\
    &= A\cos(\omega n+\psi)
\end{align}
where
- $\omega = \Omega f_\text{s}= 2\pi f/f_\text{s}$ is the **digital frequency** measured in **radians/sample**.

For a discrete-time signal, $\omega = 2\pi$ corresponds to the sampling frequency, and we will also write this frequency as $\omega_\text{s}$.

### Aliasing
- **Aliasing** comes from the word **alias**.
- It refers to that a sinusoidal component of one frequency is 'disguising' itself as a sinusoidal component with another frequency.

As an example, let us try to sample the three continuous-time sinusoids
\begin{align}
    x(t) &= \cos(2\pi f t)\\
    y(t) &= \cos(2\pi (f_\text{s}-f) t)
\end{align}
using a sampling frequency of $f_\text{s}$ Hz.

For the first sinusoid, we get that
$$
    x_n = \cos(\omega n)\ .
$$

For the second sinusoid, we get that
\begin{align}
    y_n &= \cos((\omega_\text{s}-\omega) n) = \text{Re}\left[\mathrm{e}^{j(\omega_\text{s}-\omega) n}\right] = \text{Re}\left[\mathrm{e}^{j\omega_\text{s} n}\mathrm{e}^{-j\omega n}\right]
\end{align}
However, we have that
$$
    \mathrm{e}^{j\omega_\text{s}n} = \mathrm{e}^{j2\pi n} = \cos(2\pi n)+j\sin(2\pi n) = 1
$$
for all time indices $n$ since $n$ is an integer. This means that
$$
    y_n = \text{Re}\left[\mathrm{e}^{-j\omega n}\right] = \cos(-\omega n) = \cos(\omega n)
$$
which is exactly the same as $x_n$.

**Observation:** Even though the continuous time signals $x(t)$ and $y(t)$ have **different frequencies**, the discrete-time signals $x_n$ and $y_n$ have the **same digital frequency**.

Some consequences:
- Reconstructing a continuous-time signal from $y_n$ results in $x(t)$ - not $y(t)$.
- We say that $y(t)$ has been **aliased** when we cannot recover it again after sampling it.
- A discrete-time sinusoid of digital frequency $\omega=2\pi f/f_\text{s}$, could be a sampled continuous-time sinusoid given by
$$
    y(t) = A\cos((\Omega+k2\pi f_\text{s})t + \psi)
$$
for any integer $k$.

```python
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def sinusoid(samplingIndices, digitalFreq):
    '''Compute a cosine'''
    return np.cos(2*np.pi*digitalFreq*samplingIndices)

nData = 100
samplingFreq = 100 # Hz
samplingTime = 1/samplingFreq # s
samplingIndices = np.arange(nData)
time = samplingIndices*samplingTime
freqA = 10 # Hz
freqB = 5 # Hz (samplingFreq-freqA)

# plot the results
plt.figure(figsize=(10,6))
plt.plot(time, sinusoid(samplingIndices,freqA/samplingFreq), linewidth=2, marker='o', label="$x(t)$")
plt.plot(time, sinusoid(samplingIndices,freqB/samplingFreq), linewidth=2, marker='o', label="$y(t)$")
plt.legend()
plt.xlim((time[0],time[nData-1])), plt.ylim((-1.5,1.5))
plt.xlabel('time [s]'), plt.ylabel('Amplitude [.]');
```

### Nyquist's sampling theorem
To avoid aliasing, the maximum frequency $f_\text{max}$ in a continuous-time signal must satisfy that
$$
    2f_\text{max} < f_\text{s}
$$
where $f_\text{s}$ is the sampling frequency.

We can satisfy the sampling theorem in two ways:
1. Select the sampling frequency $f_\text{s}$ high enough
2. Pre-filter the continuous-time signal with a low-pass filter (a so-called **anti-aliasing filter**) with a cut-off frequency below $f_\text{s}/2$.

#### Typical sampling frequencies used for recording audio
| Application | Sampling frequency $f_\text{s}$ |
|-------------|---------------------------------|
|CD           | 44100 Hz|
|Narrowband speech| 8000 Hz|
|Widebannd speech, VoIP| 16000 Hz|
|Video recorders| 48000 Hz|
|DVD-audio and Blu-ray| 96000 and 192000 Hz|

### Anti-aliasing filter
- Often, we do not know the highest frequency $f_\text{max}$ in our input signal.
- Instead, we simply filter out all frequency content above $f_\text{s}/2$ to avoid aliasing.
- This filter is called an **anti-aliasing filter** and is present in all practical sampling blocks.

### Aliasing also occours in videos and images

### Summary
1. A discrete-time sinusoid is written as
$$
    x_n = A\cos(\omega n +\psi)
$$
where $A$ and $\psi$ have the same meaning as for the continuous-time sinusoid and
- $\omega = 2\pi f/f_\text{s}$ is the **digital frequency** and measured in rad/sample
- $f_\text{s}$ is the **sampling frequency** measured in Hz.
2. Aliasing refers to when the frequency of a sinusoid is lowered due to undersampling the signal.
3. To avoid aliasing, we must satisfy **Nyquist's sampling theorem** stating that
$$
    2f_\text{max} < f_\text{s}
$$
where $f_\text{max}$ is the maximum frequency in the continuous-time input signal.
4. We can limit the maximum frequency of a continuous-time input signal by passing it through an **anti-aliasing filter**. This filter, which is a low-pass filter, ensures that aliasing does not occur.

## Binary numbers
In the next 20 minutes, you will learn
- What a binary number is
- What a bit and a byte is
- How you store data on, e.g., a computer or a CD
- To get the following slightly geeky joke :)

> There are 10 types of people in this world.
> Those who understand binary numbers and those who don't!

### The decimal numer systems
We are used to the decimal number system where we encounter numbers such 3, 42, and 89809.

Let's look at the example
$$
    1314\ .
$$
We can make the following observations about this decimal number:
- The number consists of four symbols, each called a **digit**
- Each digit can be one of ten possible symbols (either 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9).
- The order of the digits matter. For example, the right-most one in 1314 represents the number of 10s whereas the left-most one represents the number of 1000s. A number system with ordering is called a **positional number system**.

We can rewrite the decimal number 1314 as
\begin{align}
    1314 &= 1000 + 300 + 10 + 4\\
         &= 1\cdot 1000 + 3\cdot 100 + 1\cdot 10 + 4\cdot 1\\
         &= 1\cdot 10^3 + 3\cdot 10^2 + 1\cdot 10^1 + 4\cdot 10^0\ .
\end{align}

In general, we can write an $N$ digit decimal number $d_{N-1}d_{N-2}\cdots d_2d_1d_0$ as
$$
    d_{N-1}d_{N-2}\cdots d_2d_1d_0 = \sum_{n=0}^{N-1}d_n10^n\ .
$$

Note that
- $d_n\in\{0,1,2,3,4,5,6,7,8,9\}$
- 10 is the number of symbols that $d_n$ can take on and is called the **base** of the decimal number system.

Let us now allow for an arbitrary base $b$. Then we can write numbers as
$$
    d_{N-1}d_{N-2}\cdots d_2d_1d_0 = \sum_{n=0}^{N-1} d_n b^n
$$
where
- $d_n\in\{0,1,\ldots,b-1\}$
- $b$ is the base of the number.

For different values of $b$, we get different number systems. Some examples are
- $b=10$: The **decimal number system** with possible symbols 0,1,2,3,4,5,6,7,8,9
- $b=2$: The **binary number system** with possible symbols 0,1
- $b=16$: The **hexadecimal number system** with possible symbols 0,1,2,3,4,5,6,7,8,9, A, B, C, D, E, F

### The binary number system
A binary number
- has base 2 and
- is written only in terms of 0s and 1s.

An example of a binary number is
$$
    0110\ 1101_2
$$
where the subscript 2 is here only added to make it explicit that $0110\ 1101$ is a binary number.

Note that
- a 'digit' in a binary number is called a **bit**
- a collection of 8 bits is called a **byte** with symbol B
- a computer represents everything (numbers, colours, text, etc.) as binary numbers

Thus, the binary number $0110\ 1101_2$ has 8 bits and 1 byte (or 1 B)

#### Converting binary numbers to decimal numbers
To convert from a binary number to a decimal number, we simple use the expression
$$
    d_{N-1}d_{N-2}\cdots d_2d_1d_0 = \sum_{n=0}^{N-1} d_n b^n\ .
$$

As an example, we get that $1101_2$ converts to
\begin{align}
    1101_2 &= 1\cdot 2^3 + 1\cdot 2^2 + 0\cdot 2^1 + 1\cdot 2^0\\
    &= 1 \cdot 8 + 1 \cdot 4 + 0\cdot 2 + 1\cdot 1\\
    &= 13_{10}\ .
\end{align}

Converting from decimal numbers to binary numbers is also possible, but is not covered here.

#### Adding binary numbers
You do it exactly as you learned in 2nd grade with decimal numbers. That is,
- $0_2+0_2 = 0_2$
- $0_2+1_2 = 1_2$
- $1_2+1_2 = 0_2$ with $1_2$ in carry

Using these three rules, we obtain
$$
\begin{array}[t]{r}
    0100\ 1001_2 \\
+ \ 0111\ 1100_2 \\ \hline
    1100\ 0101_2
\end{array}
$$

#### Example: Representing text using binary numbers

</center

#### Example: Storing data on a disc
Information is stored by making tiny indentations known as **pits** on a disc.
- A pit represents a 0
- The opposite of a pit (called land) represents a 1

</center

- The binary data is stored along one long spiral on the disc.
 - **CD**: The spiral is 5.7 km long
 - **DVD**: The spiral is 12.3 km long
 - **Blu-ray**: The spiral is 28.4 km long
- A laser is used for reading the binary data by following this spiral path.

 There are 10 types of people in this world.
> Those who understand binary numbers and those who don't!

### Active five minutes break
1. Convert the following binary numbers to decimal numbers: $10_2$, $110_2$, and $0101_2$.
2. How many different binary numbers could you write with 4 bits?
3. How many bits can be stored on a 700 GB harddrive?
4. If you have an internet connection with a download speed of 10 Mbit/s, how much time does it then take to download a 100 MB file?

---
Bonusinfo:
1. With base $b=2$, a binary number can be written as
$$
    d_{N-1}d_{N-2}\cdots d_2d_1d_0 = \sum_{n=0}^{N-1} d_n b^n\ .
$$
2. G means billion ($10^9$), M means million ($10^6$), k means thousand ($10^3$), and B means byte (8 bit)

## Quantisation
In the next 20 minutes, you will learn
- What quantisation is and why it is necessary
- How you will typically do it
- What signal-to-noise ratio (SNR) and dynamic range is

### Example: Storing $\pi$ on a computer
How would you store $\pi$ (or other irrational numbers) on a computer?

</center

- It requires an **infinite** amount of memory to store $\pi$ on a computer.
- Therefore, we have to store an approximation to $\pi$ with only a **finite** number of digits. Let us call this approximation $p$.
- The approximation error $e$ can be written as
$$
    e = \pi-p\ .
$$

**Example**: Let $p$ contain only the first two digits of $\pi$ after the comma (i.e., $p=3.14$). Then
$$
    e = \pi-p = \pi-3.14 = 0.001592653589\ldots
$$

We say that we have rounded of (or **quantized**) $\pi$ to its nearest two-digits-after-the-comma representation.

### The need for quantisation
Sampling converts a continuous-time signal into a discrete-time signal. That is, we go from an infinite number of time values to a finite number of time values.

However, we also have to do something about the signal value $x(t_n)=x_n$ for every sampling time, so that we can store this number on the computer using a finite number of digits. This is called **quantisation**.

### Uniform quantisation
Assume that we sample the signal value $x_n$ and that
- the signal value is in the interval $(-\alpha,\alpha)$
- we have $\beta$ bits available for storing this signal value.
Note that we can represent $2^\beta$ different values with $\beta$ bits.

We now do the following.
1. Divide the interval $(-\alpha,\alpha)$ into $2^\beta$ equally large cells, each of size
$$
    \Delta = \frac{\alpha-(-\alpha)}{2^\beta} = \frac{2\alpha}{2^\beta} = \frac{\alpha}{2^{\beta-1}}\ .
$$
2. Round (or **quantise**) the signal value $x_n$ to the value $y_n$ at the nearest cell boundary, i.e.,
$$
    y_n = Q(x_n) = \Delta \left\lfloor\frac{x_n}{\Delta}\right\rceil = \Delta \left\lfloor\frac{x_n}{\Delta}+\frac{1}{2}\right\rfloor
$$
where $\lfloor\cdot\rceil$ and $\lfloor\cdot\rfloor$ refer to the rounding and flooring operations, respectively.

#### Example: 3 bit quantisation
In the figure below, the continuous-time signal (dashed gray) is first sampled (green) and then quantised (orange) using a three bit quantiser. The horisontal dashed red lines mark the quantisation levels. The final bit stream is
$$
    100\ 110\ 111\ 111\ 111\ 110\ 110\ 100\ 011\ 010\ 001\ 001\ .
$$

### Quantisation error
The quantisation error $e_n$ is the difference between the signal value $x_n$ and its rounded value $y_n=Q(x_n)$, i.e.,
$$
    e_n = x_n-Q(x_n)\ .
$$

We can rearrange this into
$$
    Q(x_n) = x_n+e_n\ .
$$
That is, we can think of quantisation as **adding an error** to the signal value $x_n$.

A measure of quantisation quality is how big the average power of $e_n$ is compared to the average power of $x_n$. The average power of, e.g., $e_n$ is defined as
$$
    P_e = \frac{1}{N}\sum_{n=0}^{N-1} e_n^2\ .
$$

If we define $P_x$ in a similar way, the signal-to-noise ratio (SNR) is defined as
$$
    \text{SNR} = 10\log_{10}\frac{P_x}{P_e}\ ,
$$
and it is measured in decibel (dB).

Now, assume that
- the signal values $x_n$ take on values in $(-\alpha,\alpha)$ equally often (a uniform distribution)
- the quantisation errors $e_n$ take on values in $(-\Delta/2,\Delta/2)$ equally often (a uniform distribution)

We then get
\begin{align}
    P_x &= \frac{(2\alpha)^2}{12} = \frac{\alpha^2}{3}\\
    P_e &= \frac{\Delta^2}{12} = \frac{1}{12}\left(\frac{\alpha}{2^{\beta-1}}\right)^2\ .
\end{align}
These results can be derived by computing the variance of a uniform distribution.

Finally, we get the SNR
\begin{align}
    \text{SNR} &= 10\log_{10}\frac{P_x}{P_e} = 10\log_{10}\left(\frac{\alpha^2}{3}12\left(\frac{2^{\beta-1}}{\alpha}\right)^2\right)\\
    &= 10\log_{10}\left(2^{2\beta}\right) = \beta 20 \log_{10} 2 \approx 6\beta\ .
\end{align}

Thus, for every additional bit, the SNR is improved by approximately 6 dB.

### Dynamic range
The **dynamic range** is the ratio between the loudest and softest values we can represent using a $\beta$ bit quantiser.
- **Softest value**: $1$
- **Loudest value**: $2^\beta$

The dynamic range of a quantiser is thus
$$
    \text{DR} = 10\log_{10}\left(\left(\frac{2^\beta}{1}\right)^2\right) = 10\log_{10}\left(2^{2\beta}\right) = \beta 20 \log_{10} 2 \approx 6\beta\ .
$$

Thus, we get a dynamic range of 96 dB for a 16 bit quantiser (typical CD quality) and 144 dB for a 24 bit quantiser. Note that the dynamic range of the human ear is approximately 120 dB.

### Summary
1. A quantiser rounds the signal values to a value on a grid.
2. All the points of the grid can be represented using $\beta$ bits which results in $2^\beta$ possible values.
3. Quantisation introduces noise into the digital signal. The signal-to-noise ratio (SNR) describes how powerful the signal is compared to this quantisation noise.
4. The SNR (and dynamic range) depends on the number of bits used as
$$
    \beta20\log_{10} \approx 6\beta\ .
$$


IPython à une syntaxes magiques pour travailler avec vos moteurs.

Cela suppose que vous avez démarré un cluster IPython, soit avec l'interface de l'ordinateur portable,
ou les commandes `ipcluster / controller / engine`.

```python
import ipyparallel as ipp

rc = ipp.Client()
dv = rc[:]
rc.ids
```

    [0, 1, 2, 3]

```python
%pxconfig --block
%px %matplotlib inline
```

```python
%%px
import numpy as np
import matplotlib.pyplot as plt
```

```python
dv.scatter('id', dv.targets, flatten=True)
dv['stride'] = len(dv)
```

```python
%%px --group-outputs=engine

x = np.linspace(0,np.pi,1000)
for n in range(id+1,12, stride):
    print(n)
    plt.figure()
    plt.plot(x,np.sin(n*x))
    plt.title("Plot %i" % n)
```

```python
from matplotlib.dates import date2num
from matplotlib.cm import gist_rainbow
import networkx as nx
from random import randint
from matplotlib.dates import date2num
from matplotlib.cm import gist_rainbow

def random_dag(nodes, edges):
    """Generate a random Directed Acyclic Graph (DAG) with a given number of nodes and edges."""
    G = nx.DiGraph()
    for i in range(nodes):
        G.add_node(i)
    while edges > 0:
        a = randint(0,nodes-1)
        b=a
        while b==a:
            b = randint(0,nodes-1)
        G.add_edge(a,b)
        if nx.is_directed_acyclic_graph(G):
            edges -= 1
        else:
            # we closed a loop!
            G.remove_edge(a,b)
    return G

def validate_tree(G, results):
    """Validate that jobs executed after their dependencies."""
    for node in G:
        started = results[node].metadata.started
        for parent in G.predecessors(node):
            finished = results[parent].metadata.completed
            assert started > finished, "%s should have happened after %s"%(node, parent)

G = nx.DiGraph()

# add 5 nodes, labeled 0-4:
map(G.add_node, range(5))
# 1,2 depend on 0:
G.add_edge(0,1)
G.add_edge(0,2)
# 3 depends on 1,2
G.add_edge(1,3)
G.add_edge(2,3)
# 4 depends on 1
G.add_edge(1,4)

# now draw the graph:
pos = { 0 : (0,0), 1 : (1,1), 2 : (-1,1),
        3 : (0,2), 4 : (2,2)}
nx.draw(G, pos, edge_color='r')

G = random_dag(5,5)

# now draw the graph:
pos = { 0 : (3,3), 1 : (4,4), 2 : (2,4),
        3 : (3,5), 4 : (5,5)}

nx.draw(G, pos, edge_color='r')

jobs = {}

for node in G:
       jobs[node] = randomwait

rc = ipp.Client()
view = rc.load_balanced_view()

results = {}

for node in nx.topological_sort(G):
           # get list of AsyncResult objects from nodes
           # leading into this one as dependencies
           deps = [ results[n] for n in G.predecessors(node) ]
           # submit and store AsyncResult object
           with view.temp_flags(after=deps, block=False):
                 results[node] = view.apply(jobs[node])

view.wait(results.values())

validate_tree(G, results)

pos = {}; colors = {}

for node in G:
            md = results[node].metadata
            start = date2num(md.started)
            runtime = date2num(md.completed) - start
            pos[node] = (start, runtime)
            colors[node] = md.engine_id

nx.draw(G, pos, node_list=colors.keys(), node_color=colors.values(),cmap=gist_rainbow)

```

```python
run pidigits.py
```

```python
import sympy

pi = sympy.pi.evalf(10000)

digits = (d for d in str(pi)[2:])

freqs = one_digit_freqs(digits)

plot_one_digit_freqs(freqs)
```

```python
executor = rc.become_dask(ncores=1)
executor
```

Client

Scheduler: tcp://192.168.1.19:51609

Cluster

Workers: 8

Cores: 8

Memory: 16.94 GB

```python
from distributed import progress

def square(x):
    return x ** 2

def neg(x):
        return -x

A = executor.map(square, range(1000))
B = executor.map(neg, A)
total = executor.submit(sum, B)
progress(total)
```

    VBox()

```python
def simulation_loop():
    from ipyparallel.datapub import publish_data
    import time
    import numpy as np
    for i in range(10):
        publish_data(dict(a=np.random.rand(20), i=i))
        time.sleep(2)
```

```python
ar = dv.apply_async(simulation_loop)
```

```python
import matplotlib.pyplot as plt
```

```python
data = ar.data
for i, d in enumerate(data):
    plt.plot(d['a'], label='engine: '+str(i))
plt.title('Data published at time step: ' + str(data[0]['i']))
plt.legend()
```


# Pyolite - A Python kernel backed by Pyodide

```python
import pyolite
pyolite.__version__
```

# Simple code execution

```python
a = 3
```

```python
a
```

```python
b = 89

def sq(x):
    return x * x

sq(b)
```

```python
print
```

# Redirected streams

```python
import sys

print("Error !!", file=sys.stderr)
```

# Error handling

```python
"Hello"

def dummy_function():
    import missing_module
```

```python
dummy_function()
```

# Code completion

### press `tab` to see what is available in `sys` module

```python
from sys import
```

# Code inspection

### using the question mark

```python
?print
```

### by pressing `shift+tab`

```python
print(
```

# Input support

```python
name = await input('Enter your name: ')
```

```python
'Hello, ' + name
```

# Rich representation

```python
from IPython.display import display, Markdown, HTML, JSON, Latex
```

## HTML

```python
print('Before display')

s = '<h1>HTML Title</h1>'
display(HTML(s))

print('After display')
```

## Markdown

```python
Markdown('''
# Title

**in bold**

~~Strikthrough~~
''')
```

## Pandas DataFrame

```python
import pandas as pd
import numpy as np
from string import ascii_uppercase as letters
from IPython.display import display

df = pd.DataFrame(np.random.randint(0, 100, size=(100, len(letters))), columns=list(letters))
df
```

### Show the same DataFrame

```python
df
```

## IPython.display module

```python
from IPython.display import clear_output, display, update_display
from asyncio import sleep
```

### Update display

```python
class Square:
    color = 'PeachPuff'
    def _repr_html_(self):
        return '''
        <div style="background: %s; width: 200px; height: 100px; border-radius: 10px;">
        </div>''' % self.color
square = Square()

display(square, display_id='some-square')
```

```python
square.color = 'OliveDrab'
update_display(square, display_id='some-square')
```

### Clear output

```python
print("hello")
await sleep(3)
clear_output()             # will flicker when replacing "hello" with "goodbye"
print("goodbye")
```

```python
print("hello")
await sleep(3)
clear_output(wait=True)   # prevents flickering
print("goodbye")
```

### Display classes

```python
from IPython.display import HTML
HTML('''
        <div style="background: aliceblue; width: 200px; height: 100px; border-radius: 10px;">
        </div>''')
```

```python
from IPython.display import Math
Math(r'F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx')
```

```python
from IPython.display import Latex
Latex(r"""\begin{eqnarray}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0
\end{eqnarray}""")
```

```python
from IPython.display import ProgressBar

for i in ProgressBar(10):
    await sleep(0.1)
```

```python
from IPython.display import JSON
JSON(['foo', {'bar': ('baz', None, 1.0, 2)}], metadata={}, expanded=True, root='test')
```

```python
from IPython.display import GeoJSON
GeoJSON(
  data={
      "type": "Feature",
      "geometry": {
          "type": "Point",
          "coordinates": [11.8, -45.04]
      }
  }, url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
  layer_options={
      "basemap_id": "celestia_mars-shaded-16k_global",
      "attribution" : "Celestia/praesepe",
      "tms": True,
      "minZoom" : 0,
      "maxZoom" : 5
  }
)
```

## Network requests and JSON

```python
import json
from js import fetch
```

```python
res = await fetch('https://httpbin.org/get')
text = await res.text()
obj = json.loads(text)
JSON(obj)
```

## Sympy

```python
from sympy import Integral, sqrt, symbols, init_printing

init_printing()

x = symbols('x')

Integral(sqrt(1 / x), x)
```

## Magics

```python
import os
os.listdir()
```

```python
%cd /home
```

```python
%pwd
```

```python
current_path = %pwd
print(current_path)
```

```python
%%writefile test.txt

This will create a new file.
With the text that you see here.
```

```python
%history
```

```python
import time
```

```python
%%timeit

time.sleep(0.1)
```


##### Copyright 2020 The TensorFlow Authors.

```
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
```

# 贫瘠高原

       在 TensorFlow.org 上查看
  在 Google Colab 中运行
       在 Github 上查看源代码
       下载笔记本

本教程将介绍 McClean 在 2019 年的一项研究的结果，这项研究解释了并非任何量子神经网络结构都能提供优秀的学习效果。特别是，您将看到某些大型随机量子电路系列不能有效地用作量子神经网络，因为它们几乎在各处都会出现梯度消失问题。在本例中，您不会针对特定的学习问题训练任何模型，重点是理解梯度行为的更简单问题。

## 设置

```
!pip install tensorflow==2.4.1
```

安装 TensorFlow Quantum：

```
!pip install tensorflow-quantum
```

```
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
```

现在，导入 TensorFlow 和模块依赖项：

```
import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

np.random.seed(1234)
```

## 1. 摘要

具有许多如下量子块的随机量子电路（$R_{P}(\theta)$ 为随机 Pauli 旋转）：

其中，如果将 $f(x)$ 定义为相对于任何量子位 $a$ 和 $b$ 的 $Z_{a}Z_{b}$ 的期望值，那么将存在一个问题，即 $f'(x)$ 的平均值将非常接近 0 且变化不大。具体请参见下文：

## 2. 生成随机电路

论文内提供的构造直观易懂。以下代码所实现的简单函数可以在一组量子位上生成具有给定深度的随机量子电路，有时称为*量子神经网络* (QNN)：

```
def generate_random_qnn(qubits, symbol, depth):
    """Generate random QNN's with the same structure from McClean et al."""
    circuit = cirq.Circuit()
    for qubit in qubits:
        circuit += cirq.ry(np.pi / 4.0)(qubit)

    for d in range(depth):
        # Add a series of single qubit rotations.
        for i, qubit in enumerate(qubits):
            random_n = np.random.uniform()
            random_rot = np.random.uniform(
            ) * 2.0 * np.pi if i != 0 or d != 0 else symbol
            if random_n > 2. / 3.:
                # Add a Z.
                circuit += cirq.rz(random_rot)(qubit)
            elif random_n > 1. / 3.:
                # Add a Y.
                circuit += cirq.ry(random_rot)(qubit)
            else:
                # Add a X.
                circuit += cirq.rx(random_rot)(qubit)

        # Add CZ ladder.
        for src, dest in zip(qubits, qubits[1:]):
            circuit += cirq.CZ(src, dest)

    return circuit

generate_random_qnn(cirq.GridQubit.rect(1, 3), sympy.Symbol('theta'), 2)
```

作者研究了单个参数 $\theta_{1,1}$ 的梯度。我们继续在 $\theta_{1,1}$ 所在电路中放置 `sympy.Symbol`。由于作者并没有分析电路中任何其他符号的统计信息，让我们立即将它们替换为随机值。

## 3. 运行电路

生成其中一些电路以及可观测对象以测试梯度变化不大的说法。首先，生成一批随机电路。选择一个随机 *ZZ* 可观测对象，并使用 TensorFlow Quantum 批量计算梯度和方差。

### 3.1 批量方差计算

让我们编写一个辅助函数，计算给定可观测对象的梯度在一批电路上的方差：

```
def process_batch(circuits, symbol, op):
    """Compute the variance of a batch of expectations w.r.t. op on each circuit that
    contains `symbol`. Note that this method sets up a new compute graph every time it is
    called so it isn't as performant as possible."""

    # Setup a simple layer to batch compute the expectation gradients.
    expectation = tfq.layers.Expectation()

    # Prep the inputs as tensors
    circuit_tensor = tfq.convert_to_tensor(circuits)
    values_tensor = tf.convert_to_tensor(
        np.random.uniform(0, 2 * np.pi, (n_circuits, 1)).astype(np.float32))

    # Use TensorFlow GradientTape to track gradients.
    with tf.GradientTape() as g:
        g.watch(values_tensor)
        forward = expectation(circuit_tensor,
                              operators=op,
                              symbol_names=[symbol],
                              symbol_values=values_tensor)

    # Return variance of gradients across all circuits.
    grads = g.gradient(forward, values_tensor)
    grad_var = tf.math.reduce_std(grads, axis=0)
    return grad_var.numpy()[0]
```

### 3.1 设置和运行

选择要生成的随机电路的数量及其深度，以及电路操作的量子位数。然后绘制结果。

```
n_qubits = [2 * i for i in range(2, 7)
           ]  # Ranges studied in paper are between 2 and 24.
depth = 50  # Ranges studied in paper are between 50 and 500.
n_circuits = 200
theta_var = []

for n in n_qubits:
    # Generate the random circuits and observable for the given n.
    qubits = cirq.GridQubit.rect(1, n)
    symbol = sympy.Symbol('theta')
    circuits = [
        generate_random_qnn(qubits, symbol, depth) for _ in range(n_circuits)
    ]
    op = cirq.Z(qubits[0]) * cirq.Z(qubits[1])
    theta_var.append(process_batch(circuits, symbol, op))

plt.semilogy(n_qubits, theta_var)
plt.title('Gradient Variance in QNNs')
plt.xlabel('n_qubits')
plt.ylabel('$\\partial \\theta$ variance')
plt.show()
```

此图表明，对于量子机器学习问题，不能仅凭猜测随机 QNN 拟设来希望达到最佳效果。为了使梯度变化幅度足以支持机器学习，模型电路中必须具备特定结构。

## 4. 启发式方法

Grant 于 2019 年提出了一种有趣的启发式方法，支持以近乎随机但又不完全随机的方式开始学习。作者使用了与 McClean 等人相同的电路，针对经典控制参数提出了一种不同的初始化技术，可化解“贫瘠高原”问题。这种初始化技术首先对一些层使用完全随机的控制参数，但在紧随其后的层中，将选择前几层的初始变换未完成的参数。作者称其为*标识块*。

这种启发式方法的优势在于，只需更改单个参数即可，当前块之外的所有其他块都将保持原有标识，而梯度信号将比之前更强。这种方法使用户可以通过选择需要修改的变量和块来获得强梯度信号。这种启发式方法并不能防止用户在训练阶段陷入“贫瘠高原”困境（并限制完全同步更新），但可以保证您在刚开始工作时位于“高原”之外。

### 4.1 构造新的 QNN

现在，构造一个函数来生成标识块 QNN。这与论文中的实现略有不同。目前，使单个参数的梯度行为与 McClean 等人的研究一致即可，因此可以进行一些简化。

要生成标识块并训练模型，通常需要 $U1(\theta_{1a}) U1(\theta_{1b})^{\dagger}$ 而非 $U1(\theta_1) U1(\theta_1)^{\dagger}$。最初，$\theta_{1a}$ 和 $\theta_{1b}$ 为相同的角度，但二者需独立学习。否则，即使经过训练后，您也将始终获得同一标识。标识块的数量需要根据经验进行选择。块越深，块中部的方差就越小。但在块的开头和结尾，参数梯度的方差应较大。

```
def generate_identity_qnn(qubits, symbol, block_depth, total_depth):
    """Generate random QNN's with the same structure from Grant et al."""
    circuit = cirq.Circuit()

    # Generate initial block with symbol.
    prep_and_U = generate_random_qnn(qubits, symbol, block_depth)
    circuit += prep_and_U

    # Generate dagger of initial block without symbol.
    U_dagger = (prep_and_U[1:])**-1
    circuit += cirq.resolve_parameters(
        U_dagger, param_resolver={symbol: np.random.uniform() * 2 * np.pi})

    for d in range(total_depth - 1):
        # Get a random QNN.
        prep_and_U_circuit = generate_random_qnn(
            qubits,
            np.random.uniform() * 2 * np.pi, block_depth)

        # Remove the state-prep component
        U_circuit = prep_and_U_circuit[1:]

        # Add U
        circuit += U_circuit

        # Add U^dagger
        circuit += U_circuit**-1

    return circuit

generate_identity_qnn(cirq.GridQubit.rect(1, 3), sympy.Symbol('theta'), 2, 2)
```

### 4.2 比较

在这里，您可以看到启发式方法确实有助于防止梯度方差迅速消失：

```
block_depth = 10
total_depth = 5

heuristic_theta_var = []

for n in n_qubits:
    # Generate the identity block circuits and observable for the given n.
    qubits = cirq.GridQubit.rect(1, n)
    symbol = sympy.Symbol('theta')
    circuits = [
        generate_identity_qnn(qubits, symbol, block_depth, total_depth)
        for _ in range(n_circuits)
    ]
    op = cirq.Z(qubits[0]) * cirq.Z(qubits[1])
    heuristic_theta_var.append(process_batch(circuits, symbol, op))

plt.semilogy(n_qubits, theta_var)
plt.semilogy(n_qubits, heuristic_theta_var)
plt.title('Heuristic vs. Random')
plt.xlabel('n_qubits')
plt.ylabel('$\\partial \\theta$ variance')
plt.show()
```

这是从（近乎）随机 QNN 获得更强梯度信号的一项重大改进。