# Tridimensional rigid body kinetics

Renato Naville Watanabe

The Newton-Euler laws, remain valid in the tridimensional motion of a rigid body (for revision on Newton-Euler laws, [see this notebook about these laws](newton_euler_equations.ipynb)).

\begin{equation}
    \vec{\bf{F}} = m\vec{\bf{a_{cm}}}
\end{equation}

\begin{equation}
    \vec{\bf{M_O}} = \frac{d\vec{\bf{H_O}}}{dt}
\end{equation}

The resultant force, resultant moments around a point O and the acceleration of the center of mass are computed in the same way of the two-dimensional case.

\begin{equation}
    \vec{\bf{F}} = \sum\limits_{i=1}^n \vec{\bf{F_n}}
\end{equation}

\begin{equation}
    \vec{\bf{M_O}} = \sum\limits_{i=1}^n \vec{\bf{r_{i/O}}} \times \vec{\bf{F_i}}
\end{equation}

\begin{equation}
    \vec{\bf{a_{cm}}} = \frac{d^2\vec{\bf{r_{cm}}}}{dt^2}
\end{equation}

The major difference that appears when we are dealing with tridimensional motions is to compute the derivative of the angular momentum. For the sake of simplicity, we will do the analysis considering the point O as the center of mass of the body.

We begin computing the angular momentum of a rigid body around its center of mass.

\begin{equation}
    \vec{\boldsymbol{H_{cm}}} =  \int_B \vec{\boldsymbol{r_{/cm}}} \times \vec{\boldsymbol{v}}\,dm = \int  \vec{\boldsymbol{r_{/cm}}} \times (\vec{\boldsymbol{\omega}}\times\vec{\boldsymbol{r_{/cm}}})\,dm
\end{equation}
where $\vec{\boldsymbol{r_{/cm}}}$ is the vector from the point O to the position of the infinitesimal mass considered in the integral. For simplicity of the notation, we will use $\vec{\boldsymbol{r}} = \vec{\boldsymbol{r_{/cm}}}$.

So, the angular momentum is:

\begin{align}
\begin{split}
 \vec{\boldsymbol{H_{cm}}} = \int  \vec{\boldsymbol{r}} \times (\vec{\boldsymbol{\omega}}\times\vec{\boldsymbol{r}})\,dm &=&
      \int  &(r_x\hat{\boldsymbol{i}} + r_y\hat{\boldsymbol{j}}+r_z\hat{\boldsymbol{k}}) \times \left[(\omega_x\hat{\boldsymbol{i}} + \omega_y\hat{\boldsymbol{j}}+\omega_z\hat{\boldsymbol{k}})\times(r_x\hat{\boldsymbol{i}} + r_y\hat{\boldsymbol{j}}+r_z\hat{\boldsymbol{k}})\right]\,dm =\\
      &=& \int &(r_x\hat{\boldsymbol{i}} + r_y\hat{\boldsymbol{j}}+r_z\hat{\boldsymbol{k}}) \times \left[(\omega_x r_y\hat{\boldsymbol{k}} - \omega_xr_z\hat{\boldsymbol{j}} - \omega_yr_x\hat{\boldsymbol{k}} + \omega_yr_z\hat{\boldsymbol{i}}+ \omega_zr_x\hat{\boldsymbol{j}}- \omega_zr_y\hat{\boldsymbol{i}}\right]\,dm =\\
      &=& \int&(-\omega_x r_xr_y\hat{\boldsymbol{j}} -\omega_x r_xr_z\hat{\boldsymbol{k}} +\omega_y r_x^2\hat{\boldsymbol{j}} + \omega_zr_x^2\hat{\boldsymbol{k}}+ \omega_xr_y^2\hat{\boldsymbol{i}} -\omega_yr_xr_y\hat{\boldsymbol{i}}-\\
      &&&-\omega_yr_yr_z\hat{\boldsymbol{k}}+\omega_zr_y^2\hat{\boldsymbol{k}}+\omega_x r_z^2\hat{\boldsymbol{i}} +\omega_y r_z^2\hat{\boldsymbol{j}} -\omega_z r_xr_z\hat{\boldsymbol{i}} - \omega_zr_yr_z\hat{\boldsymbol{j}})\,dm=\\
      &=&\bigg(&\int \omega_xr_y^2 -\omega_yr_xr_y+\omega_x r_z^2-\omega_z r_xr_z\,dm\bigg)\;\hat{\boldsymbol{i}}+\\
      & &+\bigg(&\int-\omega_x r_xr_y +\omega_y r_x^2 +\omega_y r_z^2  - \omega_zr_yr_z\,dm\bigg)\hat{\boldsymbol{j}}+\\
      & &+\bigg(&\int-\omega_x r_xr_z  + \omega_zr_x^2-\omega_yr_yr_z+\omega_zr_y^2 \,dm\bigg)\hat{\boldsymbol{k}}=\\
      &=&\bigg(\int &\omega_x(r_y^2+r_z^2)\,dm+  \int-\omega_yr_xr_y\,dm + \int-\omega_z r_xr_z\,dm\bigg)\;\hat{\boldsymbol{i}}+\\
      & &+&\bigg(\int-\omega_x r_xr_y\,dm +\int\omega_y (r_x^2 +r_z^2)\,dm + \int- \omega_zr_yr_z\,dm\bigg)\hat{\boldsymbol{j}}+\\
      & &+&\bigg(\int-\omega_x r_xr_z\,dm  + \int-\omega_yr_yr_z\,dm + \int \omega_z(r_x^2+r_y^2) \,dm\bigg)\hat{\boldsymbol{k}}=\\
      &=&&\bigg(\omega_x\int (r_y^2+r_z^2)\,dm+  \omega_y\int-r_xr_y\,dm + \omega_z\int- r_xr_z\,dm\bigg)\;\hat{\boldsymbol{i}}+\\
      & &+&\bigg(\omega_x\int- r_xr_y\,dm +\omega_y\int (r_x^2 +r_z^2)\,dm +  \omega_z\int-r_yr_z\,dm\bigg)\hat{\boldsymbol{j}}+\\
      & &+&\bigg(\omega_x\int- r_xr_z\,dm  + \omega_y\int-r_yr_z\,dm + \omega_z\int (r_x^2+r_y^2) \,dm\bigg)\hat{\boldsymbol{k}} = \\
      &=&&\bigg(\omega_xI_{xx}^{cm}+  \omega_yI_{xy}^{cm} + \omega_zI_{xz}^{cm}\bigg)\;\hat{\boldsymbol{i}}+\\
      & &+&\bigg(\omega_xI_{xy}^{cm} +\omega_yI_{yy}^{cm} +  \omega_zI_{yz}^{cm}\bigg)\hat{\boldsymbol{j}}+\\
      & &+&\bigg(\omega_xI_{yz}^{cm}  + \omega_yI_{yz}^{cm} + \omega_zI_{zz}^{cm}\bigg)\hat{\boldsymbol{k}}=\\
      &=&&\left[\begin{array}{ccc}I_{xx}^{cm}&I_{xy}^{cm}&I_{xz}^{cm}\\
                                  I_{xy}^{cm}&I_{yy}^{cm}&I_{yz}^{cm}\\
                                  I_{xz}^{cm}&I_{yz}^{cm}&I_{zz}^{cm}\end{array}\right]\cdot
               \left[\begin{array}{c}\omega_x\\\omega_y\\\omega_z \end{array}\right] = I\vec{\boldsymbol{\omega}}
      \end{split}
      \label{eq:angmom}
 \end{align}

 where this matrix is the Matrix of Inertia (or more rigorously speaking, Tensor of Inertia) as defined previously in [this notebook about moment of inertia](CenterOfMassAndMomentOfInertia.ipynb#matrixinertia).

 So, to compute the angular momentum of a body we have to multiply the matrix of inertia $I$ of the body by its angular velocity $\vec{\boldsymbol{\omega}}$. The problem on this approach is that the moments and products of inertia depends on the orientation of the body relative to the frame of reference. As they depend on the the distances of each point of the body to the axes, if the body is rotating, the matrix of inertia I will be different at each instant.

<figure>

The solution to this problem is to attach a frame of reference to the body. We will denote this frame of reference as $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$, with origin in the center of mass of the body. As can be noted from the figure below, the frame of reference moves along the body. Now the matrix of inertia $I$ will be constant relative to this new basis $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$.

<figure>

So, we can write the angular momentum vector relative to this new basis. To do this, we must write the angular velocity in this new basis:

\begin{equation}
    \vec{\boldsymbol{\omega}} = \omega_1\hat{\boldsymbol{e_1}} + \omega_2\hat{\boldsymbol{e_2}} + \omega_3\hat{\boldsymbol{e_3}}
\end{equation}

Note that this angular velocity is the same vector that we used previously in Eq. \eqref{eq:angmom}. We are just describing it in a basis attached to the body (local basis).

As we can choose the basis versors as we want, we can choose them so as to the products of inertia be equal to zero. This can always be done. If the body has axes of symmetry,  we can choose this axes (principal axes) as the direction of the basis to the products of inertia be equal to zero. Having the basis $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$, the matrix of inertia will be:

\begin{equation}
    I = \left[\begin{array}{ccc}I_1&0&0\\
          0&I_2&0\\
          0&0&I_3\end{array}\right]
\end{equation}

So, using the basis in the direction of the principal axes of the body the angular momentum simplifies to:

\begin{equation}
 \vec{\boldsymbol{H_{cm}}} = I\vec{\boldsymbol{\omega}} = I_1\omega_1 \hat{\boldsymbol{e_1}} + I_2\omega_2 \hat{\boldsymbol{e_2}} +I_3\omega_3 \hat{\boldsymbol{e_3}}
 \label{eq:angmomprinc}
\end{equation}

For the second Newton-Euler law, we must compute the derivative of the angular momentum. So, we derive the angular momentum in Eq. \eqref{eq:angmomprinc}. As the versors $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$ are varying in time, we must consider their derivatives.

\begin{equation}
    \frac{d\vec{\boldsymbol{H_{cm}}}}{dt} = I_1\dot{\omega_1}\hat{\boldsymbol{e_1}} + I_2\dot{\omega_2}\hat{\boldsymbol{e_2}}+I_3\dot{\omega_3}\hat{\boldsymbol{e_3}} + I_1\omega_1\frac{d\hat{\boldsymbol{e_1}}}{dt} + I_2\omega_2\frac{d\hat{\boldsymbol{e_2}}}{dt}+I_3\omega_3\frac{d\hat{\boldsymbol{e_3}}}{dt}
    \label{eq:derivangmom}
\end{equation}

Now it only remains to find an expression for the derivative of the versors $\frac{d\hat{\boldsymbol{e_1}}}{dt}$, $\frac{d\hat{\boldsymbol{e_2}}}{dt}$ and $\frac{d\hat{\boldsymbol{e_3}}}{dt}$.

It would be interesting to find some relation between these derivatives and the angular velocity of the body. So, now we will express the angular velocity $\vec{\boldsymbol{\omega}}$ in  terms of these derivatives. Remember that the angular velocity is described as a vector in the orthogonal plane of the rotation. ($\vec{\boldsymbol{\omega_1}} = \frac{d\theta_1}{dt}\hat{\boldsymbol{e_1}}$, $\vec{\boldsymbol{\omega_2}} = \frac{d\theta_2}{dt}\hat{\boldsymbol{e_2}}$ and $\vec{\boldsymbol{\omega_3}} = \frac{d\theta_3}{dt}\hat{\boldsymbol{e_3}}$). Note also that the derivative of the angle $\theta_1$ can be described as the projection of the vector $\frac{d\hat{\boldsymbol{e_2}}}{dt}$ on the vector $\hat{\boldsymbol{e_3}}$. This can be written by using the scalar product between these vectors: $ \frac{d\theta_1}{dt} = \frac{d\hat{\boldsymbol{e_2}}}{dt}\cdot \hat{\boldsymbol{e_3}}$. Similarly, the same is valid for the angles in the other two directions: $ \frac{d\theta_2}{dt} = \frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot \hat{\boldsymbol{e_1}}$ and $ \frac{d\theta_3}{dt} = \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}$.

<figure>

So, we can write the angular velocity as:

\begin{equation}
    \vec{\boldsymbol{\omega}} =  \left(\frac{d\hat{\boldsymbol{e_2}}}{dt}\cdot \hat{\boldsymbol{e_3}}\right) \hat{\boldsymbol{e_1}} + \left(\frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot \hat{\boldsymbol{e_1}}\right) \hat{\boldsymbol{e_2}} + \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}\right) \hat{\boldsymbol{e_3}}
    \label{eq:angvel}
\end{equation}

Now, we must isolate the derivative of the versors to substitute them in the Eq. \eqref{eq:derivangmom}. To isolate the derivative of the versor $\hat{\boldsymbol{e_1}}$, first we cross multiply both sides of the equation above by $\hat{\boldsymbol{e_1}}$:

\begin{equation}
    \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_1}} = - \left(\frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot \hat{\boldsymbol{e_1}}\right) \hat{\boldsymbol{e_3}} + \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}\right) \hat{\boldsymbol{e_2}}
\end{equation}

If we note that the term multipliying $\hat{\boldsymbol{e_3}}$ in the right side of the identity can be obtained by $\frac{d\hat{\boldsymbol{e_1}}\cdot\hat{\boldsymbol{e_3}} }{dt} = \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_3}} + \frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot\hat{\boldsymbol{e_1}} \rightarrow 0 = \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_3}} + \frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot\hat{\boldsymbol{e_1}} \rightarrow \frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot\hat{\boldsymbol{e_1}}  = - \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_3}} $ (the scalar product $\hat{\boldsymbol{e_1}}\cdot\hat{\boldsymbol{e_3}}$ is zero because these vectors are orthogonal ), we can write the equation above becomes:

\begin{equation}
    \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_1}} = \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_3}}\right) \hat{\boldsymbol{e_3}} + \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}\right) \hat{\boldsymbol{e_2}}
\end{equation}

Finally, we can note that $\frac{d\hat{\boldsymbol{e_1}}\cdot\hat{\boldsymbol{e_1}} }{dt} = \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_1}} + \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_1}} \rightarrow \frac{d(1)}{dt} = 2\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_1}} \rightarrow \frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_1}}  = 0 $. As this term is equal to zero, we can add it to the expression above:

\begin{equation}
    \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_1}} = \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot\hat{\boldsymbol{e_1}}\right)\hat{\boldsymbol{e_1}} +  \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_3}}\right) \hat{\boldsymbol{e_3}} + \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}\right) \hat{\boldsymbol{e_2}}
\end{equation}

Note that the expression above is just another manner to write the vector $\frac{d\hat{\boldsymbol{e_1}}}{dt}$, as any vector can be described by the sum of the projections on each of the versors forming a basis.

So, the derivative of the versor $\hat{\boldsymbol{e_1}}$  can be written as:

\begin{equation}
    \frac{d\hat{\boldsymbol{e_1}}}{dt} = \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_1}}
\end{equation}

Similarly, the derivatives of the versors $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$ can be written as:

\begin{equation}
    \frac{d\hat{\boldsymbol{e_2}}}{dt} = \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_2}} ~~~~~~~~\text{and} ~~~~~~ \frac{d\hat{\boldsymbol{e_3}}}{dt} = \vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_3}}
\end{equation}

Now we can get back to the equation describing the derivative of the angular momentum (Eq.\eqref{eq:derivangmom}):

\begin{align}
    \begin{split}
    \frac{d\vec{\boldsymbol{H_{cm}}}}{dt} =&I_1\dot{\omega_1}\hat{\boldsymbol{e_1}} + I_2\dot{\omega_2}\hat{\boldsymbol{e_2}}+I_3\dot{\omega_3}\hat{\boldsymbol{e_3}} + I_1\omega_1\frac{d\hat{\boldsymbol{e_1}}}{dt} + I_2\omega_2\frac{d\hat{\boldsymbol{e_2}}}{dt}+I_3\omega_3\frac{d\hat{\boldsymbol{e_3}}}{dt}=\\
    =& I_1\dot{\omega_1}\hat{\boldsymbol{e_1}} + I_2\dot{\omega_2}\hat{\boldsymbol{e_2}}+I_3\dot{\omega_3}\hat{\boldsymbol{e_3}} + I_1\omega_1(\vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_1}}) + I_2\omega_2(\vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_2}})+I_3\omega_3(\vec{\boldsymbol{\omega}} \times \hat{\boldsymbol{e_3}}) = \\
   =& I_1\dot{\omega_1}\hat{\boldsymbol{e_1}} + I_2\dot{\omega_2}\hat{\boldsymbol{e_2}}+I_3\dot{\omega_3}\hat{\boldsymbol{e_3}} + \vec{\boldsymbol{\omega}} \times I_1\omega_1\hat{\boldsymbol{e_1}} + \vec{\boldsymbol{\omega}} \times I_2\omega_2\hat{\boldsymbol{e_2}}+\vec{\boldsymbol{\omega}} \times I_3\omega_3\hat{\boldsymbol{e_3}} = \\
   =& I_1\dot{\omega_1}\hat{\boldsymbol{e_1}} + I_2\dot{\omega_2}\hat{\boldsymbol{e_2}}+I_3\dot{\omega_3}\hat{\boldsymbol{e_3}} + \vec{\boldsymbol{\omega}} \times (I_1\omega_1\hat{\boldsymbol{e_1}} + I_2\omega_2\hat{\boldsymbol{e_2}} +  I_3\omega_3\hat{\boldsymbol{e_3}})=\\
   =&I\vec{\dot{\boldsymbol{\omega}}} + \vec{\boldsymbol{\omega}} \times (I\vec{\boldsymbol{\omega}})
   \end{split}
   \label{eq:derivangmomVec}
\end{align}

Performing the cross products, we can get the expressions for each of the coordinates attached to the body:

\begin{align}
    \begin{split}
    \frac{d\vec{\boldsymbol{H_{cm}}}}{dt} =\left[\begin{array}{c}I_1\dot{\omega_1}\\I_2\dot{\omega_2}\\I_3\dot{\omega_3}\end{array}\right] + \left[\begin{array}{c}\omega_1\\\omega_2\\\omega_3\end{array}\right]  \times \left[\begin{array}{c}I_1\omega_1\\I_2\omega_2\\I_3\omega_3\end{array}\right]  = \left[\begin{array}{c}I_1\dot{\omega_1} + \omega_2\omega_3(I_3-I_2)\\I_2\dot{\omega_2}+\omega_1\omega_3(I_1-I_3)\\I_3\dot{\omega_3}+\omega_1\omega_2(I_2-I_1)\end{array}\right]
   \end{split}
\end{align}

Having computed the derivative of the angular momentum, we have the final forms of the Newton-Euler laws:

\begin{equation}
    F_x = ma_{cm_x}
\end{equation}

\begin{equation}
    F_y = ma_{cm_y}
\end{equation}

\begin{equation}
    F_z = ma_{cm_z}
\end{equation}

\begin{equation}
    M_{cm_1} = I_1\dot{\omega_1} + \omega_2\omega_3(I_3-I_2)
    \label{eq:M1}
\end{equation}

\begin{equation}
    M_{cm_2} = I_2\dot{\omega_2}+\omega_1\omega_3(I_1-I_3)
    \label{eq:M2}
\end{equation}

\begin{equation}
    M_{cm_3} = I_3\dot{\omega_3}+\omega_1\omega_2(I_2-I_1)
    \label{eq:M3}
\end{equation}

Note that the equations of the forces are written in the global frame of reference and the equations of the moments are described in the frame of reference of the body. So, before using Eq.~\eqref{eq:derivangmomVec} or the equations Eq.\eqref{eq:M1},\eqref{eq:M2} and \eqref{eq:M3} you must transform all the forces and moment-arms to the frame of reference of the body by using rotation matrices.

Below are shown some examples with tridimensional kinematic data to find the forces and moments acting on the body.

## Examples

### 1 ) 3D pendulum bar

 At the file '../data/3Dpendulum.txt' there are 3 seconds of data of 3 points of the tridimensional cylindrical pendulum, very similar to the pendulum shown in the [notebook about free-body diagrams](FreeBodyDiagramForRigidBodies.ipynb#pendulum), except that it can move in every direction. Also it has a motor at the upper part of the cylindrical bar producing torques to move the bar. It has mass $m=1$ kg, length $l=1$ m and radius $r=0.1$ m. The point m1 is at the upper part of the cylinder and is the origin of the system. The point m2 is at the center of mass of the cylinder and the point m3 is a point at the surface of the cylinder.

The free-body diagram of the 3d pendulum is depicted below. There is the gravitational force acting at the center of mass of gravity of the body and the torque $M_1$ due to the motor acting on the pendulum and the force $F_1$ due to the restraint at the upper part of the cylinder. Together with the forces, the local basis $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$ in the direction of the principal axes an origin at the center of mass of the body is also shown.

<figure>

The resultant forces acting on the cylinder is:

\begin{equation}
    \vec{\boldsymbol{F}} = \vec{\boldsymbol{F_O}} - mg\hat{\boldsymbol{k}}
\end{equation}

So, the first Newton-Euler law, at each component of the global  basis, is written as:

\begin{align}
    \begin{split}
        F_{O_x} &=  ma_{cm_x} &\rightarrow  F_{O_x} &=  ma_{cm_x} \\
        F_{O_y} &= ma_{cm_y} &\rightarrow  F_{O_y} &=  ma_{cm_y}\\
        F_{O_z} - mg &= ma_{cm_z} &\rightarrow  F_{O_z} &=  ma_{cm_z} + mg
    \end{split}
    \label{eq:fnependulum}
\end{align}

Now, the resultant moment applied to the body, computed relative to the center of mass, is:

\begin{equation}
    \vec{\boldsymbol{M}} = \vec{\boldsymbol{M_O}} + \vec{\boldsymbol{r_{O/cm}}} \times \vec{\boldsymbol{F_O}}
\end{equation}

So, the second Newton-Euler law, at each of the components at the local basis of the body, is written as:

\begin{align}
    \begin{split}
        M_{O_1} + MFocm_1 &= I_1\dot{\omega_1} + \omega_2\omega_3(I_3-I_2) \rightarrow M_{O_1} &= I_1\dot{\omega_1} + \omega_2\omega_3(I_3-I_2) -  MFocm_1\\
        M_{O_2} + MFocm_2 &= I_2\dot{\omega_2} + \omega_1\omega_3(I_1-I_3) \rightarrow M_{O_2} &= I_2\dot{\omega_2} + \omega_1\omega_3(I_1-I_3) -  MFocm_2\\
        M_{O_3} + MFocm_3 &= I_3\dot{\omega_3} + \omega_1\omega_2(I_2-I_1) \rightarrow M_{O_3} &= I_3\dot{\omega_3} + \omega_1\omega_2(I_2-I_1) -  MFocm_3
    \end{split}
\end{align}

where $\vec{\boldsymbol{MFocm}} = \vec{\boldsymbol{r_{O/cm}}} \times \vec{\boldsymbol{F_O}}$.

The moments of inertia at the directions of $\hat{\boldsymbol{e_1}}$, $\hat{\boldsymbol{e_2}}$ and $\hat{\boldsymbol{e_3}}$ are, $I_1 = \frac{mR^2}{12}$ and $I_2=I_3=\frac{m(3R^2+l^2)}{12}$. Now, to compute the moment $ \vec{\boldsymbol{M_O}}$ and the force  $\vec{\boldsymbol{F_O}}$, we will need to find the acceleration of the center of mass $ \vec{\boldsymbol{a_{cm}}}$, the angular velocity  $\vec{\boldsymbol{\omega}}$, the time-derivatives of each component of the angular velocity, and the moment-arm $\vec{\boldsymbol{r_{O/cm}}}$ to compute the torque due to the force $\vec{\boldsymbol{F_O}}$. These signals will come from the kinematic data file.

First,  we need to open the file with the data:

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib notebook
plt.rc('text', usetex=True)
plt.rc('font', family='serif')

data = np.loadtxt('../data/3dPendulum.txt', skiprows=1, delimiter = ',')
m = 1
g= 9.81
l = 1
r = 0.1

I1 = m*r**2/12
I2 = m*(3*r**2+l**2)/12
I3 = I2
```

Now, we assign the proper columns to variables:

```python
t = data[:,0]
m1 = data[:,1:4]
m2 = data[:,4:7]
m3 = data[:,7:]

```

As the center of mass is the data contained in m2, we can find the acceleration of the center of mass $\vec{\boldsymbol{a_{cm}}}$. This will be performed by deriving numerically the position of the center of mass twice. The numerical derivative of a function $f(t)$ with the samples $f(i)$ can be performed by taking the forward difference of the values $f(i)$:

\begin{equation}
    \frac{df}{dt}(i) = \frac{f(i+1)-f(i)}{\Delta t}
\end{equation}

The numerical derivative could be obtained by taking the backward differences as well:

\begin{equation}
    \frac{df}{dt}(i) = \frac{f(i)-f(i-1)}{\Delta t}
\end{equation}

A better estimation of the derivative of the time derivative of the function $f(t)$ would be obtained by the average value between the estimations using the backward and forward differences (this subject is treated [in this notebook about data filtering](DataFiltering.ipynb#numdiff)):

\begin{equation}
    \frac{df}{dt}(i) = \frac{\frac{f(i+1)-f(i)}{\Delta t} + \frac{f(i)-f(i-1)}{\Delta t}}{2} = \frac{f(i+1)-f(i-1)}{2\Delta t}
    \label{eq:centralderiv}
\end{equation}

So, the acceleration of the center of mass, is:

```python
dt = t[1]-t[0]

rcm = m2
vcm = (rcm[2:,:]-rcm[0:-2,:])/(2*dt)
acm = (vcm[2:,:]-vcm[0:-2,:])/(2*dt)

```

Now we can find the force $\vec{\boldsymbol{F_O}}$  using the Eq. \eqref{eq:fnependulum}.

```python
Fox = m*acm[:,0]
Foy = m*acm[:,1]
Foz = m*acm[:,2] + m*g
Fo=np.hstack((Fox.reshape(-1,1),Foy.reshape(-1,1),Foz.reshape(-1,1)))

plt.figure()
plt.plot(t[0:acm.shape[0]], Fox)
plt.plot(t[0:acm.shape[0]], Foy,'--')
plt.plot(t[0:acm.shape[0]], Foz)
plt.legend(('x','y','z'))
plt.show()

```

    <IPython.core.display.Javascript object>

Now, to find the moment being applied to the body, we need to compute a basis attached to the body

```python
e1 = m2 - m1
e1 = e1/np.linalg.norm(e1,axis=1,keepdims=True)

e2 = m3-m2
e2 = e2/np.linalg.norm(e2,axis=1,keepdims=True)

e3 = np.cross(e1,e2,axis=1)
e3 = e3/np.linalg.norm(e3,axis=1,keepdims=True)

e2 = np.cross(e3,e1, axis=1)
e2 = e2/np.linalg.norm(e2,axis=1,keepdims=True)

```

 To compute the moment applied to the body, we need the angular velocity described in the basis attached to the body. The easiest way to find this angular velocity is to use Eq. \eqref{eq:angvel}, repeated here.

 \begin{equation}
    \vec{\boldsymbol{\omega}} =  \left(\frac{d\hat{\boldsymbol{e_2}}}{dt}\cdot \hat{\boldsymbol{e_3}}\right) \hat{\boldsymbol{e_1}} + \left(\frac{d\hat{\boldsymbol{e_3}}}{dt}\cdot \hat{\boldsymbol{e_1}}\right) \hat{\boldsymbol{e_2}} + \left(\frac{d\hat{\boldsymbol{e_1}}}{dt}\cdot \hat{\boldsymbol{e_2}}\right) \hat{\boldsymbol{e_3}}
\end{equation}

 To do this we need the derivatives of the basis versors. This will also be performed with Eq. \eqref{eq:centralderiv}.

 To perform the computation of the angular velocity remember that the scalar product between two vectors is given by:

 \begin{equation}
         \vec{\bf{v}}\cdot\vec{\bf{w}} = \left[\begin{array}{c}v_x\\v_y\\v_z \end{array}\right]\cdot \left[\begin{array}{c}w_x\\w_y\\w_z \end{array}\right] =  v_x.w_x+v_yw_y+v_zw_z
 \end{equation}

```python
de1dt = (e1[2:,:]-e1[0:-2,:])/(2*dt)
de2dt = (e2[2:,:]-e2[0:-2,:])/(2*dt)
de3dt = (e3[2:,:]-e3[0:-2,:])/(2*dt)

omega = np.hstack((np.sum(de2dt*e3[1:-1,:], axis = 1).reshape(-1,1),
                   np.sum(de3dt*e1[1:-1,:], axis = 1).reshape(-1,1),
                   np.sum(de1dt*e2[1:-1,:], axis = 1).reshape(-1,1)))

```

From the angular velocity vector we can obtain the derivatives of each component of it, also needed to compute the moment applied to the body. To do this we will use Eq. \eqref{eq:centralderiv}:

```python
alpha = (omega[2:,:]-omega[0:-2,:])/(2*dt)

```

It remains to find the moment caused by the force $\vec{\boldsymbol{F_O}}$, $\vec{\boldsymbol{MFocm}} = \vec{\boldsymbol{r_{O/cm}}} \times \vec{\boldsymbol{F_O}}$. The moment-arm $\vec{\boldsymbol{r_{O/cm}}} =-\vec{\boldsymbol{r_{cm}}}$.

```python
MFocm = np.cross(-rcm[2:-2], Fo, axis = 1)
```

The problem is that this moment is in the global basis. We need to transform it to the local basis. This will be performed using the rotation matrix of the bar. Each row of this matrix is one of the basis versors. Note that at each instant the matrix of rotation $R$ will be different.  After the matrix is formed, we can find the components of the moment $\vec{\boldsymbol{MFocm}}$ in the local basis by multiplying the matrix of rotation $R$ by the vector $\vec{\boldsymbol{MFocm}}$.

```python
MFocmRotated = np.zeros_like(MFocm)
for i in range(MFocm.shape[0]):
    R = np.vstack((e1[i,:],e2[i,:],e3[i,:]))
    MFocmRotated[i,:]=R@MFocm[i,:]

```

```python
Mo1 = I1*alpha[:,0] + omega[0:alpha.shape[0],1]*omega[0:alpha.shape[0],2]*(I3-I2) - MFocmRotated[:,0]
Mo2 = I2*alpha[:,1] + omega[0:alpha.shape[0],0]*omega[0:alpha.shape[0],2]*(I1-I3) - MFocmRotated[:,1]
Mo3 = I3*alpha[:,2] + omega[0:alpha.shape[0],0]*omega[0:alpha.shape[0],1]*(I2-I1) - MFocmRotated[:,2]
plt.figure()
plt.plot(t[2:-2], Mo1)
plt.plot(t[2:-2], Mo2)
plt.plot(t[2:-2], Mo3)
plt.legend(('$e_1$','$e_2$','$e_3$'))
plt.show()
```

    <IPython.core.display.Javascript object>

We could also have used the vectorial form of the derivative of the angular momentum (Eq. \eqref{eq:derivangmomVec}) and instead of writing three lines of code, write only one. The result is the same.

```python
I = np.array([[I1,0,0],[0,I2,0],[0,0,I3]])
Mo = (I@alpha.T).T  + np.cross(omega[0:alpha.shape[0],:], (I@omega[0:alpha.shape[0],:].T).T,axis=1) - MFocmRotated
plt.figure()
plt.plot(t[2:-2], Mo)
plt.legend(('$e_1$','$e_2$','$e_3$'))
plt.show()
```

    <IPython.core.display.Javascript object>

## 2 ) Data from postural control

This example will use real data from a subject during quiet standing during 60 seconds. This data is from the database freely available at [https://github.com/demotu/datasets/tree/master/PDS](https://github.com/demotu/datasets/tree/master/PDS). The data of this subject is in the file '../data/postureData.txt'.

The mass of the subject was $m = 53$ kg and her height was $h= 1.65 $ m.

   The free-body diagram is very similar to the free-body diagram shown [in the notebook about free-body diagram](FreeBodyDiagramForRigidBodies.ipynb#quietstanding), except that the force $\vec{\boldsymbol{F_A}}$ and the moment $\vec{\boldsymbol{M_A}}$ have components at all 3 directions.

   So, the first Newton-Euler law, at each component of the global  basis, is written as (note that in these data, the vertical direction is the y coordinate):

\begin{align}
    \begin{split}
        F_{A_x} &=  ma_{cm_x} &\rightarrow  F_{O_x} &=  ma_{cm_x} \\
        F_{A_y}  - mg &= ma_{cm_y} &\rightarrow  F_{O_y} &=  ma_{cm_y} + mg\\
        F_{A_z} &= ma_{cm_z} &\rightarrow  F_{O_z} &=  ma_{cm_z}
    \end{split}
    \label{eq:fnequiet}
\end{align}

Now, the resultant moment applied to the body, computed relative to the center of mass, is:

\begin{equation}
    \vec{\boldsymbol{M}} = \vec{\boldsymbol{M_A}} + \vec{\boldsymbol{r_{A/cm}}} \times \vec{\boldsymbol{F_A}}
\end{equation}

So, the second Newton-Euler law, at each of the components at the local basis of the body, is written as:

\begin{align}
    \begin{split}
        \vec{\boldsymbol{M_A}} + \vec{\boldsymbol{MFacm}} &= I\left[\begin{array}{c}\dot{\omega_1}\\\dot{\omega_2}\\\dot{\omega_3}\end{array}\right] + \vec{\boldsymbol{\omega}}\times  I\vec{\boldsymbol{\omega}} \rightarrow \vec{\boldsymbol{M_A}} = I\left[\begin{array}{c}\dot{\omega_1}\\\dot{\omega_2}\\\dot{\omega_3}\end{array}\right] + \vec{\boldsymbol{\omega}} \times  I\vec{\boldsymbol{\omega}}- \vec{\boldsymbol{MFacm}}
    \end{split}
\end{align}
where  $\vec{\boldsymbol{MFAcm}} = \vec{\boldsymbol{r_{A/cm}}} \times \vec{\boldsymbol{F_A}}$.

Now we open the data and assign the coordinates of each marker to a variable.

```python
data = np.loadtxt('../data/postureData.txt', skiprows=1, delimiter = ',')

t = data[:,0]
dt = t[1]-t[2]
rcm = data[:,1:4] #center of mass
rrA = data[:,4:7] # Right lateral malleolus
rlA = data[:,7:] # Left lateral maleolus

```

 The body will be approximated by a cylinder with the height of the subject and radius equal to half of the mean distances between the right and left medial malleoli.

```python
m = 53
h = 1.65
r = np.mean(np.linalg.norm(rrA-rlA, axis = 1))/2
I1 = m*r**2/12 # longitudnal
I2 = m*(3*r**2+h**2)/12 # sagittal
I3 = I2 # transversal

```

```python
# acceleration of the center of mass by deriving the center of mass twice
vcm = (rcm[2:,:]-rcm[0:-2,:])/(2*dt)
acm = (vcm[2:,:]-vcm[0:-2,:])/(2*dt)

FAx = m*acm[:,0]
FAy = m*acm[:,1] + m*g
FAz = m*acm[:,2]
FA=np.hstack((FAx.reshape(-1,1),FAy.reshape(-1,1),FAz.reshape(-1,1)))

```

Now we form the basis attached to the body. The first versor $\hat{\boldsymbol{e_1}}$ will be a versor from the midpoint between the medial malleoli and the center of mass of the body. The second versor $\hat{\boldsymbol{e_2}}$ will be a versor from  the right to the left malleolus. The third versor $\hat{\boldsymbol{e_3}}$ will be a cross product between $\hat{\boldsymbol{e_1}}$ and $\hat{\boldsymbol{e_2}}$.

```python
e1 = rcm - (rlA+rrA)/2
e1 = e1/np.linalg.norm(e1,axis=1,keepdims=True)

e2 = rlA-rrA
e2 = e2/np.linalg.norm(e2,axis=1,keepdims=True)

e3 = np.cross(e1,e2,axis=1)
e3 = e3/np.linalg.norm(e3,axis=1,keepdims=True)

e2 = np.cross(e3,e1, axis=1)
e2 = e2/np.linalg.norm(e2,axis=1,keepdims=True)

```

Now we can find the angular velocity $\vec{\boldsymbol{\omega}}$ at the basis attached to the body using Eq.\eqref{eq:angvel} and the time derivatives of its components.

```python
de1dt = (e1[2:,:]-e1[0:-2,:])/(2*dt)
de2dt = (e2[2:,:]-e2[0:-2,:])/(2*dt)
de3dt = (e3[2:,:]-e3[0:-2,:])/(2*dt)

omega = np.hstack((np.sum(de2dt*e3[1:-1,:], axis = 1).reshape(-1,1),
                   np.sum(de3dt*e1[1:-1,:], axis = 1).reshape(-1,1),
                   np.sum(de1dt*e2[1:-1,:], axis = 1).reshape(-1,1)))

alpha = (omega[2:,:]-omega[0:-2,:])/(2*dt)

```

Now we need to find the moment caused by the force at the ankles $\vec{\boldsymbol{F_A}}$, $\vec{\boldsymbol{MFAcm}} = \vec{\boldsymbol{r_{A/cm}}} \times \vec{\boldsymbol{F_A}}$. The moment-arm $\vec{\boldsymbol{r_{A/cm}}}$ is the vector from the center of mass to the midpoint of the lateral malleoli.

Besides the description of the moment due to the force $\vec{\boldsymbol{F_A}}$ in the basis attached to the body, we will also describe the force $\vec{\boldsymbol{F_A}}$ at the local basis. This is useful because it has an anatomical meaning. After this we can use the equations of Newton-Euler to obtain the moment at the ankle.

After having all signals described in the basis of the body, the moment being applied by the muscles of the ankle is computed using Eq. \eqref{eq:derivangmomVec}.

```python

#computing the moment due to the ankle force
racm = (rlA+rrA)/2-rcm
MFAcm = np.cross(racm[0:FA.shape[0],:], FA, axis =1)

MFAcmRotated = np.zeros_like(MFAcm)
FARotated = np.zeros_like(MFAcm)

# rotation matrix and description of the ankle force and moment due to the ankle force
for i in range(MFAcm.shape[0]):
    R = np.vstack((e1[i,:],e2[i,:],e3[i,:]))
    MFAcmRotated[i,:]=R@MFAcm[i,:]
    FARotated[i,:]=R@FA[i,:]

# Second Newton-Euler law to obtain the ankle moment

I = np.array([[I1,0,0],[0,I2,0],[0,0,I3]])
MA = (I@alpha.T).T  + np.cross(omega[0:alpha.shape[0],:], (I@omega[0:alpha.shape[0],:].T).T,axis=1) - MFAcmRotated

plt.figure()
plt.plot(t[2:-2], MA)
plt.legend(('longitudinal','sagittal','mediolateral'))
plt.title('Ankle Torque')
plt.xlabel('t (s)')
plt.ylabel('M (N.m)')

plt.show()

plt.figure()
plt.plot(t[2:-2], FARotated[:,0])
plt.plot(t[2:-2], FARotated[:,1])
plt.plot(t[2:-2], FARotated[:,2])
plt.title('Ankle Force')
plt.legend(('longitudinal','sagittal','mediolateral'))
plt.xlabel('t (s)')
plt.ylabel('F (N)')
plt.show()
```

    <IPython.core.display.Javascript object>

    <IPython.core.display.Javascript object>

## Problems

Compute the derivative of the angular momentum of the foot and the leg using one of  the following data acquired during the gait of a subject: ['../data/BiomecII2018_gait_d.txt'](../data/BiomecII2018_gait_d.txt) or ['../data/BiomecII2018_gait_n.txt'](../data/BiomecII2018_gait_n.txt).

## References

- Beer, F P; Johnston, E R; Cornwell, P. J.(2010) Vector Mechanics for Enginners: Dynamics.
- Kane T, Levinson D (1985) [Dynamics: Theory and Applications](https://ecommons.cornell.edu/handle/1813/638). McGraw-Hill, Inc
- Hibbeler, R. C. (2005) Engineering Mechanics: Dynamics.
- Taylor, J, R (2005) Classical Mechanics
- Winter D. A., (2009) Biomechanics and motor control of human movement. John Wiley and Sons.
- Santos DA, Fukuchi CA, Fukuchi RK, Duarte M. (2017) A data set with kinematic and ground reaction forces of human balance. PeerJ Preprints.

```python

```

notebooks/Tridimensional rigid body Kinetics.ipynb


### Instructions

When running the notebook the first time, make sure to run all cells before making changes in the notebook. Hit Shift + Enter to run the selected cell or, in the top menu, click on: `Kernel` > `Restart Kernel and Run All Cells...` to rerun the whole notebook. If you make any changes in a cell, rerun that cell.

If you make any changes in a coding cell, rerun the notebook by `Run` > `Run Selected Cell and All Below`

```python
# Import dependencies
import sys
sys.path.append('python/')        # Define path
import time
startTime = time.time()           # Calculate time for running this notebook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import ipywidgets as w
from matplotlib.widgets import RadioButtons
import load_galaxies as lg        # Load load_galaxies.py library
```

# Rotation Curve Plotting

There are three steps of the rotation curve building process:

1. Plotting the measured velocity data
2. Constructing the rotation curves for each component
3. Fitting the total velocity to the measured values

The following notebook explores the first and **the second step** of the process by walking you through the method of **building a rotation curve** for each component of the galaxy of your choice. The rotation of the bulge, disk and gas were traced from a scientific journal (Noordermeer 2008) for galaxy NGC 5533, or were imported from the Spitzer Photometry & Accurate Rotation Curves (SPARC) database for galaxies NGC 5005, NGC 7814, and NGC 891. These rotational velocities were derived from luminosities, surface brightness, and surface mass densities. However, the calculation is beyond the scope of this workshop. On the contrary, theoretical curves can also be obtained using empirically derived parameters, instead of full profiles.

_Insight: As a bonus in this workshop, the calculation for the theoretical curve of the bulge is introduced  in notebook 07\_Bonus\_Bulge\_Rotation\_Curve.ipynb_.

For this activity, you will calculate the rotation curve for the Dark Matter using an isothermal density profile and its two free parameters, central core density ($\rho_0$) and radius ($r_c$). As a final exercise, you will also plot the rotation curve with its total velocities and all of its components, and modify some of the parameters to experiment with the curve.

Activity 1)

>Choose your galaxy: Enter `galaxy = lg.GALAXYNAME` below to select your galaxy.

```python
galaxy = lg.NGC5533          # Options: lg.NGC5005 , lg.NGC5533 , lg.NGC7814 , lg.NGC0891
```

### Import measured radial velocity data of the chosen galaxy

```python
# Measured data
measured_data = galaxy['measured_data']

# Separate the columns of the data into arrays
m_radii = galaxy['m_radii']           # Radius
m_velocities = galaxy['m_velocities'] # Velocity
m_v_errors = galaxy['m_v_errors']     # Errors in velocity
```

### Import the light matter components

The structure of a galaxy can be separated into individual components based on their shape to simplify the process of producing a galactic rotation curve.

The bulge and disk are two stellar components of spiral galaxies. The less dense disk is made from the spiral arms and the bulge is the dense spherical core of the galaxy. The mass of these components is estimated using their luminosities, then given a mass-to-light ratio. The variation in the mass-to-light ratio means there is a wider range of masses that are plausible for the stellar components.

The gas is distributed somewhat evenly throughout the galaxy's disk and is composed of mainly Hydrogen. The gas's composition is determined by using light emissions spectra to identify the elements present. The element’s known molecular mass is used to calculate the gas components mass with higher accuracy. For this reason, gas rotation curves are often plotted with the gas parameters fixed, and they are multiplied by 1.33 to account for helium.

Note: the following data has been interpolated or 'splined' using `scipy.interpolate.BSpline`, which means the data points has been connected and turned into a function. Below, we have called these variables as functions of radius at selected points. For example: `bulge_v(r)`, `disk_v(r)`, `gas(r)`, etc.

```python
# Bulge spline
bulge_v = galaxy['bulge']['spline']
# Disk spline
disk_v = galaxy['disk']['spline']
# Gas spline
gas_v = galaxy['gas']['spline']
```

### For NGC 5533 ONLY: Calculate central supermassive black hole component

**Note: If you chose a galaxy other than NGC 5533, you can proceed to the section titled 'Calculate Dark Matter Halo Component' .**

Calculate the central black hole component for galaxy NGC 5533. The other galaxies have their black hole component included with their bulge, which scientific publications may do depending on the focus of their research. For example: In a two-component model, only the halo and disk are plotted (Casertano, 1983). In the case of NGC 7814, the bulge curve that is used in these calculations includes the possible black hole. (Note that this is not specified in the article (Fraternali, Sancisi, and Kamphuis 2011) but is suggested in the NASA website: https://www.nasa.gov/feature/goddard/caldwell-43.)

Due to the extreme mass and negligible radius, a central supermassive black hole is represented as a point value and the rotation curve can be calculated using the orbital velocity of a point-mass (mass is not a function of radius in this case).
>__Rotational velocity due to central supermassive black hole__:

\begin{equation}
v_{BH} = \sqrt\frac{GM_{BH}}{r}
\end{equation}

>where:

        $G$ = gravitational constant

        $M_{BH}$ = mass of central supermassive black hole

        $r$ = radius or distance from the center of the galaxy

Activity 2)

>Modify the radius value while keeping the mass of the black hole fixed to see how it affects the velocity of the stars and gas. Use an estimate for the black hole’s mass.

Activity 3)

>Based on the values measured, how might the black hole component be affecting the galaxy's rotational velocity?

Activity 4)

>Change the mass of the black hole at a fixed radius to see how it affects the velocity of stars and gas.

_Python help: Change the value of the mass of the black hole by changing the value for massbh. For example: `massbh = 3e10`_

```python
# Set Parameters
massbh = galaxy['massbh']        # Mass of the central black hole in (solar mass) imported from our Python library
radius = 10                      # Radius to calculate velocity due to black hole component (in kpc)
G = 4.300e-6                     # Gravitational constant (kpc/solar mass*(km/s)^2)

# Display the mass of the black hole
if massbh != 0:
    print("Mass of the central supermassive black hole at the center of the galaxy {}: {:.3e} solar masses".format(galaxy['galaxyname'],massbh))

# Equation for point-mass rotation curve
def blackhole_v(r,massbh):
    return np.sqrt((G*massbh)/r)

# Displaying the value of the velocity of stars and gas at a chosen radius
if massbh == 0:
    print("Galaxy {} might have a central supermassive black hole but it is included in the bulge curve.".format(galaxy['galaxyname']))
if massbh != 0:
    print("Radial velocity at r={}[kpc]: {:.3f} km/s".format(radius,blackhole_v(radius,massbh)))
```

    Mass of the central supermassive black hole at the center of the galaxy NGC 5533: 2.700e+09 solar masses
    Radial velocity at r=10[kpc]: 34.073 km/s

### Calculate Dark Matter Halo Component

The Isothermal Density Profile is used to model the structure of the Dark Matter halo. It takes two parameters, central core radius ($r_c$) and central core density ($\rho_0$). The density profile is used to find the enclosed mass, which is then used to determine the rotational velocity defined by the parameters $\rho_0$ and $r_c$ and evaluated for the best fit. (Jimenez et al. 2003).

>__Isothermal Density Profile__:

    \begin{equation}
    \rho(r) = \rho_{0}\left[1+{\left(\frac{r}{r_c}\right)}^{2}\right]^{-1}
    \end{equation}

>where:

    $\rho_{0}$ = core density

    $r_c$ = core radius

    $r$ = radius

>__Enclosed Mass__:

    \begin{equation}
    M_{DM}(r) = \int_{0}^{r} 4\pi\rho(r^{\prime})r^{\prime 2}d r^{\prime}
    \end{equation}

>where:

        $M_{DM}$ = mass of the dark matter halo

        $\rho(r)$ = density profile

>__Rotational Velocity__:

    \begin{equation}
    v_{DM}^{2}(r) = \frac{GM_{DM}(r)}{r}
    \end{equation}

    \begin{equation}
    v_{DM}(r) = \sqrt{4 \pi G \rho_{0} r_c^2 \big( 1- \frac{r_c}{r} \arctan{\frac{r}{r_c}}\big)}
    \end{equation}

>where:

        $G$ = gravitational constant

Activity 5)

>Looking at the Isothermal Density Profile equation above, consider what happens to $\rho_0$ at large and small radii. How does the density change as we look further from the center?

Activity 6)

>Change the value of the three variables `rho0` (for $\rho_0$) and `rc` (for $r_c$) and `radius`  in the next cell to see how the velocity of the dark matter changes at the selected radius.

```python
# Set parameters
rho0 = galaxy['rho0']   # central mass density (in solar mass/kpc^3)
rc = galaxy['rc']       # core radius (in kpc)
radius = 10             # radius (in kpc)

# Display the two dark matter parameters
print("Dark matter halo parameters: ")
print("     Central mass density: {:.2e} solar mass/kpc^3".format(rho0))
print("     Core radius: {:.2f} kpc".format(rc))

# Equation for dark matter halo velocity
def halo_v(r,rho0,rc):
    v = np.sqrt(4*np.pi*G*rho0*rc**2*(1 - rc/r * np.arctan(r/rc)))
    return v

# Display the velocity at the selected radius
print("     Velocity at {} kpc: {:.3f} km/s".format(radius,halo_v(radius,rho0,rc)))
```

    Dark matter halo parameters:
         Central mass density: 3.10e+08 solar mass/kpc^3
         Core radius: 1.40 kpc
         Velocity at 10 kpc: 162.022 km/s

### Calculate total velocity

All components of the galaxy contribute to the rotation curve or the total velocity of stars and gas. This can be added quadratically in the following way:

>__Total velocity__:

    \begin{equation}
    v_{total}(r) = \sqrt{v_{bulge}^2 + v_{disk}^2 + v_{gas}^2 + v_{blackhole}^2 + v_{dm\ halo}^2}
    \end{equation}

### Scaling parameter (Mass-to-Light ratio)

The scaling parameter is analogous to the mass-to-light ratio (M/L) that indicates how much mass it is expected from the light source. Because the mass is a rough estimation, the mass-to-light ratio is a good free parameter to use for curve fitting (see _08\_Interactive\_Fitting.ipynb_ module).

Suggestions on the scaling parameters:
- NGC 891 : Notice that the disk and the bulge rotation curve is much higher than the measured velocity (data was imported from SPARC). This discrepancy can be later fixed by fitting these curves to the measured data points. For now, you may scale it down to 1/2 or 3/4. (i.e. `bpref = 0.75`, `dpref = 0.5`)
- NGC 7814 : Similar to NGC 891, the disk and bulge should be scaled down to align with the measured velocities well. (i.e. `bpref = 0.75`, `dpref = 0.8`)

Activity 7)

>Modify the total rotational velocity WITHOUT the Dark Matter component by changing the scaling parameters of each galaxy component. For example: if you want the contribution from the disk to the rotation curve to be doubled, change `dpref = 1` to `dpref = 2`.

_Python help: Rerun the notebook to see the changes in the graph by hitting Shift+Enter on both the cells or by clicking on the menu: `Kernel` > `Restart and Run All Cells...`._

```python
# Scaling parameters
bpref = 0.75 #1      # Bulge
dpref = 0.75 #1      # Disk
gpref = 1      # Gas
bhpref = 1     # Central supermassive black hole
```

```python
# Total velocity WITHOUT dark matter halo component but with adjustable prefactors
def total_v_noDM(r,massbh):
    v = np.sqrt( (bpref * bulge_v(r))**2                   # bulge component + scaling parameter
               + (dpref * disk_v(r))**2                    # disk component + scaling parameter
               + (gpref * gas_v(r))**2                     # gas component + scaling parameter
               + (bhpref * blackhole_v(r,massbh))**2)      # central supermassive black hole component + scaling factor
    return v
```

Activity 8)

>Modify the total rotational velocity WITH the Dark Matter component by commenting out each galaxy component included in the _total_v_ function below to see how it affects the total rotation curve plotted at the end of the notebook. For example: remove the bulge by inserting a pound sign `#` before the term: `(bpref * bulge_v(r)**2`.

```python
# Total velocity WITH dark matter halo component
def total_v(r,massbh,rho0,rc):
    v = np.sqrt(
        (bpref * bulge_v(r))**2                  # bulge component
        + (dpref * disk_v(r))**2                 # disk component
        + (gpref * gas_v(r))**2                  # gas component
        + (bhpref * blackhole_v(r,massbh))**2    # central black hole component (this might be zero for NGC 891, NGC 7814 and NGC 5005 but it is because it's included in the bulge velocity)
        + halo_v(r,rho0,rc)**2)                  # dark matter halo component
    return v
```

### Plot all components and total velocity

In the graph at the end of this notebook, notice the difference between the total velocity with the Dark Matter halo component (red) and without the Dark Matter halo component (yellow).

Activity 9)

>Using the scaling parameters defined in Activty 7 above, are you able to scale the components of the total velocity without the Dark Matter component to match the curve of the measured data? Do these scaling parameters make sense or are they unreasonable?

_Insight: In the activity 08\_Interactive\_Fitting.ipynb, you won't have to guess these scaling parameters. Instead, you will be able to "fit" all curves to the measured data points and see what these multipliers might be._

```python
# Define radius for plotting
r_lowerlim = np.min(m_radii)                       # limit the radius to the min value of the measured radius
if r_lowerlim == 0:                                # getting rid of dividing by zero error
    r_lowerlim = 0.01
r_upperlim = np.max(m_radii)                       # limit the radius to the max value of the measured radius
r = np.linspace(r_lowerlim,r_upperlim,100)         # starting from zero results in a divide by zero error

# Plot
plt.figure(figsize=(10.0,7.0))                     # size of the plot
plt.plot(r, bpref * bulge_v(r),            color='m', label='Bulge')
plt.plot(r, dpref * disk_v(r),             color='b', label='Stellar disk')
plt.plot(r, gpref * gas_v(r),              color='c', label='Gas')
plt.plot(r, bhpref * blackhole_v(r,massbh), color='k', label='Central black hole')    # only for NGC 5533
plt.plot(r, halo_v(r,rho0,rc),     color='g', label='Dark Matter Halo')

plt.plot(r, total_v(r,massbh,rho0,rc), color='r',    label='Total curve with DM')
plt.plot(r, total_v_noDM(r,massbh),    color='gold', label='Total curve without DM')

plt.errorbar(m_radii, m_velocities, yerr=m_v_errors,
             marker='o', markersize=8,
             linestyle='none',
             label='{}'.format(galaxy['galaxyname']))

plt.title('Rotation curve of {}'.format(galaxy['galaxyname']), fontsize=16)     # giving the plot a title
plt.xlabel('Radius (kpc)', fontsize=12)                           # labeling the x-axis
plt.ylabel('Velocity (km/s)', fontsize=12)                        # labeling the y-axis
plt.xlim(0,r_upperlim)                                            # limits of the x-axis
plt.ylim(0,350)                                                   # limits of the y-axis
plt.legend(bbox_to_anchor=(1,1), loc="upper left")
plt.show()
```

```python
#NBVAL_IGNORE_OUTPUT
#Because the timing won't be exactly the same each time.
# Time
executionTime = (time.time() - startTime)
ttt = executionTime/60
print(f'Execution time: {ttt:.2f} minutes')
```

    Execution time: 0.02 minutes

### References

>Casertano, Stefano. **Rotation curve of the edge-on spiral galaxy NGC 5907: disc and halo masses.** _Monthly Notices of the Royal Astronomical Society_ 203, no. 3 (1983): 735-747. https://doi.org/10.1093/mnras/203.3.735.

>Fraternali, F., R. Sancisi, and P. Kamphuis. **A Tale of Two Galaxies: Light and Mass in NGC 891 and NGC 7814.** Astronomy & Astrophysics 531 (June 13, 2011). https://doi.org/10.1051/0004-6361/201116634.

>Jimenez, Raul, Licia Verde, and S. Peng Oh. **Dark halo properties from rotation curves.** _Monthly Notices of the Royal Astronomical Society_ 339, no. 1 (2003): 243-259. https://doi.org/10.1046/j.1365-8711.2003.06165.x.

>Kapteyn Institute. (n.d.). Groningen Image Processing System. **GIPSY, the GRONINGEN image processing system.** https://www.astro.rug.nl/~gipsy/index.html.

>Karukes, E. V., P. Salucci, and Gianfranco Gentile. **The dark matter distribution in the spiral NGC 3198 out to 0.22 $R_{vir}$.** _Astronomy & Astrophysics_ 578 (2015): A13. https://doi.org/10.1051/0004-6361/201425339.

>Noordermeer, Edo. **The rotation curves of flattened Sérsic bulges.** _Monthly Notices of the Royal Astronomical Society_ 385, no. 3 (2007): 1359-1364. https://doi.org/10.1111/j.1365-2966.2008.12837.x.

>Richards, Emily E., L. van Zee, K. L. Barnes, S. Staudaher, D. A. Dale, T. T. Braun, D. C. Wavle, et al. **Baryonic Distributions in the Dark Matter Halo of NGC 5005.** Monthly Notices of the Royal Astronomical Society 449, no. 4 (June 1, 2015): 3981–96. https://doi.org/10.1093/mnras/stv568.
***

### Images

>**NGC5005**: Based on observations made with the NASA/ESA Hubble Space Telescope, and obtained from the Hubble Legacy Archive, which is a collaboration between the Space Telescope Science Institute (STScI/NASA), the Space Telescope European Coordinating Facility (ST-ECF/ESA) and the Canadian Astronomy Data Centre (CADC/NRC/CSA).

>**NGC5533**: Sloan Digital Sky Survey, DR14 with SciServer https://www.sdss.org/dr14/imaging/jpg-images-on-skyserver/

>**NGC7814**: Adam Block/Mount Lemmon SkyCenter/University of Arizona http://www.caelumobservatory.com/gallery/n7814.shtml

>**NGC891**: Adam Block/Mount Lemmon SkyCenter/University of Arizona https://apod.nasa.gov/apod/ap170112.html


# ***Introduction to Radar Using Python and MATLAB***
## Andy Harrison - Copyright (C) 2019 Artech House
<br/>

# LFM Pulse Ambiguity Function
***

Referring to Section 8.6.2, the complex envelope of an LFM pulse is written as (Equation 8.65)

\begin{equation}\label{eq:single_lfm_pulse}
    s(t) = \text{rect}\bigg[ \frac{t}{\tau_p} \bigg]\, e^{j \pi \beta t^2}.
\end{equation}

Using (Equation 8.65) and (Equation.53), the ambiguity function is written as (Equation 8.66)

\begin{equation}
    \big| \chi(\tau, f) \big|^2  = \Bigg| \int\limits_{-\infty}^{\infty} \text{rect}\bigg[ \frac{t}{\tau_p}\bigg] \, \text{rect}\bigg[ \frac{t - \tau}{\tau_p} \bigg] e^{j \pi \beta t^2}\, e^{-j \pi \beta (t - \tau)^2}\, e^{j 2 \pi f t}\, dt\Bigg|^2.
\end{equation}

Performing the integration leads to (Equation 8.67)

\begin{equation}
    \big| \chi(\tau, f) \big|^2  = \Big| (\tau_p - |\tau|) \, \text{sinc}\big[ \tau_p (f + \beta \tau) (\tau_p - |\tau|) \big] \Big|^2 \quad \text{for  } 0 \le |\tau| \le \tau_p.
\end{equation}
***

Begin by getting the library path

```python
import lib_path
```

Set the pulsewidth (s) and the bandwidth (Hz)

```python
pulsewidth = 1e-3

bandwidth = 2e3
```

Generate the time delay (s) using the `linspace` routine from `scipy`

```python
from numpy import linspace

# Set the time delay

time_delay = linspace(-pulsewidth, pulsewidth, 5000)
```

Calculate the ambiguity function for the LFM pulse

```python
from Libs.ambiguity.ambiguity_function import lfm_pulse

from numpy import finfo

ambiguity = lfm_pulse(time_delay, finfo(float).eps, pulsewidth, bandwidth)
```

Plot the zero-Doppler cut using the `matplotlib` routines

```python
from matplotlib import pyplot as plt

# Set the figure size

plt.rcParams["figure.figsize"] = (15, 10)

# Plot the ambiguity function

plt.plot(time_delay, ambiguity, '')

# Set the x and y axis labels

plt.xlabel("Time (s)", size=12)

plt.ylabel("Relative Amplitude", size=12)

# Turn on the grid

plt.grid(linestyle=':', linewidth=0.5)

# Set the plot title and labels

plt.title('LFM Pulse Ambiguity Function', size=14)

# Set the tick label size

plt.tick_params(labelsize=12)
```

Set the Doppler mismatch frequencies using the `linspace` routine

```python
doppler_frequency = linspace(-bandwidth, bandwidth, 5000)
```

Calculate the ambiguity function for the LFM pulse

```python
ambiguity = lfm_pulse(finfo(float).eps, doppler_frequency, pulsewidth, bandwidth)
```

Display the zero-range cut for the LFM pulse

```python
plt.plot(doppler_frequency, ambiguity, '')

# Set the x and y axis labels

plt.xlabel("Doppler (Hz)", size=12)

plt.ylabel("Relative Amplitude", size=12)

# Turn on the grid

plt.grid(linestyle=':', linewidth=0.5)

# Set the plot title and labels

plt.title('LFM Pulse Ambiguity Function', size=14)

# Set the tick label size

plt.tick_params(labelsize=12)
```

Set the time delay and Doppler mismatch frequency and create the two-dimensional grid using the `meshgrid` routine from `scipy`

```python
from numpy import meshgrid

# Set the time delay

time_delay = linspace(-pulsewidth, pulsewidth, 500)

# Set the Doppler mismatch

doppler_frequency = linspace(-bandwidth, bandwidth, 500)

# Create the grid

t, f = meshgrid(time_delay, doppler_frequency)
```

Calculate the ambiguity function for the LFM pulse

```python
ambiguity = lfm_pulse(t, f, pulsewidth, bandwidth)
```

Display the two-dimensional contour plot for the LFM pulse ambiguity function

```python
# Plot the ambiguity function

plt.contour(t, f, ambiguity, 20, cmap='jet', vmin=-0.2, vmax=1.0)

# Set the x and y axis labels

plt.xlabel("Time (s)", size=12)

plt.ylabel("Doppler (Hz)", size=12)

# Turn on the grid

plt.grid(linestyle=':', linewidth=0.5)

# Set the plot title and labels

plt.title('LFM Pulse Ambiguity Function', size=14)

# Set the tick label size

plt.tick_params(labelsize=12)
```

```python

```


## ZDT

The ZDT <cite data-cite="zdt"></cite> problem suite is based on the construction process

\begin{align}
\begin{split}
\min && \; f_1(x)\\[2mm]
\min && \; f_2(x) = g(x) \, h(f_1(x),g(x))
\end{split}
\end{align}

where two objective have to be minimized. The function $g(x)$ can be considered as the function for convergenece  and usually $g(x) = 1$ holds for pareto-optimal solutions (except for ZDT5).

### ZDT1

This is a 30-variable problem ($n=30$) with a convex Pareto-optimal set:

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & x_1 \\
g(x) &=& 1 + \frac{9}{n-1} \; \sum_{i=2}^{n} x_i \\
h(f_1,g) &=& 1 - \sqrt{f_1/g} \\
\end{split}
\end{align}

$$0 \leq x_i \leq 1 \quad i=1,\ldots,n$$

**Optimum**

$$0 \leq x_1^* \leq 1  \quad \text{and} \quad x_i^*=0 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt1")
plot(problem.pareto_front(), no_fill=True)
```

### ZDT2

This is also a 30-variable problem ($n=30$) with a non-convex Pareto-optimal set:

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & x_1 \\
g(x) &=& 1 + \frac{9}{n-1} \; \sum_{i=2}^{n} x_i \\
h(f_1,g) &=& 1 - (f_1/g)^2 \\
\end{split}
\end{align}

$$0 \leq x_i \leq 1 \quad i=1,\ldots,n$$

**Optimum**

$$0 \leq x_1^* \leq 1  \quad \text{and} \quad x_i^*=0 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt2")
plot(problem.pareto_front(), no_fill=True)
```

### ZDT3

This is also a 30-variable problem ($n=30$) with a number of disconnected Pareto-optimal fronts:

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & x_1 \\
g(x) &=& 1 + \frac{9}{n-1} \; \sum_{i=2}^{n} x_i \\
h(f_1,g) &=& 1 - \sqrt{f_1/g} - (f_1/g) \; \sin(10\pi f_1)\\
\end{split}
\end{align}

$$0 \leq x_i \leq 1 \quad i=1,\ldots,n$$

**Optimum**

$$0 \leq x_1^* \leq 0.0830 \\
0.1822 \leq x_1^* \leq 0.2577\\
0.4093 \leq x_1^* \leq 0.4538 \\
0.6183 \leq x_1^* \leq 0.6525 \\
0.8233 \leq x_1^* \leq 0.8518 \\
x_i^*=0 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt3")
plot(problem.pareto_front(), no_fill=True)
```

### ZDT4

This is a 10-variable ($n=10$) problem having a convex Pareto-optimal set. There exist many local Pareto-optimal solutions in this problem. Therefore, algorithms can easily get stuck in a local optimum.

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & x_1 \\
g(x) &=& 1 + 10(n-1) + \sum_{i=2}^{n} (x_i^2 - 10 \cos(4\pi x_i))\\
h(f_1,g) &=& 1 - \sqrt{f_1/g}\\
\end{split}
\end{align}

$$0 \leq x_1 \leq 1 \\
-10 \leq x_i \leq 10 \quad i=2,\ldots,n$$

**Optimum**

$$0 \leq x_1^* \leq 1  \quad \text{and} \quad x_i^*=0 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt4")
plot(problem.pareto_front(), no_fill=True)
```

### ZDT5

In ZDT5 in variables are decodec by bitsrings. At all 11 discrete variables are used, where $x_1$ is represented by 30 bits and the rest $x_2$ to $x_{11}$ by 5 bits each. The function $u(x)$ does nothing else than count the number of $1$ of the corresponding variable. Also, note that the objective function is deceptive, because the values of $v(u(x_i))$ are decreasing with the number of 1's, but has its minimum when all variables are indeed 1.

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & 1+u(x_1)\\
g(x) &=& \sum_{i=2}^{n} v(u(x_i))\\[2mm]
v(u(x_i)) &=&
\begin{cases}
2 + u(x_i) \quad \text{if} \; u(x_i) < 5\\
1 \quad \quad \quad \; \; \; \text{if} \; u(x_i) = 5
\end{cases}\\[4mm]
h(f_1,g) &=& 1 /f_1(x)\\[2mm]
\end{split}
\end{align}

**Optimum**

$$0 \leq u(x_1^*) \leq 30  \quad \text{and} \quad u(x_i^*)=5 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt5", normalize=False)
plot(problem.pareto_front(), no_fill=True)
```

Please note that by default here the Pareto-front is not normalized. However, this can be easily achived normalizing $f_1$ in the range of $(1,31)$ and $f_2$ in $(10/31, 10)$ which are the known bounds of the Pareto-set. By default the normalized problem is used.

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt5", normalize=False)
plot(problem.pareto_front(), no_fill=True)
```

### ZDT6

This is a 10-variable ($n=10$) problem having a nonconvex Pareto-optimal set. The density of solutions across the Pareto-optimal region is non-uniform.

**Definition**

\begin{align}
\begin{split}
f_1(x) &= \, & 1 - \exp(-4 x_1) \sin^6 (6 \pi x_1) \\[2mm]
g(x) &=& 1 + 9 \bigg[\bigg( \sum_{i=2}^{n} x_i \bigg) / 9\bigg]^{0.25}\\[2mm]
h(f_1,g) &=& 1 - (f_1/g)^2\\
\end{split}
\end{align}

$$0 \leq x_i \leq 1 \quad i=1,\ldots,n$$

**Optimum**

$$0 \leq x_1^* \leq 1  \quad \text{and} \quad x_i^*=0 \; \text{for} \; i=2,\ldots,n$$

**Plot**

```python
from pymoo.factory import get_problem
from pymoo.util.plotting import plot

problem = get_problem("zdt6")
plot(problem.pareto_front(), no_fill=True)
```


# Propagation of a light pulse in a dispersive medium

In this exercise we will solve the time evolution of a light pulse. We will describe its initial shape by function $A(z,t = 0)$, which is assumed to be Gaussian for simplicity:

$$
A(z, t=0) = e^{-z^2/2\sigma^2} e^{ik_0z}
$$

In plots etc, we will always only work with the real part of the wave package.

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

A quick initial plot.

```python
# properties of the wave package
sigmaz = 2
k0 = 5

# parameters for plotting
Nz = 512
zmax = 7.5

zlin = np.linspace(-zmax, zmax, Nz)
Az = np.exp(-zlin**2/2/sigmaz**2)*np.exp(1j*k0*zlin)
Aenv = np.exp(-zlin**2/2/sigmaz**2)

f, ax = plt.subplots()
ax.plot(zlin, Az.real, label = 'electric field');
ax.plot(zlin, Aenv, 'r', ls = '--', label='envelope');
ax.set_xlabel('position $z$[L]');
ax.set_ylabel(' $\mathcal{Re}(A)$');
ax.legend()
```

We would now like to understand, how this wave-package propagates. In a non-linear medium the propagation is not easily solved, but we have to know the dispersion relationship of the medium. In our case, we will use the relationship:

$$
\omega(k) = k_0 v_p + (k-k_0)v_g + \frac{\Gamma}{2}(k-k_0)^2
$$

For each single wavelength $k$, we then know the time evolution of the wave package to be:

$$
a(k,t) = a(k, t=0) e^{-i\omega(k)t}
$$

And the full time evolution is then obtain through the Fourier transform

$$
A(z, t) = \int dk e^{ikz} a(k,t)
$$

## The intial wavepackage in Fourier space

As we know $A(z,t=0)$, we can now also directly calculate:

$$
\begin{align}
a(k) &= \frac{1}{2\pi} \int dz e^{-ikz}A(z)\\
 &= \frac{1}{2\pi} \int dz e^{-i(k-k_0)z}e^{-z^2/2\sigma^2}\\
\end{align}
$$

This is a Gaussian integral of the type $\int dz e^{-z^2} = \sqrt{\pi}$. We will rewrite:

$$
i(k-k_0)z+z^2/2\sigma^2 = \frac{\left[z+i(k-k_0)\sigma^2\right]^2}{2\sigma^2}+\frac{(k-k_0)^2\sigma^2}{2}
$$

We can now shift the limits of the integral to obtain:

$$
\begin{align}
a(k) &= \frac{1}{2\pi} e^{-\frac{(k-k_0)^2\sigma^2}{2}}\int dz e^{-\frac{z^2}{2\sigma^2}}
\end{align}
$$

Changing the variable of integration to $z' = \frac{z}{\sqrt{2}\sigma}$, we end up with the integral:

$$
\begin{align}
a(k) &= \frac{\sqrt{2}\sigma}{2\pi} e^{-\frac{(k-k_0)^2\sigma^2}{2}}\int dz' e^{-z'^2}\\
&= \frac{\sigma}{\sqrt{2\pi}} e^{-\frac{(k-k_0)^2\sigma^2}{2}}
\end{align}
$$

It is Gaussian centered around $k_c$ and of width $\sigma_k = \frac{1}{\sigma}$

```python
Nk = 512;
klin = k0 + np.linspace(-4/sigmaz, 4/sigmaz, Nk);
ak = sigmaz/np.sqrt(2*np.pi)*np.exp(-sigmaz**2/2*(klin-k0)**2)

f, ax = plt.subplots()
ax.plot(klin, ak);
ax.set_xlabel('wave vector $k$ [1/L]');
ax.set_ylabel(' $a(k)$');
```

## Time evolution in k space

We are now ready to simply calculate the time evolution of the wave package in k space. It is given by:

$$
a(k,t) = \frac{\sigma}{\sqrt{2\pi}} e^{-\frac{\sigma^2}{2}(k-k_0)^2} e^{-i\left[k_0 v_p + (k-k_0)v_g + \frac{\Gamma}{2}(k-k_0)^2\right]t}
$$

Quite importantly it remains a Gaussian in $k$, but only we complex numbers. Ordering in polynoms of k, we have:

$$
a(k,t) = \frac{\sigma}{\sqrt{2\pi}} e^{-\frac{\sigma^2-i\Gamma t}{2}(k-k_0)^2} e^{-i\left[k_0 v_p + (k-k_0)v_g\right]t}
$$

```python
# parameters of the dispersion relation. You can set them freely
vp = 1
vg = 0.5
gamma = 1

#time
tmax = 10; Nt = 100;

tlin = np.linspace(0, tmax, Nt)
ks, ts = np.meshgrid(klin, tlin);

# the dispersion relationship
omegak = k0*vp+(ks-k0)*vg+gamma/2*(ks-k0)**2
aks = sigmaz/np.sqrt(2*np.pi)*np.exp(-sigmaz**2/2*(ks-k0)**2)

akt = ak*np.exp(-1j*omegak*ts);

f, ax = plt.subplots()
im1 = ax.pcolormesh(ts, ks, akt.real, cmap = 'bwr')
ax.set_xlabel('time $t$')
ax.set_ylabel('wavevector $k$')
ax.set_title('time evolution in k space')
f.colorbar(im1)
```

# Time evolution in real space

We have now everything assembled together to calculate the time evolution in real space:

$$
A(z, t) = \int dk e^{ikz} a(k,t)
$$

Given the nice form of $a(k,t)$, we can already read it off:

- The width will be the inverse of $\sigma_x = \frac{1}{\sqrt{\sigma^2-i\Gamma t}}$
- It is centered around $v_g t$
- It has some complicafted phase prefactor, which we will simply ignore here.

We then obtain:

$$
A(z, t) \propto e^{-\frac{(z-v_gt)^2}{2\left(\sigma^2 -i\Gamma t\right)}}
$$

To make it simpler to understand we rewrite:

$$
 \frac{1}{\sigma^2-i\Gamma t} = \frac{\sigma^2+i\Gamma t}{\sigma^4+\Gamma^2 t^2}
$$

The wave function has then the following form:

$$
A(z, t) \propto e^{-\frac{(z-v_gt)^2}{2\left(\sigma^4+\Gamma^2 t^2\right)}\left(\sigma^2+i\Gamma t\right)}
$$

This is a Gaussian that is centered at $z = v_gt$ with envelope:

$$
\sigma^2_x(t) = \sigma^2+\Gamma^2 t^2/\sigma^2
$$

```python
zs, ts = np.meshgrid(zlin, tlin);

# the dispersion relationship
sigmat_sq = sigmaz**2+gamma**2*ts**2/sigmaz**2

azt = np.exp(-(zs-vg*ts)**2/2/sigmat_sq);

f, ax = plt.subplots()
im1 = ax.pcolormesh(ts, zs, azt, cmap = 'Blues')
ax.set_xlabel('time $t$')
ax.set_ylabel('position $z$')
ax.set_title('time evolution in real space')
f.colorbar(im1)
```

```python

```


# Random Signals and LTI-Systems

*This jupyter notebook is part of a [collection of notebooks](../index.ipynb) on various topics of Digital Signal Processing. Please direct questions and suggestions to [Sascha.Spors@uni-rostock.de](mailto:Sascha.Spors@uni-rostock.de).*

## Linear Mean

In the following we aim at finding a relation between the linear mean $\mu_x[k]$ of the input signal $x[k]$ and the linear mean $\mu_y[k]$ of the output signal $y[k] = \mathcal{H} \{ x[k] \}$ of an LTI system.

### Non-Stationary Input Signal

Let's first impose no restrictions in terms of stationarity to the input signal. The [linear mean](../random_signals/ensemble_averages.ipynb#Linear-mean) of the output signal is then given as

\begin{equation}
\mu_y[k] = E\{ y[k] \} = E\{ x[k] * h[k] \}
\end{equation}

where $h[k]$ denotes the impulse response of the system. Since the convolution and the ensemble average are linear operations, and $h[k]$ is a deterministic signal, this can be rewritten as

\begin{equation}
\mu_y[k] = \mu_x[k] * h[k]
\end{equation}

The linear mean of the output signal $\mu_y[k]$ is given as the convolution of the linear mean of the input signal $\mu_x[k]$ with the impulse response $h[k]$ of the system.

#### Example

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

L = 32  # number of random samples
N = 10000  # number of sample functions

# generate input signal (white Gaussian noise)
np.random.seed(2)
x = np.random.normal(size=(N, L))
x[:, L//2] += 1
# generate output signal
h = 2*np.fft.irfft([1,1,1,0,0,0])
y = np.asarray([np.convolve(x[n,:], h, mode='full') for n in range(N)])

# estimate and plot linear mean
def estimate_plot_linear_mean(x):
    # estimate linear mean by ensemble average
    mu = 1/N * np.sum(x, 0)
    # plot linear mean
    plt.stem(mu)
    plt.xlabel(r'$k$')
    plt.ylabel(r'$\hat{\mu}[k]$')
    plt.axis([0, x.shape[1], -1.2, 1.2])

plt.figure(figsize = (10, 3))
plt.title(r'Estimated linear mean $\hat{\mu}_x[k]$ of input signal')
estimate_plot_linear_mean(x)

plt.figure(figsize = (10, 3))
plt.title(r'Estimated linear mean $\hat{\mu}_y[k]$ of output signal')
estimate_plot_linear_mean(y)
```

**Exercise**

* Can you estimate the impulse response $h[k]$ of the system from above plots of $\hat{\mu}_x[k]$ and $\hat{\mu}_y[k]$?
* You can check your results by plotting the impulse response $h[k]$, for instance with the command `plt.stem(h)`.

Inspecting above plot, the linear mean of the input signal can be approximated as $\mu_x[k] = \delta[k]$. The linear mean of the output is then given as $\mu_y[k] = \delta[k] * h[k] = h[k]$. It follows that the impulse response of the LTI system can be estimated from the linear mean $\mu_y[k]$.

### Stationary Input Signal

For a (wide-sense) stationary process, the linear mean of the input signal $\mu_x[k] = \mu_x$ does not depend on the time index $k$. For a (wide-sense) stationary input signal, also the output signal of the system is (wide-sense) stationary. Using the result for the non-stationary case above yields

\begin{align}
\mu_y &= \mu_x * h[k] \\
&= \sum_{\kappa = -\infty}^{\infty}\mu_x[k-\kappa]h[\kappa] \\
&= \mu_x \cdot \sum_{\kappa = -\infty}^{\infty}h[\kappa] \\
&= \mu_x \cdot \sum_{\kappa = -\infty}^{\infty}h[\kappa]\cdot\mathrm{e}^{-\mathrm{j}\Omega\kappa} \hspace{5mm} \text{for}\,\,\Omega=0 \\
&= \mu_x \cdot H(\mathrm{e}^{\,\mathrm{j}\,\Omega}) \big\vert_{\Omega = 0}
\end{align}

where $H(\mathrm{e}^{\,\mathrm{j}\,\Omega}) = \mathcal{F}_* \{ h[k] \}$ denotes the discrete time Fourier transformation (DTFT) of the impulse response. The linear mean of a (wide-sense) stationary input signal is weighted by the transmission characteristics for the constant (i.e. DC, $\Omega = 0$) component of the LTI system. This implies that the output signal to a zero-mean $\mu_x = 0$ input signal is also zero-mean $\mu_y = 0$.

**Copyright**

This notebook is provided as [Open Educational Resource](https://en.wikipedia.org/wiki/Open_educational_resources). Feel free to use the notebook for your own purposes. The text is licensed under [Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/), the code of the IPython examples under the [MIT license](https://opensource.org/licenses/MIT). Please attribute the work as follows: *Sascha Spors, Digital Signal Processing - Lecture notes featuring computational examples, 2016-2017*.


# Some manipulations on the two degree of freedom model

```python
from sympy import *
init_printing()

def symb(x, y = ''):
    return symbols('{0}_{1}'.format(x,y), type = float)
```

Displacement vector:

```python
x = Matrix([symb('u','g'), symb('u','r')])
display(x)
```

$\displaystyle \left[\begin{matrix}u_{g}\\u_{r}\end{matrix}\right]$

Inertia matrix:

```python
J_r, J_g, n = symbols('J_r J_g n', positive=True)
M = diag(J_r, J_g*n**2)
display(M)
```

$\displaystyle \left[\begin{matrix}J_{r} & 0\\0 & J_{g} n^{2}\end{matrix}\right]$

Stiffness matrix

```python
k = symbols('k', positive=True)
K = eye(2)
K[0, 1] = -1
K[1, 0] = -1
K = k*K
display(K)
```

$\displaystyle \left[\begin{matrix}k & - k\\- k & k\end{matrix}\right]$

## Characteristic polynomial

```python
lamda = symb('lambda')
omega = symb('omega')
A = Inverse(M)*K
cp = A.charpoly(lamda)
cp = factor(cp)
display(cp)
```


# American Put Option Pricing Model

**Randall Romero Aguilar, PhD**

This demo is based on the original Matlab demo accompanying the  Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.

Original (Matlab) CompEcon file: **demdp05.m**

Running this file requires the Python version of CompEcon. This can be installed with pip by running

    !pip install compecon --upgrade

Last updated: 2021-Oct-01

## About

An American put option gives the holder the right, but not the obligation, to sell a specified quantity of a commodity at a specified strike price $K$ on or before a specified expiration period $T$. In the discrete time, continuous state Black-Scholes option pricing model, the price of the commodity follows an exogenous continuous-valued Markov process
\begin{equation}
    P_{t+1} = h(P_t, \epsilon_{t+1})
\end{equation}

**What is the value of an American put option in period $t$ if the commodity price is $P$?** At what critical price is it optimal to exercise the put option, and how does this critical price vary over time?

This is a finite horizon, stochastic model with time $t$ measured in periods. The state variables
\begin{align}
    P &\in (0,\infty) \\
    d &\in \{0, 1\}
\end{align}

are the current commodity price, a continuous variable, and the exercise status of the option, a discrete variable that equals 1 if the option has been exercised previously and equals 0 otherwise. The action variable
\begin{equation}
    j \in \{0, 1\}
\end{equation}

is the exercise decision, a discrete variable that equals 1 if the option is exercised and equals 0 otherwise. The state transition function is
\begin{equation}
    g(P,d,j,\epsilon) = \left(h(P,\epsilon), j\right)
\end{equation}

The reward function is
\begin{equation}
    f(P,d,j) = \begin{cases}K − P, &d = 0, j = 1 \\
    0, &\text{otherwise} \end{cases}
\end{equation}

The value of an unexercised option in period $t$, given that the commodity price is $P$, satisfies the Bellman equation
\begin{equation}
    V_t(P,0) = \max\left\{K − P, \quad\delta E_\epsilon V_{t+1}\left(h(P,\epsilon), 0\right)\right\}
\end{equation}

subject to the terminal condition $V_{T+1}(P,1) = 0$. The value of a previously exercised option is zero, regardless of the price of the commodity; that is, $V_t(P,1) = 0$ for all $P$ and $t$.

# Preliminary tasks

```python
import numpy as np
import matplotlib.pyplot as plt
from compecon import BasisSpline, NLP, qnwnorm, demo
```

## FORMULATION

In what follows, we are going to solve the problem in term of the log-price, defining $p\equiv\log(P)$. We assume that log-price of the commodity follows a random walk:
\begin{equation}
    p_{t+1} = p_t + \epsilon_{t+1}
\end{equation}

where $\epsilon$ is a normal $(\mu, \sigma^2)$ shock. We discretize this distribution by using ```qnwnorm```, assuming that $\mu=0.0001,\quad\sigma=0.008$ and setting up $m=15$ nodes.

```python
mu, sigma    = 0.0001, 0.0080
m = 15
[e,w] = qnwnorm(m,mu,sigma ** 2)
```

We are going to compute the critical exercise price in terms of the time to expiration, up to an horizon of $T=300$ periods. First we allocate memory for the critical prices:

```python
T = 300
pcrit = np.empty(T + 1)
```

The critical exercise price is the price at which the value of exercising the option $K-\exp(p)$ equals the discounted expected value of keeping the option one more period $\delta E_\epsilon V(p + \epsilon)$. To find it, we set it as a nonlinear rootfinding problem by using the ```NLP``` class; here we assume that the option strike price is $K=1$ and that the discount factor is $\delta=0.9998$

```python
K = 1.0
delta = 0.9998
f = NLP(lambda p: K - np.exp(p) - delta * Value(p))
```

Notice that we have not defined the ```Value(p)``` function yet. This function is unknown, so we are going to approximate it with a cubic spline, setting 500 nodes between -1 and 1. Since the basis is expressed in terms of log-prices, this interval corresponds to prices between 0.3679 and 2.7183.

```python
n = 500
pmin  = -1 # minimum log price
pmax  =  1 # maximum log price
Value = BasisSpline(n, pmin, pmax,
                    labels=['logprice'], l=['value'])
print(Value)
```

    A 1-dimension Cubic spline basis:  using 500 Canonical nodes and 500 polynomials
    ___________________________________________________________________________
    	logprice: 500 nodes in [ -1.00,   1.00]

    ===========================================================================
    WARNING! Class Basis is still work in progress

In the last expression, by passing the option `l` with a one-element list we are telling the ```BasisSpline``` class that we a single function named "value". On creation, the function will be set by default to $V(p)=0$ for all values of $p$, which conveniently corresponds to the terminal condition of this problem.

## Finding the critical exercise prices

We are going to find the prices recursively, starting form a option in the expiration date. Notice that the solution to this problem is trivial: since next-period value is zero, the exercise price is $K$. Either way, we can find it numerically by calling the ```zero``` method on the `f` object.

```python
pcrit[0] = f.zero(0.0)
```

Next, for each possible price shock, we compute next period log-price by adding the shock to current log-prices (the nodes of the Value object). Then, we use each next-period price to compute the expected value of an option with one-period to maturity (save the values in ```v```). We update the value function to reflect the new time-to-maturity and use ```broyden``` to solve for the critical value. We repeat this procedure until we reach the $T=300$ horizon.

```python
for t in range(T):
    v = np.zeros((1, n))
    for k in range(m):
        pnext = Value.nodes + e[k]
        v += w[k] * np.maximum(K - np.exp(pnext), delta * Value(pnext))

    Value[:] = v
    pcrit[t + 1] = f.broyden(pcrit[t])
```

### Print Critical Exercise Price 300 Periods to Expiration

```python
print('Critical Price  = %5.2f' % np.exp(pcrit[-1]))
```

    Critical Price  =  0.88

### Plot Critical Exercise Prices

```python
fig1 = demo.figure('American Put Option Optimal Exercise Boundary',
            'Periods Remaining Until Expiration',
            'Exercise Price')
plt.plot(np.exp(pcrit))
```

```python
#demo.savefig([fig1])
```

_build/html/_sources/notebooks/dp/05 America Put Option Pricing Model.ipynb


# Kodlabb - *Gradient descent*

### Förkunskaper
- Matte 3: Derivata

# Inledning
En dator gör enbart det vi säger åt den med vår programmering, men det går att be en dator att utforska hur väl en viss programkod presterar och att söka efter den bästa versionen av programkoden. Detta är _maskininlärning_ (ML), och den bästa versionen av programkoden är den _artificiella intelligensen_ (AI).

Maskininlärning är ofta bara en minimering av en funktion. Föreställ dig att $f(x)$ beskriver summan av alla misstag en mjukvara presterar med inställningen $x$, isåfall kan vi hitta en mycket bra inställning av mjukvaran genom att hitta funktionens minpunkter. $x$ kommer dock inte vara en [skalär][1], utan en [vektor][2] med tusentals eller miljontals komponenter.

Titta på minst en av videofilmerna nedan.

|Elon Musk's 'Dota 2' Experiment is Disrupting Esports in a Big Way|Meet the dazzling flying machines of the future|
|:-|:-|
|[![][img1]][vid1]|[![][img2]][vid2]|
|*YouTube: En artificiell intelligens har besegrat Dota2 proffs i dueller (Augusti 2017).*|*YouTube: De flesta quadrokoptrar styrs med hjälp av AI, tränad med ML.*|

|Real-Time Facial Expression Transfer|A.I. Experiments: Giorgio Cam|
|:-|:-|
|[![][img3]][vid3]|[![][img4]][vid4]|
|*YouTube: En AI kopplar en skådespelares ansiktsuttryck till ett annat ansikte.*|*YouTube: Använd befintlig AI för att känna igen föremål i bilder, översätta språk m.m.*|

[1]: https://en.wikipedia.org/wiki/Scalar_(mathematics)
[2]: https://en.wikipedia.org/wiki/Vector_(mathematics_and_physics)

[img1]: https://img.youtube.com/vi/jAu1ZsTCA64/0.jpg
[vid1]: https://www.youtube.com/watch?v=jAu1ZsTCA64
[img2]: https://img.youtube.com/vi/RCXGpEmFbOw/0.jpg
[vid2]: https://www.youtube.com/watch?v=RCXGpEmFbOw
[img3]: https://img.youtube.com/vi/mkI6qfpEJmI/0.jpg
[vid3]: https://www.youtube.com/watch?v=mkI6qfpEJmI
[img4]: https://img.youtube.com/vi/eKeI63VSpto/0.jpg
[vid4]: https://www.youtube.com/watch?v=eKeI63VSpto&list=PLOU2XLYxmsIKubpTZNmgNKL6ToSQ1pWmy&index=2

### Fråga 1

> __Fråga:__ Besvara frågorna i svarscellen nedan, dubbelklicka på cellen för att börja skriva och kör cellen för att presentera resultatet.

* Vilka filmer såg du?
* Vad skulle du vilja använda AI till?

YOUR ANSWER HERE

# Let's go!
Nu ska du få börja koda! Du kommer få testa och lära dig om _gradient descent_ (GD). Algoritmen hjälper dig att hitta en funktions minimipunkter, och är en mycket bra och välanvänd strategi för maskininlärning - du kan träna upp en AI med den.

### Kodkomplettering 1

> __Kodkomplettering:__ Här ska du skriva och testköra kod utifrån instruktioner.

Vi behöver en funktion med ett antal minimipunkter att testa algoritmen på.

Definiera pythonfunktion `f` så att den beter sig precis som den matematiska funktionen $f(x, y)$ nedan. Använd dig av `numpy` bibliotekets funktioner `np.sin`, `np.cos` och `np.exp` tillsammans med Pythons `**` operator (`x**2`$=x^2$).

$$f(x, y) = \sin(\,({\frac{x}{2}-2})^2 + (\frac{y}{2}-2)^2\,) \cdot \cos(\,x-y+e^{-y}\,)$$

```python
import numpy as np
from simulation import Sim
sim = Sim()
```

```python
def f(x, y):
    """The function to investigate described above, it should returns
    a value for all numerical inputs x and y."""
    # YOUR CODE HERE
    raise NotImplementedError()
```

```python
# Test and visualize your code as a 3D graph.
# - Two dimensions represents the inputs x and y, and the third represents
#   the output z = f(x,y)
sim.setup_f(f)
sim.run(show_2d=False)
```

### Studiestund 1

> __Studiestund__: Stanna till och lär dig om relevant material inför kommande uppgifter.

Innan vi fortsätter behöver vi utvidga våra kunskaper från Matte 3 och lära oss om _funktioner av flera variabler_, _partiell derivata_ och _gradienter_.

####  Funktioner av flera variabler

En _definitionsmängd_ eller en _domän_ är inom matematiken mängden av alla möjliga värden du kan stoppa in i en funktion, och en _värdemängd_ är alla värden du kan få av en funktionen.

> __Förstår du?__

> Vad har denna funktion tänkt att ha för definitionsmängd och värdemängd? Vad kan du stoppa in i funktionen, och vad kan du få ut från funktionen?
> ```python
def getWordLength(word):
    return len(word)
>```

Vissa funktioner har en flerdimensionell definitionsmängd och tar emot flera variabler. Funktionerna $g$ och $h$ har exempelvis definitionsmängder som är två- och tredimensionella.

\begin{align}
f(x)     & = x^2         & f(2)     & = 4  \\
g(x,y)   & = x^2+y^2     & f(2,3)   & = 13 \\
h(x,y,z) & = x^2+y^2+z^2 & f(2,3,4) & = 29 \\
\end{align}

> __Förstår du?__

> Är $g(x,y)$ och $h(x,y,z)$:s _värdemängder_ endimensionella eller flerdimensionella?
>
> $f(x)$ och $g(x,y)$:s definitionsmängd kan motsvaras av en linje och en yta, vad kan $ħ(x,y,z)$:s definitionsmängd motsvaras av?

#### Partiell derivata och gradienter

Tänk dig att du är en robotmyra utan syn på en spishäll och enbart kan mäta temperaturen precis du står. Du behöver ta dig till säkerhet och går sönder om du går över en varm spisplatta. Tänk på temperaturen som funktionen $T(x,y)$. Som handlingskraftig robotmyra sträckte du __fram__ ett av dina ben och märkte att temperaturen __ökade med en grad__. Du stod kvar och sträckte ut ett myrben åt __höger__ och märkte att temperaturen __minskade med en grad__. Som robotmyra undersökte du precis temperaturfunktions _partiella derivata_ för där du står just nu, du fick två värden, en för varje dimension du kan röra dig i. Om du kan lista ut vilket håll du borde undvika att gå mot, där temperaturen ökar som mest, har du även hittat riktningen på _gradienten_.

> __Förstår du?__

> Vilket håll bör myran gå åt för undvika värmen?

Titta nu på följande film och besvara därefter några kontrollfrågor.

|Gradients and Partial Derivatives|
|:-|
|[](https://www.youtube.com/watch?v=GkB4vW16QHI)|
|*YouTube: Lär dig om [partiell derivata](https://sv.wikipedia.org/wiki/Partiell_derivata) och [gradientvektorer](https://sv.wikipedia.org/wiki/Gradient) $\nabla f = (\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y})$*|

### Fråga 2
I bilden ovan ser vi en funktion $z=h(x,y)$ representerad som en yta (2D) i en volym (3D). Föreställ dig att du är en robotmyra på den ytan.
- a) Beskriv vart du skulle gå om du följde gradienten och stod i en backe.
- b) Beskriv vart du skulle gå om du följde gradienten och stod på en kulles topp.
- c) Beskriv vad det engelska ordet *descent* betyder och gissa hur algoritmen _gradient descent_ fungerar.

YOUR ANSWER HERE

### Kodkomplettering 2
*Gradient descent* bygger på att vi kan promenera antiparallellt (åt andra hållet) till gradienten, och gradienten som är en vektor, kan bestämmas om vi känner till de partiella derivatorna. Vi kan beräkna de partiella derivatorna ungefärligt med ett litet värde på $h$ på följande sätt.

$$\frac{\partial f}{\partial x} \approx \frac{f(x+h, y) - f(x-h, y)}{2h}$$

$$\frac{\partial f}{\partial y} \approx \frac{f(x, y+h) - f(x, y-h)}{2h}$$

Vi använder oss av en så kallad *central differenskvot* - central eftersom vi tittar på ett funktionsvärde till både vänster och höger om en punkt, och differenskvot för att det är en kvot vars täljare och nämnare består av differenser. Centrala differenskvoter är lämpliga att använda för att uppskatta derivata numeriskt.

Ditt uppdrag är att komplettera funktionerna nedan, så att de numeriskt beräknar de partiella derivatorna för funktionen `f`.

```python
h = 0.001
def df_dx(x, y):
    """Return an approximate value for the partial derivative of the function f with respect to x, as described above."""
    # YOUR CODE HERE
    raise NotImplementedError()
def df_dy(x, y):
    """Return an approximate value for the partial derivative of the function f with respect to y, as described above."""
    # YOUR CODE HERE
    raise NotImplementedError()
```

```python
# Test and visualize your code, the arrows are created using df_dx and df_dy.
sim.setup_grad_f(df_dx, df_dy)
sim.run(show_3d=False)
```

### Fråga 4
Ovan ser du förhoppningsvis nu ett [färgdiagram][1] (heat map) där röda färger motsvarar höga funktionsvärden och blåa färger motsvarar låga. I diagrammet finns även [konturlinjer (också kallat *isaritmer* och *isolinjer*)][2].

* a) Beskriv vad vektorerna (pilarna) motsvarar.
* b) Beskriv hur pilarna pekar kring extrempunkterna.
* c) Om du gjort rätt kommer pilarna inte peka längst med konturlinjerna, varför inte?

[1]: https://sv.wikipedia.org/wiki/F%C3%A4rgdiagram
[2]: https://sv.wikipedia.org/wiki/Isaritm

YOUR ANSWER HERE

### Studiestund 2

Nu är du redo för själva gradient descent algoritmen.

> __Gradient descent:__ att ta steg efter steg i en funktions definitionsmängd motriktat gradienten [proportionella][1] till gradientens storlek tills du tar så små steg att du anser dig framme eller helt enkelt tagit för många.

Algoritmens möjlighet att hitta minimivärden för funktioner är otroligt betydelsefull för maskininlärning (ML) och har gett upphov till en mängd AI. Allt genom att minimera funktioner.

Du har kanske hört talas om neurala nätverk (neural networks - NN), som eventuellt är stora/djupa (deep neural networks - DNN), eller har en viss struktur (convolutional neural networks - CNN)? Alla neurala nätverk måste få sina reglage som avgör hur de beter sig inställda, gradient descent kan hitta bra inställningar på dessa genom att minimera den hundratusentalsdimensionella funktionen som beskriver alla fel nätverket gjort på tusentals övningsexempel.

Titta på den andra filmen __mellan 5:21 och 12:18__ så får du en välpresenterad förklaring av gradient descent, skulle du titta på båda filmerna får du en inblick i neurala nätverk.

But what *is* a neural network? | Deep learning, Part 1|Gradient descent, how neural networks learn | Deep learning, Part 2
:-|:-
[![][img1]][vid1]|[![][img2]][vid2]
*YouTube: Del 1 i serien om neurala nätverk.*|*YouTube: Från 5:21 till 12:18 i filmen får du lära dig det du behöver veta om algoritmen gradient descent. Del 2 i serien om neurala nätverk.*

[1]: https://sv.wikipedia.org/wiki/Proportionalitet_(matematik)

[img1]: https://img.youtube.com/vi/aircAruvnKk/maxresdefault.jpg
[vid1]: https://www.youtube.com/watch?v=aircAruvnKk&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi&index=1
[img2]: https://img.youtube.com/vi/IHZwWFHWa-w/maxresdefault.jpg
[vid2]: https://www.youtube.com/watch?v=IHZwWFHWa-w&index=2&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi&t=5m21s

### Fråga 3
* a) Gradient descent hjälper oss att hitta en minimipunkt, är den globala eller lokal?
* b) Hur många dimensioner har gradientvektorerna till f(x,y)=z och varför?
* c) Låt oss anta att du känner till gradienten av funktionen för en punkt där du är nu, åt vilket håll bör du försöka gå för att hitta en minimipunkt?

YOUR ANSWER HERE

### Kodkomplettering 3

Du ska nu skapa en funktion som tar __ett__ steg i definitionsmängden av funktionen $f(x,y)$ i motsatt riktning till gradienten och anpassar det stegets längd utifrån hur lång gradientvektorn är samt en konstant $\alpha$ (alpha) som vi kan kalla för en _steglängdsfaktor_ (step size multiplier / the learning rate). Ett högt alpha värde gör att vi raskt rör oss mot minimipunkten. Här är en beskrivning över vad din kod ska göra.

\begin{align}
\text{new_x} \gets x - \alpha \cdot \frac{\partial f}{\partial x}(x, y)\\
\\
\text{new_y} \gets y - \alpha \cdot \frac{\partial f}{\partial y}(x, y)\\
\end{align}

Funktionen `gradient_descent_step` ska även räkna ut hur långt det föreslagna steget var, detta kan göras med pythagoras sats och [`np.sqrt`][1] eller med [`np.linalg.norm`][2].

[1]: https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.sqrt.html
[2]: https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.linalg.norm.html

```python
alpha = 0.2
def gradient_descent_step(x, y, alpha=alpha):
    # YOUR CODE HERE
    raise NotImplementedError()
    return new_x, new_y, step_size
```

```python
# Verify you got it right
sim.setup_gds(gradient_descent_step)
```

# Resultat
Testköra och experimentera med algoritmen med denna kod som bygger på det du gjort tidigiare.

```python
# Experiment with these values
# - Add/move starting points to find all minima in between (0,0) and (5,5)
# - Analyze what happens with a large step size multiplier (alpha).
starting_points = [(2, 2), (2, 3), (3, 2), (3, 3)]
alpha = 0.2
min_step_size = 0.001
max_steps = 100

# ------------------------------------------------------------------------------

# Runs gradient descent until our steps becomes too small (< min_step_size) or
# we have taken the maximum amount of steps
sim.gd_trails = []
for starting_point in starting_points:
    x, y = starting_point
    gd_trail = np.array([[x, y, f(x,y)]])
    latest_step_length = np.inf

    while latest_step_length > min_step_size and len(gd_trail) <= max_steps:
        x, y, latest_step_length = gradient_descent_step(x, y, alpha=alpha)
        gd_trail = np.append(gd_trail, [[x, y, f(x,y)]], axis=0)

    sim.gd_trails.append(gd_trail)
    print('Ran GD from f({:.2f}, {:.2f})={:5.2f} to f({:.2f}, {:.2f})={:5.2f} after {} steps.'.format(*gd_trail[0,:], *gd_trail[-1,:], len(gd_trail)))

sim.run()
```

### Fråga 5
Undersök på egen hand och försök besvara följande frågor.

- a) Hur många minimipunkter mellan (0,0) och (5,5) fanns det och vilken har högst funktionsvärde? ( $f(x,y)=z$ )
- b) Undersök och beskriv för och nackdelar med låga och höga värden på steglängdsfaktorn (alpha).
- c) Undersök och beskriv för och nackdelar med låga och höga värden på `min_step_size`?

YOUR ANSWER HERE

# Avslutning
Grattis! Är du klar?

### Kontrollera din kod
Testkör din kod __Kernel > Restart & Run all__ i menyraden och __Validate__ knappen i verktygsraden.

### Maila din mattelärare
Få en kopia på ditt arbete genom __File > Download as > HTML (.html)__, maila din mattelärare och visa upp att du genomfört labben!

# Fortsättning
Det finns mycket att lära sig inom ML/AI. Här är tips på fortsättningar.

### Använd befintlig AI
Du behöver inte lära dig ML och träna din egen AI - du kan använda befintlig AI genom av fritt tillgängliga [APIer](https://sv.wikipedia.org/wiki/Application_Programming_Interface).

|A.I. Experiments|Getting Started in 3 Minutes with API.AI|
|:-|:-|
|[](https://www.youtube.com/watch?v=oOwfiYnRi5c&list=PLOU2XLYxmsIKubpTZNmgNKL6ToSQ1pWmy)|[](https://www.youtube.com/watch?v=Om7tyGGemXI)
|*YouTube playlist: Flera av apparna som demonstreras använder sig av APIer.*|*YouTube: api.ai hjälper din mjukvara att ta beslut utifrån vad användaren säger.*|

### Python
Alla programmeringsspråk kan använda ML, men det är till Python som det utvecklas mest spännande stödbibliotek just nu.

Python - Basics|Python - Intermediate
:-|:-
[](https://pythonprogramming.net/introduction-to-python-programming/)|[](https://pythonprogramming.net/introduction-intermediate-python-tutorial/)
*Kurs och YouTube playlist: En grundkurs i Python.*|*Kurs och YouTube playlist: En fortsättningskurs i Python.*

### Grunderna i ML
Vill du använda ML för att träna en AI på egen hand? Då kan vara bra att börja här. Skapa dig en en översikt av ML innan du beger sig in i de mer avancerade teknikerna och lär dig vad *Supervised* respektive *Unsupervised learning* innebär exempelvis. I dessa filmer stöter du på grundtekniker såsom *linear regression*, *support vector machines*, *decision trees* med mera.

Python - Machine Learning|Machine Learning Recipes
:-|:-
[](https://pythonprogramming.net/machine-learning-tutorial-python-introduction/)|[](https://www.youtube.com/watch?v=cKxRvEZd3Mw&list=PLRAcvynLm0SCOJ7Ik3Q58-DTOKgY5h0Ky)
*Kurs och YouTube playlist: En grundkurs i ML.*|*YouTube playlist: Kort och praktiskt - en kokbok för ML.*

### Avancerat: Neurala nätverk, *deep learning* och matematik
Det finns många varianter av ML, och de har sina styrkor och svagheter. Neurala nätverk ligger som grund till några av de mest avancerade ML teknikerna i skrivande stund.

Neural networks|The Math of Intelligence
:-|:-
[](https://www.youtube.com/watch?v=aircAruvnKk&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi)|[](https://www.youtube.com/watch?v=xRJCOz3AfYY&list=PL2-dafEMk2A7mu0bSksCGMJEmeddU_H4D)
*YouTube playlist: Ger insikt om neurala nätverk.*|*YouTube playlist: Teoretiskt om matematiken i ML.*

Neural Networks Demystified|Learning To See
:-|:-
[](https://www.youtube.com/watch?v=bxe2T-V8XRs&list=PLiaHhY2iBX9hdHaRr6b7XevZtgZRa1PoU)|[](https://www.youtube.com/watch?v=i8D90DkCLhI&list=PLiaHhY2iBX9ihLasvE8BKnS2Xg8AhY6iV)
*YouTube playlist: Ger teknisk och matematisk bakom hur neurala nätverk fungerar.*|*YouTube playlist: Om utmaningar och lösningar för att träna en dator att räkna fingrar.*