# Programming Assignment

## RealNVP for the LSUN bedroom dataset

### Instructions

In this notebook, you will develop the RealNVP normalising flow architecture from scratch, including the affine coupling layers, checkerboard and channel-wise masking, and combining into a multiscale architecture. You will train the normalising flow on a subset of the LSUN bedroom dataset.

Some code cells are provided for you in the notebook. You should avoid editing provided code, and make sure to execute the cells in order to avoid unexpected errors. Some cells begin with the line:

`#### GRADED CELL ####`

Don't move or edit this first line - this is what the automatic grader looks for to recognise graded cells. These cells require you to write your own code to complete them, and are automatically graded when you submit the notebook. Don't edit the function name or signature provided in these cells, otherwise the automatic grader might not function properly.

### How to submit

Complete all the tasks you are asked for in the worksheet. When you have finished and are happy with your code, press the **Submit Assignment** button at the top of this notebook.

### Let's get started!

We'll start running some imports, and loading the dataset. Do not edit the existing imports in the following cell. If you would like to make further Tensorflow imports, you should add them here.

```python
#### PACKAGE IMPORTS ####

# Run this cell first to import all required packages. Do not make any imports elsewhere in the notebook

import tensorflow as tf
import tensorflow_probability as tfp
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Image

from tensorflow.keras import Model, Input
from tensorflow.keras.layers import Conv2D, BatchNormalization
from tensorflow.keras.optimizers import Adam

tfd = tfp.distributions
tfb = tfp.bijectors

# If you would like to make further imports from tensorflow, add them here

```

#### The LSUN Bedroom Dataset

In this assignment, you will use a subset of the [LSUN dataset](https://www.yf.io/p/lsun). This is a large-scale image dataset with 10 scene and 20 object categories. A subset of the LSUN bedroom dataset has been provided, and has already been downsampled and preprocessed into smaller, fixed-size images.

* F. Yu, A. Seff, Y. Zhang, S. Song, T. Funkhouser and J. Xia. "LSUN: Construction of a Large-scale Image Dataset using Deep Learning with Humans in the Loop". [arXiv:1506.03365](https://arxiv.org/abs/1506.03365), 10 Jun 2015

Your goal is to develop the RealNVP normalising flow architecture using bijector subclassing, and use it to train a generative model of the LSUN bedroom data subset. For full details on the RealNVP model, refer to the original paper:

* L. Dinh, J. Sohl-Dickstein and S. Bengio. "Density estimation using Real NVP". [arXiv:1605.08803](https://arxiv.org/abs/1605.08803), 27 Feb 2017.

#### Import the data

The dataset required for this project can be downloaded from the following link:

https://drive.google.com/file/d/1scbDZrn5pkRjF_CeZp66uHVQC9o1gIsg/view?usp=sharing

You should upload this file to Drive for use in this Colab notebook. It is recommended to unzip it on Drive, which can be done using the `zipfile` package:

>```
import zipfile
with zipfile.ZipFile("/path/to/lsun_bedroom.zip","r") as zip_ref:
   zip_ref.extractall('lsun_bedroom_data')
```

```python
# Run this cell to connect to your Drive folder

from google.colab import drive
drive.mount('/content/gdrive')
```

#### Load the dataset

The following functions will be useful for loading and preprocessing the dataset. The subset you will use for this assignment consists of 10,000 training images, 1000 validation images and 1000 test images.

The images have been downsampled to 32 x 32 x 3 in order to simplify the training process.

```python
# Functions for loading and preprocessing the images

def load_image(filepath):
    raw_img = tf.io.read_file(filepath)
    img_tensor_int = tf.image.decode_jpeg(raw_img, channels=3)
    img_tensor_flt = tf.image.convert_image_dtype(img_tensor_int, tf.float32)
    img_tensor_flt = tf.image.resize(img_tensor_flt, [32, 32])
    img_tensor_flt = tf.image.random_flip_left_right(img_tensor_flt)
    return img_tensor_flt, img_tensor_flt

def load_dataset(split):
    train_list_ds = tf.data.Dataset.list_files('/path/to/data/{}/*.jpg'.format(split), shuffle=False)
    train_ds = train_list_ds.map(load_image)
    return train_ds
```

```python
# Load the training, validation and testing datasets splits

train_ds = load_dataset('train')
val_ds = load_dataset('val')
test_ds = load_dataset('test')
```

```python
# Shuffle the datasets

shuffle_buffer_size = 1000
train_ds = train_ds.shuffle(shuffle_buffer_size)
val_ds = val_ds.shuffle(shuffle_buffer_size)
test_ds = test_ds.shuffle(shuffle_buffer_size)
```

```python
# Display a few examples

n_img = 4
f, axs = plt.subplots(n_img, n_img, figsize=(14, 14))

for k, image in enumerate(train_ds.take(n_img**2)):
    i = k // n_img
    j = k % n_img
    axs[i, j].imshow(image[0])
    axs[i, j].axis('off')
f.subplots_adjust(wspace=0.01, hspace=0.03)
```

```python
# Batch the Dataset objects

batch_size = 64
train_ds = train_ds.batch(batch_size)
val_ds = val_ds.batch(batch_size)
test_ds = test_ds.batch(batch_size)
```

### Affine coupling layer

We will begin the development of the RealNVP architecture with the core bijector that is called the _affine coupling layer_. This bijector can be described as follows: suppose that $x$ is a $D$-dimensional input, and let $d<D$. Then the output $y$ of the affine coupling layer is given by the following equations:

$$
\begin{align}
y_{1:d} &= x_{1:d} \tag{1}\\
    y_{d+1:D} &= x_{d+1:D}\odot \exp(s(x_{1:d})) + t(x_{1:d}), \tag{2}
\end{align}
$$

where $s$ and $t$ are functions from $\mathbb{R}^d\rightarrow\mathbb{R}^{D-d}$, and define the log-scale and shift operations on the vector $x_{d+1:D}$ respectively.

The log of the Jacobian determinant for this layer is given by $\sum_{j}s(x_{1:d})_j$.

The inverse operation can be easily computed as

$$
\begin{align}
x_{1:d} &= y_{1:d}\tag{3}\\
x_{d+1:D} &= \left(y_{d+1:D} - t(y_{1:d})\right)\odot \exp(-s(y_{1:d})),\tag{4}
\end{align}
$$

In practice, we will implement equations $(1)$ and $(2)$ using a binary mask $b$:

$$
\begin{align}
\text{Forward pass:}\qquad y &= b\odot x + (1-b)\odot\left(x\odot\exp(s(b\odot x)) + t(b\odot x)\right),\tag{5}\\
\text{Inverse pass:}\qquad x &= b\odot y + (1-b)\odot\left(y - t(b\odot x) \odot\exp( -s(b\odot x))\right).\tag{6}
\end{align}
$$

Our inputs $x$ will be a batch of 3-dimensional Tensors with `height`, `width` and `channels` dimensions. As in the original architecture, we will use both spatial 'checkerboard' masks and channel-wise masks:

```python
# Run this cell to download and view a figure to illustrate the checkerboard and binary masks

!wget -q -O binary_masks.png --no-check-certificate "https://docs.google.com/uc?export=download&id=1d_cBjyPGm8i0l5GsRSspPoAlxiPo3HGt"
Image("binary_masks.png", width=800)
```

Figure 1. Spatial checkerboard mask (left) and channel-wise mask (right). From the original paper.

#### Custom model for log-scale and shift

You should now create a custom model for the shift and log-scale parameters that are used in the affine coupling layer bijector. We will use a convolutional residual network, with two residual blocks and a final convolutional layer. Using the functional API, build the model according to the following specifications:

* The function takes the `input_shape` and `filters` as arguments
* The model should use the `input_shape` in the function argument to set the shape in the Input layer (call this layer `h0`).
* The first hidden layer should be a Conv2D layer with number of filters set by the `filters` argument, and a ReLU activation
* The second hidden layer should be a BatchNormalization layer
* The third hidden layer should be a Conv2D layer with the same number of filters as the input `h0` to the model, and a ReLU activation
* The fourth hidden layer should be a BatchNormalization layer
* The fifth hidden layer should be the sum of the fourth hidden layer output and the inputs `h0`. Call this layer `h1`
* The sixth hidden layer should be a Conv2D layer with filters set by the `filters` argument, and a ReLU activation
* The seventh hidden layer should be a BatchNormalization layer
* The eighth hidden layer should be a Conv2D layer with the same number of filters as `h1` (and `h0`), and a ReLU activation
* The ninth hidden layer should be a BatchNormalization layer
* The tenth hidden layer should be the sum of the ninth hidden layer output and `h1`
* The eleventh hidden layer should be a Conv2D layer with the number of filters equal to twice the number of channels of the model input, and a linear activation. Call this layer `h2`
* The twelfth hidden layer should split `h2` into two equal-sized Tensors along the final channel axis. These two Tensors are the shift and log-scale Tensors, and should each have the same shape as the model input
* The final layer should then apply the `tanh` nonlinearity to the log_scale Tensor. The outputs to the model should then be the list of Tensors `[shift, log_scale]`

All Conv2D layers should use a 3x3 kernel size, `"SAME"` padding and an $l2$ kernel regularizer with regularisation coefficient of `5e-5`.

_Hint: use_ `tf.split` _with arguments_ `num_or_size_splits=2, axis=-1` _to create the output Tensors_.

In total, the network should have 14 layers (including the `Input` layer).

```python
#### GRADED CELL ####

# Complete the following function.
# Make sure to not change the function name or arguments.

def get_conv_resnet(input_shape, filters):
    """
    This function should build a CNN ResNet model according to the above specification,
    using the functional API. The function takes input_shape as an argument, which should be
    used to specify the shape in the Input layer, as well as a filters argument, which
    should be used to specify the number of filters in (some of) the convolutional layers.
    Your function should return the model.
    """

```

```python
# Test your function and print the model summary

conv_resnet = get_conv_resnet((32, 32, 3), 32)
conv_resnet.summary()
```

You can also inspect your model architecture graphically by running the following cell. It should look something like the following:

```python
# Run this cell to download and view an example model plot

!wget -q -O model_plot.png --no-check-certificate "https://docs.google.com/uc?export=download&id=1I9MhFGquwyHsJlDgO8hItXnMqAvc5cQg"
Image("model_plot.png", width=1400)
```

```python
# Plot the model graph

tf.keras.utils.plot_model(conv_resnet, show_layer_names=False, rankdir='LR')
```

```python
# Check the output shapes are as expected

print(conv_resnet(tf.random.normal((1, 32, 32, 3)))[0].shape)
print(conv_resnet(tf.random.normal((1, 32, 32, 3)))[1].shape)
```

#### Binary masks

Now that you have a shift and log-scale model built, we will now implement the affine coupling layer. We will first need functions to create the binary masks $b$ as described above. The following function creates the spatial 'checkerboard' mask.

It takes a rank-2 `shape` as input, which correspond to the `height` and `width` dimensions, as well as an `orientation` argument (an integer equal to `0` or `1`) that determines which way round the zeros and ones are entered into the Tensor.

```python
# Function to create the checkerboard mask

def checkerboard_binary_mask(shape, orientation=0):
    height, width = shape[0], shape[1]
    height_range = tf.range(height)
    width_range = tf.range(width)
    height_odd_inx = tf.cast(tf.math.mod(height_range, 2), dtype=tf.bool)
    width_odd_inx = tf.cast(tf.math.mod(width_range, 2), dtype=tf.bool)
    odd_rows = tf.tile(tf.expand_dims(height_odd_inx, -1), [1, width])
    odd_cols = tf.tile(tf.expand_dims(width_odd_inx, 0), [height, 1])
    checkerboard_mask = tf.math.logical_xor(odd_rows, odd_cols)
    if orientation == 1:
        checkerboard_mask = tf.math.logical_not(checkerboard_mask)
    return tf.cast(tf.expand_dims(checkerboard_mask, -1), tf.float32)
```

This function creates a rank-3 Tensor to mask the `height`, `width` and `channels` dimensions of the input. We can take a look at this checkerboard mask for some example inputs below. In order to make the Tensors easier to inspect, we will squeeze out the single channel dimension (which is always 1 for this mask).

```python
# Run the checkerboard_binary_mask function to see an example
# NB: we squeeze the shape for easier viewing. The full shape is (4, 4, 1)

tf.squeeze(checkerboard_binary_mask((4, 4), orientation=0))
```

```python
# The `orientation` should be 0 or 1, and determines which way round the binary entries are

tf.squeeze(checkerboard_binary_mask((4, 4), orientation=1))
```

You should now complete the following function to create a channel-wise mask. This function takes a single integer `num_channels` as an input, as well as an `orientation` argument, similar to above. You can assume that the `num_channels` integer is even.

The function should return a rank-3 Tensor with singleton entries for `height` and `width`. In the channel axis, the first `num_channels // 2` entries should be zero (for `orientation=0`) and the final `num_channels // 2` entries should be one (for `orientation=0`). The zeros and ones should be reversed for `orientation=1`. The `dtype` of the returned Tensor should be `tf.float32`.

```python
#### GRADED CELL ####

# Complete the following function.
# Make sure to not change the function name or arguments.

def channel_binary_mask(num_channels, orientation=0):
    """
    This function takes an integer num_channels and orientation (0 or 1) as
    arguments. It should create a channel-wise binary mask with
    dtype=tf.float32, according to the above specification.
    The function should then return the binary mask.
    """

```

```python
# Run your function to see an example channel-wise binary mask

channel_binary_mask(6, orientation=0)
```

```python
#### GRADED CELL ####

# Complete the following functions.
# Make sure to not change the function names or arguments.

def forward(x, b, shift_and_log_scale_fn):
    """
    This function takes the input Tensor x, binary mask b and callable
    shift_and_log_scale_fn as arguments.
    This function should implement the forward transformation in equation (5)
    and return the output Tensor y, which will have the same shape as x
    """

def inverse(y, b, shift_and_log_scale_fn):
    """
    This function takes the input Tensor x, binary mask b and callable
    shift_and_log_scale_fn as arguments.
    This function should implement the forward transformation in equation (5)
    and return the output Tensor y, which will have the same shape as x
    """

```

The new bijector class also requires the `log_det_jacobian` methods to be implemented. Recall that the log of the Jacobian determinant of the forward transformation is given by $\sum_{j}s(x_{1:d})_j$, where $s$ is the log-scale function of the affine coupling layer.

You should now complete the following functions to define the `forward_log_det_jacobian` and `inverse_log_det_jacobian` methods of the affine coupling layer bijector.

* Both functions `forward_log_det_jacobian` and `inverse_log_det_jacobian` takes an input Tensor `x` (or `y`), a rank-3 binary mask `b`, and the `shift_and_log_scale_fn` callable
* These arguments are the same as the description for the `forward` and `inverse` functions
* The `forward_log_det_jacobian` function should implement the log of the Jacobian determinant for the transformation $(5)$
* The `inverse_log_det_jacobian` function should implement the log of the Jacobian determinant for the transformation $(6)$
* Both functions should reduce sum over the last three axes of the input Tensor (`height`, `width` and `channels`)

```python
#### GRADED CELL ####

# Complete the following functions.
# Make sure to not change the function names or arguments.

def forward_log_det_jacobian(x, b, shift_and_log_scale_fn):
    """
    This function takes the input Tensor x, binary mask b and callable
    shift_and_log_scale_fn as arguments.
    This function should compute and return the log of the Jacobian determinant
    of the forward transformation in equation (5)
    """

def inverse_log_det_jacobian(y, b, shift_and_log_scale_fn):
    """
    This function takes the input Tensor y, binary mask b and callable
    shift_and_log_scale_fn as arguments.
    This function should compute and return the log of the Jacobian determinant
    of the forward transformation in equation (6)
    """

```

You are now ready to create the coupling layer bijector, using bijector subclassing. You should complete the class below to define the `AffineCouplingLayer`.

* You should complete the initialiser `__init__`, and the internal class method `_get_mask`
* The `_forward`, `_inverse`, `_forward_log_det_jacobian` and `_inverse_log_det_jacobian` methods are completed for you using the functions you have written above. Do not modify these methods
* The initialiser takes the `shift_and_log_scale_fn` callable, `mask_type` string (either `"checkerboard"` or `"channel"`, `orientation` (integer, either `0` or `1`) as required arguments, and allows for extra keyword arguments
  * The required arguments should be set as class attributes in the initialiser (note that the `shift_and_log_scale_fn` attribute is being used in the `_forward`, `_inverse`, `_forward_log_det_jacobian` and `_inverse_log_det_jacobian` methods)
  * The initialiser should call the base class initialiser, and pass in any extra keyword arguments
  * The class should have a required number of event dimensions equal to 3
* The internal method `_get_mask` takes a `shape` as an argument, which is the shape of an input Tensor
  * This method should use the `checkerboard_binary_mask` and `channel_binary_mask` functions above, as well as the `mask_type` and `orientation` arguments passed to the initialiser to compute and return the required binary mask
  * This method is used in each of the `_forward`, `_inverse`, `_forward_log_det_jacobian` and `_inverse_log_det_jacobian` methods

```python
#### GRADED CELL ####

# Complete the following class.
# Make sure to not change the class or method names or arguments.

class AffineCouplingLayer(tfb.Bijector):
    """
    Class to implement the affine coupling layer.
    Complete the __init__ and _get_mask methods according to the instructions above.
    """

    def __init__(self, shift_and_log_scale_fn, mask_type, orientation, **kwargs):
        """
        The class initialiser takes the shift_and_log_scale_fn callable, mask_type,
        orientation and possibly extra keywords arguments. It should call the
        base class initialiser, passing any extra keyword arguments along.
        It should also set the required arguments as class attributes.
        """

    def _get_mask(self, shape):
        """
        This internal method should use the binary mask functions above to compute
        and return the binary mask, according to the arguments passed in to the
        initialiser.
        """

    def _forward(self, x):
        b = self._get_mask(x.shape)
        return forward(x, b, self.shift_and_log_scale_fn)

    def _inverse(self, y):
        b = self._get_mask(y.shape)
        return inverse(y, b, self.shift_and_log_scale_fn)

    def _forward_log_det_jacobian(self, x):
        b = self._get_mask(x.shape)
        return forward_log_det_jacobian(x, b, self.shift_and_log_scale_fn)

    def _inverse_log_det_jacobian(self, y):
        b = self._get_mask(y.shape)
        return inverse_log_det_jacobian(y, b, self.shift_and_log_scale_fn)
```

```python
# Test your function by creating an instance of the AffineCouplingLayer class

affine_coupling_layer = AffineCouplingLayer(conv_resnet, 'channel', orientation=1,
                                            name='affine_coupling_layer')
```

```python
# The following should return a Tensor of the same shape as the input

affine_coupling_layer.forward(tf.random.normal((16, 32, 32, 3))).shape
```

```python
# The following should compute a log_det_jacobian for each event in the batch

affine_coupling_layer.forward_log_det_jacobian(tf.random.normal((16, 32, 32, 3)), event_ndims=3).shape
```

#### Combining the affine coupling layers

In the affine coupling layer, part of the input remains unchanged in the transformation $(5)$. In order to allow transformation of all of the input, several coupling layers are composed, with the orientation of the mask being reversed in subsequent layers.

```python
# Run this cell to download and view a sketch of the affine coupling layers

!wget -q -O alternating_masks.png --no-check-certificate "https://docs.google.com/uc?export=download&id=1r1vASfLOW3kevxRzFUXhCtHN8dzldHve"
Image("alternating_masks.png", width=800)
```

Figure 2. RealNVP alternates the orientation of masks from one affine coupling layer to the next. From the original paper.

Our model design will be similar to the original architecture; we will compose three affine coupling layers with checkerboard masking, followed by a batch normalization bijector (`tfb.BatchNormalization` is a built-in bijector), followed by a squeezing operation, followed by three more affine coupling layers with channel-wise masking and a final batch normalization bijector.

The squeezing operation divides the spatial dimensions into 2x2 squares, and reshapes a Tensor of shape `(H, W, C)` into a Tensor of shape `(H // 2, W // 2, 4 * C)` as shown in Figure 1.

The squeezing operation is also a bijective operation, and has been provided for you in the class below.

```python
# Bijector class for the squeezing operation

class Squeeze(tfb.Bijector):

    def __init__(self, name='Squeeze', **kwargs):
        super(Squeeze, self).__init__(forward_min_event_ndims=3, is_constant_jacobian=True,
                                      name=name, **kwargs)

    def _forward(self, x):
        input_shape = x.shape
        height, width, channels = input_shape[-3:]
        y = tfb.Reshape((height // 2, 2, width // 2, 2, channels), event_shape_in=(height, width, channels))(x)
        y = tfb.Transpose(perm=[0, 2, 1, 3, 4])(y)
        y = tfb.Reshape((height // 2, width // 2, 4 * channels),
                        event_shape_in=(height // 2, width // 2, 2, 2, channels))(y)
        return y

    def _inverse(self, y):
        input_shape = y.shape
        height, width, channels = input_shape[-3:]
        x = tfb.Reshape((height, width, 2, 2, channels // 4), event_shape_in=(height, width, channels))(y)
        x = tfb.Transpose(perm=[0, 2, 1, 3, 4])(x)
        x = tfb.Reshape((2 * height, 2 * width, channels // 4),
                        event_shape_in=(height, 2, width, 2, channels // 4))(x)
        return x

    def _forward_log_det_jacobian(self, x):
        return tf.constant(0., x.dtype)

    def _inverse_log_det_jacobian(self, y):
        return tf.constant(0., y.dtype)

    def _forward_event_shape_tensor(self, input_shape):
        height, width, channels = input_shape[-3], input_shape[-2], input_shape[-1]
        return height // 2, width // 2, 4 * channels

    def _inverse_event_shape_tensor(self, output_shape):
        height, width, channels = output_shape[-3], output_shape[-2], output_shape[-1]
        return height * 2, width * 2, channels // 4
```

You can see the effect of the squeezing operation on some example inputs in the cells below. In the forward transformation, each spatial dimension is halved, whilst the channel dimension is multiplied by 4. The opposite happens in the inverse transformation.

```python
# Test the Squeeze bijector

squeeze = Squeeze()
squeeze(tf.ones((10, 32, 32, 3))).shape
```

```python
# Test the inverse operation

squeeze.inverse(tf.ones((10, 4, 4, 96))).shape
```

We can now construct a block of coupling layers according to the architecture described above. You should complete the following function to chain together the bijectors that we have constructed, to form a bijector that performs the following operations in the forward transformation:

* Three `AffineCouplingLayer` bijectors with `"checkerboard"` masking with orientations `0, 1, 0` respectively
* A `BatchNormalization` bijector
* A `Squeeze` bijector
* Three more `AffineCouplingLayer` bijectors with `"channel"` masking with orientations `0, 1, 0` respectively
* Another `BatchNormalization` bijector

The function takes the following arguments:
* `shift_and_log_scale_fns`: a list or tuple of six conv_resnet models
  * The first three models in this list are used in the three coupling layers with checkerboard masking
  * The last three models in this list are used in the three coupling layers with channel masking
* `squeeze`: an instance of the `Squeeze` bijector

_NB: at this point, we would like to point out that we are following the exposition in the original paper, and think of the forward transformation as acting on the input image. Note that this is in contrast to the convention of using the forward transformation for sampling, and the inverse transformation for computing log probs._

```python
#### GRADED CELL ####

# Complete the following function.
# Make sure to not change the function name or arguments.

def realnvp_block(shift_and_log_scale_fns, squeeze):
    """
    This function takes a list or tuple of six conv_resnet models, and an
    instance of the Squeeze bijector.
    The function should construct the chain of bijectors described above,
    using the conv_resnet models in the coupling layers.
    The function should then return the chained bijector.
    """

```

```python
# Run your function to create an instance of the bijector

checkerboard_fns = []
for _ in range(3):
    checkerboard_fns.append(get_conv_resnet((32, 32, 3), 512))
channel_fns = []
for _ in range(3):
    channel_fns.append(get_conv_resnet((16, 16, 12), 512))

block = realnvp_block(checkerboard_fns + channel_fns, squeeze)
```

```python
# Test the bijector on a dummy input

block.forward(tf.random.normal((10, 32, 32, 3))).shape
```

#### Multiscale architecture

The final component of the RealNVP is the multiscale architecture. The squeeze operation reduces the spatial dimensions but increases the channel dimensions. After one of the blocks of coupling-squeeze-coupling that you have implemented above, half of the dimensions are factored out as latent variables, while the other half is further processed through subsequent layers. This results in latent variables that represent different scales of features in the model.

```python
# Run this cell to download and view a sketch of the multiscale architecture

!wget -q -O multiscale.png --no-check-certificate "https://docs.google.com/uc?export=download&id=19Sc6PKbc8Bi2DoyupHZxHvB3m6tw-lki"
Image("multiscale.png", width=700)
```

Figure 3. RealNVP creates latent variables at different scales by factoring out half of the dimensions at each scale. From the original paper.

The final scale does not use the squeezing operation, and instead applies four affine coupling layers with alternating checkerboard masks.

The multiscale architecture for two latent variable scales is implemented for you in the following bijector.

```python
# Bijector to implement the multiscale architecture

class RealNVPMultiScale(tfb.Bijector):

    def __init__(self, **kwargs):
        super(RealNVPMultiScale, self).__init__(forward_min_event_ndims=3, **kwargs)

        # First level
        shape1 = (32, 32, 3)  # Input shape
        shape2 = (16, 16, 12)  # Shape after the squeeze operation
        shape3 = (16, 16, 6)  # Shape after factoring out the latent variable
        self.conv_resnet1 = get_conv_resnet(shape1, 64)
        self.conv_resnet2 = get_conv_resnet(shape1, 64)
        self.conv_resnet3 = get_conv_resnet(shape1, 64)
        self.conv_resnet4 = get_conv_resnet(shape2, 128)
        self.conv_resnet5 = get_conv_resnet(shape2, 128)
        self.conv_resnet6 = get_conv_resnet(shape2, 128)
        self.squeeze = Squeeze()
        self.block1 = realnvp_block([self.conv_resnet1, self.conv_resnet2,
                                    self.conv_resnet3, self.conv_resnet4,
                                    self.conv_resnet5, self.conv_resnet6], self.squeeze)

        # Second level
        self.conv_resnet7 = get_conv_resnet(shape3, 128)
        self.conv_resnet8 = get_conv_resnet(shape3, 128)
        self.conv_resnet9 = get_conv_resnet(shape3, 128)
        self.conv_resnet10 = get_conv_resnet(shape3, 128)
        self.coupling_layer1 = AffineCouplingLayer(self.conv_resnet7, 'checkerboard', 0)
        self.coupling_layer2 = AffineCouplingLayer(self.conv_resnet8, 'checkerboard', 1)
        self.coupling_layer3 = AffineCouplingLayer(self.conv_resnet9, 'checkerboard', 0)
        self.coupling_layer4 = AffineCouplingLayer(self.conv_resnet10, 'checkerboard', 1)
        self.block2 = tfb.Chain([self.coupling_layer4, self.coupling_layer3,
                                 self.coupling_layer2, self.coupling_layer1])

    def _forward(self, x):
        h1 = self.block1.forward(x)
        z1, h2 = tf.split(h1, 2, axis=-1)
        z2 = self.block2.forward(h2)
        return tf.concat([z1, z2], axis=-1)

    def _inverse(self, y):
        z1, z2 = tf.split(y, 2, axis=-1)
        h2 = self.block2.inverse(z2)
        h1 = tf.concat([z1, h2], axis=-1)
        return self.block1.inverse(h1)

    def _forward_log_det_jacobian(self, x):
        log_det1 = self.block1.forward_log_det_jacobian(x, event_ndims=3)
        h1 = self.block1.forward(x)
        _, h2 = tf.split(h1, 2, axis=-1)
        log_det2 = self.block2.forward_log_det_jacobian(h2, event_ndims=3)
        return log_det1 + log_det2

    def _inverse_log_det_jacobian(self, y):
        z1, z2 = tf.split(y, 2, axis=-1)
        h2 = self.block2.inverse(z2)
        log_det2 = self.block2.inverse_log_det_jacobian(z2, event_ndims=3)
        h1 = tf.concat([z1, h2], axis=-1)
        log_det1 = self.block1.inverse_log_det_jacobian(h1, event_ndims=3)
        return log_det1 + log_det2

    def _forward_event_shape_tensor(self, input_shape):
        height, width, channels = input_shape[-3], input_shape[-2], input_shape[-1]
        return height // 4, width // 4, 16 * channels

    def _inverse_event_shape_tensor(self, output_shape):
        height, width, channels = output_shape[-3], output_shape[-2], output_shape[-1]
        return 4 * height, 4 * width, channels // 16
```

```python
# Create an instance of the multiscale architecture

multiscale_bijector = RealNVPMultiScale()
```

#### Data preprocessing bijector

We will also preprocess the image data before sending it through the RealNVP model. To do this, for a Tensor $x$ of pixel values in $[0, 1]^D$, we transform $x$ according to the following:

$$
T(x) = \text{logit}\left(\alpha + (1 - 2\alpha)x\right),\tag{7}
$$

where $\alpha$ is a parameter, and the logit function is the inverse of the sigmoid function, and is given by

$$
\text{logit}(p) = \log (p) - \log (1 - p).
$$

You should now complete the following function to construct this bijector from in-built bijectors from the bijectors module.

* The function takes the parameter `alpha` as an input, which you can assume to take a small positive value ($\ll0.5$)
* The function should construct and return a bijector that computes $(7)$ in the forward pass

```python
#### GRADED CELL ####

# Complete the following function.
# Make sure to not change the function name or arguments.

def get_preprocess_bijector(alpha):
    """
    This function should create a chained bijector that computes the
    transformation T in equation (7) above.
    This can be computed using in-built bijectors from the bijectors module.
    Your function should then return the chained bijector.
    """

```

```python
# Create an instance of the preprocess bijector

preprocess = get_preprocess_bijector(0.05)
```

#### Train the RealNVP model

Finally, we will use our RealNVP model to train

We will use the following model class to help with the training process.

```python
# Helper class for training

class RealNVPModel(Model):

    def __init__(self, **kwargs):
        super(RealNVPModel, self).__init__(**kwargs)
        self.preprocess = get_preprocess_bijector(0.05)
        self.realnvp_multiscale = RealNVPMultiScale()
        self.bijector = tfb.Chain([self.realnvp_multiscale, self.preprocess])

    def build(self, input_shape):
        output_shape = self.bijector(tf.expand_dims(tf.zeros(input_shape[1:]), axis=0)).shape
        self.base = tfd.Independent(tfd.Normal(loc=tf.zeros(output_shape[1:]), scale=1.),
                                    reinterpreted_batch_ndims=3)
        self._bijector_variables = (
            list(self.bijector.variables))
        self.flow = tfd.TransformedDistribution(
            distribution=self.base,
            bijector=tfb.Invert(self.bijector),
        )
        super(RealNVPModel, self).build(input_shape)

    def call(self, inputs, training=None, **kwargs):
        return self.flow

    def sample(self, batch_size):
        sample = self.base.sample(batch_size)
        return self.bijector.inverse(sample)
```

```python
# Create an instance of the RealNVPModel class

realnvp_model = RealNVPModel()
realnvp_model.build((1, 32, 32, 3))
```

```python
# Compute the number of variables in the model

print("Total trainable variables:")
print(sum([np.prod(v.shape) for v in realnvp_model.trainable_variables]))
```

Note that the model's `call` method returns the `TransformedDistribution` object. Also, we have set up our datasets to return the input image twice as a 2-tuple. This is so we can train our model with negative log-likelihood as normal.

```python
# Define the negative log-likelihood loss function

def nll(y_true, y_pred):
    return -y_pred.log_prob(y_true)
```

It is recommended to use the GPU accelerator hardware on Colab to train this model, as it can take some time to train. Note that it is not required to train the model in order to pass this assignment. For optimal results, a larger model should be trained for longer.

```python
# Compile and train the model

optimizer = Adam()
realnvp_model.compile(loss=nll, optimizer=Adam())
realnvp_model.fit(train_ds, validation_data=val_ds, epochs=30)
```

```python
# Evaluate the model

realnvp_model.evaluate(test_ds)
```

#### Generate some samples

```python
# Sample from the model

samples = realnvp_model.sample(8).numpy()
```

```python
# Display the samples

n_img = 8
f, axs = plt.subplots(2, n_img // 2, figsize=(14, 7))

for k, image in enumerate(samples):
    i = k % 2
    j = k // 2
    axs[i, j].imshow(image[0])
    axs[i, j].axis('off')
f.subplots_adjust(wspace=0.01, hspace=0.03)
```

Congratulations on completing this programming assignment! In the next week of the course we will look at the variational autoencoder.

ICL_Probabilistic_Deep_Learning_with_TensorFlow/Week 3 RealNVP Normalising Flow.ipynb


# Free Body Diagram for Rigid Bodies

Renato Naville Watanabe

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

## Equivalent systems

A set of forces and moments is considered equivalent if its resultant force and sum of the moments computed relative to a given point are the same. Normally, we want to reduce all the forces and moments being applied to a body into a single force and a single moment.

We have done this with particles for the resultant force. The resultant force is simply the sum of all the forces being applied to the body.

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

where $\vec{\bf{F_i}}$ is each force applied to the body.

Similarly, the total moment applied to the body relative to a point O is:

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

where $\vec{\bf{r_{i/O}}} $ is the vector from the point O to the point where the force $\vec{\bf{F_i}}$ is being applied.

But where the resultant force should be applied in the body? If the resultant force were applied to any point different from the point O, it would produce an additional  moment to the body relative to point O. So, the resultant force must be applied to the point O.

So, any set of forces can be reduced to a moment relative to a chosen point O and a resultant force applied to the point O.

To compute the resultant force and moment relative to another point O', the new moment is:

\begin{equation}
    \vec{\bf{M_{O'}}} = \vec{\bf{M_O}} + \vec{\bf{r_{O'/O}}} \times \vec{\bf{F}}
\end{equation}

And the resultant force is the same.

It is worth to note that if the resultant force  $\vec{\bf{F}}$ is zero, than the moment is the same relative to any point.

### Steps to draw a free-body diagram

The steps to draw the free-body diagram of a body is very similar to the case of particles.

1 - Draw separately each object considered in the problem. How you separate depends on what questions you want to answer.

2 - Identify the forces acting on each object. If you are analyzing more than one object, remember the third Newton law (action and reaction), and identify where the reaction of a force is being applied. Whenever a movement of translation of the body is constrained, a force at the direction of the constraint must exist.

3 - Identify the moments acting on each object. In the case you are analyzing more than one object, you must consider the  action and reaction law (third Newton-Euler law). Whenever a movement of rotation of the body is constrained, a moment at the direction of the constraint must exist.

4 - Draw all the identified forces, representing them as vectors. The vectors should be represented with the origin in the object.

5 - Draw all the identified moments, representing them as vectors. In planar movements, the moments we be orthogonal to the considered plane. In these cases, normally the moment vectors are represented as curved arrows.

6 - If necessary, you should represent the reference frame (or references frames in case you use more than one reference frame) in the free-body diagram.

7 - After this, you can solve the problem using the First and Second Newton-Euler  Laws (see, e.g,  [Newton-Euler Laws](newton_euler_equations.ipynb)) to find the motion of the body.

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

\begin{equation}
    \vec{\bf{M_O}} = I_{zz}^{cm}\alpha + m \vec{\bf{r_{cm/O}}} \times \vec{\bf{a_{cm}}}=I_{zz}^{cm}\frac{d^2\theta}{dt^2} + m \vec{\bf{r_{cm/O}}} \times \frac{d^2\vec{\bf{r_{cm}}}}{dt^2}
\end{equation}

Below, we will see some examples of how to draw the free-body diagram and obtain the equation of motion.

## 1) Horizontal fixed bar

The first example is an example of statics. The bar has no velocity and no acceleration.

The free-body diagram of the bar is depicted below. At the point where the bar is connected to the wall, there is a force $\vec{\bf{F_1}}$ constraining the translation movement of the point O and a moment $\vec{\bf{M}}$ constraining the rotation of the bar.

The resultant force being applied to the bar is:

\begin{equation}
    \vec{\bf{F}} = -mg\hat{\bf{j}} + \vec{\bf{F_1}}
\end{equation}

And the total moment in the z direction around the point O is:

\begin{equation}
    \vec{\bf{M_O}} = \vec{\bf{r_{C/O}}}\times-mg\hat{\bf{j}} + \vec{\bf{M}}
\end{equation}

The vector from the point O to the point C is given by $\vec{\bf{r_{C/O}}} =\frac{l}{2}\hat{\bf{i}}$.

As the bar is fixed, all the accelerations are zero. So we can find the forces and the moment at the constraint.

\begin{equation}
    \vec{\bf{F}} = \vec{\bf{0}} \rightarrow -mg\hat{\bf{j}} + \vec{\bf{F_1}} = \vec{\bf{0}} \rightarrow   \vec{\bf{F_1}} = mg\hat{\bf{j}}
\end{equation}

\begin{equation}
    \vec{\bf{M_O}} = \vec{\bf{0}} \rightarrow  \vec{\bf{r_{C/O}}}\times-mg\hat{\bf{j}} + \vec{\bf{M}} = \vec{\bf{0}} \rightarrow \frac{l}{2}\hat{\bf{i}}\times-mg\hat{\bf{j}} + \vec{\bf{M}} = \vec{\bf{0}} \rightarrow -\frac{mgl}{2}\hat{\bf{k}} + \vec{\bf{M}} = \vec{\bf{0}} \rightarrow \vec{\bf{M}} = \frac{mgl}{2}\hat{\bf{k}}
\end{equation}

## 2) Rotating ball with drag force

A basketball has a mass $m = 0.63$ kg and radius equal $R = 12$ cm. A basketball player shots the ball from the free-throw line (4.6 m from the basket) with a speed of 9.5 m/s, angle of 51 degrees with the court, height of 2 m and angular velocity of 42 rad/s. At the ball is acting a drag force proportional to the modulus of the ball velocity in the opposite direction of the velocity and a drag moment, proportional and in the opposite direction of the angular velocity of the ball. Consider the moment of inertia of the ball as $I_{zz}^{cm} = \frac{2mR^2}{3}$.

Below is depicted the free-body diagram of the ball.

The resultant force being applied at the ball is:

\begin{equation}
    \vec{\bf{F}} = -mg\hat{\bf{j}} - b_l\vec{\bf{v}} + b_m\omega\hat{\bf{k}}\times \vec{\bf{v}} = -mg\hat{\bf{j}} - b_l\frac{d\vec{\bf{r}}}{dt} = -mg\hat{\bf{j}} - b_l\frac{d\vec{\bf{r}}}{dt}
\end{equation}

\begin{equation}
    \vec{\bf{M_C}} = - b_r\omega\hat{\bf{k}}=- b_r\frac{d\theta}{dt}\hat{\bf{k}}
\end{equation}

\begin{equation}
    \frac{d\vec{\bf{H_C}}}{dt} = I_{zz}^{C}\frac{d^2\theta}{dt^2}\hat{\bf{k}}
\end{equation}

So, by the second Newton-Euler law:

\begin{equation}
    \frac{d\vec{\bf{H_C}}}{dt}=\vec{\bf{M_C}} \rightarrow I_{zz}^{C}\frac{d^2\theta}{dt^2} = - b_r\frac{d\theta}{dt}
\end{equation}

and by the first Newton-Euler law (for a revision on Newton-Euler laws, [see this notebook](newton_euler_equations.ipynb)):

\begin{equation}
    m\frac{d\vec{\bf{r}}}{dt}=\vec{\bf{F}} \rightarrow \frac{d\vec{\bf{r}}}{dt} = -g\hat{\bf{j}} - \frac{b_l}{m}\frac{d\vec{\bf{r}}}{dt}
\end{equation}

So, we can split the differential equations above in three equations:

\begin{equation}
    \frac{d^2\theta}{dt^2} = - \frac{b_r}{I_{zz}^{C}}\frac{d\theta}{dt}
\end{equation}

\begin{equation}
    \frac{d^2x}{dt^2} = - \frac{b_l}{m}\frac{dx}{dt}
\end{equation}

\begin{equation}
    \frac{d^2y}{dt^2} = -g - \frac{b_l}{m}\frac{dy}{dt}
\end{equation}

To solve these equations numerically, we can split each of these equations in first-order equations and the use a numerical method to integrate them. The first-order equations we be written in a matrix form, considering the moment of inertia of the ball as $I_{zz}^{C}=\frac{2mR^2}{3}$:

\begin{equation}
    \left[\begin{array}{c}\frac{d\omega}{dt}\\\frac{dv_x}{dt}\\\frac{dv_y}{dt}\\\frac{d\theta}{dt}\\\frac{dx}{dt}\\\frac{dy}{dt} \end{array}\right] = \left[\begin{array}{c}- \frac{3b_r}{2mR^2}\omega\\- \frac{b_l}{m}v_x\\-g - \frac{b_l}{m}v_y\\\omega\\v_x\\v_y\end{array}\right]
\end{equation}

Below, the equations were solved numerically by using the Euler method (for a revision on numerical methods to solve ordinary differential equations, [see this notebook](OrdinaryDifferentialEquation.ipynb)).

```python
m = 0.63
R = 0.12
I = 2.0/3*m*R**2

bl = 0.5
br = 0.001
g = 9.81

x0 = 0
y0 = 2

v0 = 9.5
angle = 51*np.pi/180.0

vx0 = v0*np.cos(angle)
vy0 = v0*np.sin(angle)

dt = 0.001
t = np.arange(0, 2.1, dt)

x = x0
y = y0
vx = vx0
vy = vy0

omega = 42
theta = 0

r = np.array([x,y])
ballAngle = np.array([theta])

state = np.array([omega, vx, vy, theta, x, y])

while state[4]<=4.6:

    dstatedt = np.array([-br/I*state[0],-bl/m*state[1], -g-bl/m*state[2],state[0], state[1], state[2] ])
    state = state + dt * dstatedt

    r = np.vstack((r, [state[4], state[5]]))
    ballAngle = np.vstack((ballAngle, [state[3]]))

plt.figure()
plt.plot(r[0:-1:50,0], r[0:-1:50,1], 'o', color = np.array([1, 0.6,0]), markerSize =10)
plt.plot(np.array([4, 4.45]), np.array([3.05, 3.05]))
for i in range(len(r[0:-1:50,0])):
    plt.plot(r[i*50,0]+np.array([-0.05*(np.cos(ballAngle[i*50])-np.sin(ballAngle[i*50])), 0.05*(np.cos(ballAngle[i*50])-np.sin(ballAngle[i*50]))]),
             r[i*50,1] + np.array([-0.05*(np.sin(ballAngle[i*50])+np.cos(ballAngle[i*50])), 0.05*(np.sin(ballAngle[i*50])+np.cos(ballAngle[i*50]))]),'k')
plt.ylim((0,4.5))
plt.show()
print(state[0])
```

    <IPython.core.display.Javascript object>

    34.5034544615

Above is the trajectory of the ball until it reaches the basket (height of 3.05 m, marked with a blue line).

## 3) Pendulum

Now, we will analyze a pendulum. It consists of a bar with its upper part linked to a hinge.

Below is the free-body diagram of the bar. On the center of mass of the bar is acting the gravitational force. On the point O, where there is a hinge, a force $\vec{\bf{F_1}}$ restrains the point to translate. As the hinge does not constrains a rotational movement of the bar, there is no moment applied by the hinge.

To find the equation of motion of the bar we can use the second Newton-Euler law. So, we must find the sum of the moments and the resultant force being applied to the bar. The sum of moments could be computed relative to any point, but if we choose the fixed point O, it is easier because we can ignore the force $\vec{\bf{F_1}}$.

The moment around the fixed point is:

\begin{equation}
    \vec{\bf{M_O}} = \vec{\bf{r_{cm/O}}} \times (-mg\hat{\bf{j}})
\end{equation}

The resultant force applied in the bar is:

\begin{equation}
        \vec{\bf{F}} = -mg\hat{\bf{j}} + \vec{\bf{F_1}}
\end{equation}

The angular momentum derivative of the bar around point O is:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = I_{zz}^{cm} \frac{d^2\theta}{dt^2} \hat{\bf{k}}  + m \vec{\bf{r_{cm/O}}} \times \vec{\bf{a_{cm}}}
\end{equation}

The vector from point O to the center of mass is:

\begin{equation}
    \vec{\bf{r_{cm/O}}} = \frac{l}{2}\sin{\theta}\hat{\bf{i}}-\frac{l}{2}\cos{\theta}\hat{\bf{j}}
\end{equation}

The position of the center of mass is, considering the point O as the origin, equal to $\vec{\bf{r_{cm/O}}}$. So, the center of mass acceleration is obtained by deriving it twice.

\begin{equation}
    \vec{\bf{v_{cm}}} = \frac{\vec{\bf{r_{cm/O}}}}{dt} = \frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d\theta}{dt} \rightarrow \vec{\bf{a_{cm}}} = \frac{l}{2}(-\sin{\theta}\hat{\bf{i}}+\cos{\theta}\hat{\bf{j}})\left(\frac{d\theta}{dt}\right)^2 + \frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d^2\theta}{dt^2}
\end{equation}

So, the moment around the point O:

\begin{equation}
    \vec{\bf{M_O}} = \left(\frac{l}{2}\sin{\theta}\hat{\bf{i}}-\frac{l}{2}\cos{\theta}\hat{\bf{j}}\right) \times (-mg\hat{\bf{j}}) = \frac{-mgl}{2}\sin{\theta}\hat{\bf{k}}
\end{equation}

And the derivative of the angular momentum is:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = I_{zz}^{cm} \frac{d^2\theta}{dt^2}\hat{\bf{k}}  + m \frac{l}{2}(\sin{\theta}\hat{\bf{i}}-\cos{\theta}\hat{\bf{j}}) \times \left[ \frac{l}{2}(-\sin{\theta}\hat{\bf{i}}+\cos{\theta}\hat{\bf{j}})\left(\frac{d\theta}{dt}\right)^2 + \frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d^2\theta}{dt^2}    \right] = I_{zz}^{cm} \frac{d^2\theta}{dt^2}\hat{\bf{k}}  + m \frac{l^2}{4}\frac{d^2\theta}{dt^2} \hat{\bf{k}}  =\left(I_{zz}^{cm} + \frac{ml^2}{4}\right)\frac{d^2\theta}{dt^2} \hat{\bf{k}}
\end{equation}

Now, by using the Newton-Euler laws, we can obtain the differential equation that describes the bar angle along time:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = \vec{\bf{M_O}} \rightarrow \frac{-mgl}{2}\sin{\theta} = \left(I_{zz}^{cm} + \frac{ml^2}{4}\right)\frac{d^2\theta}{dt^2} \rightarrow \frac{d^2\theta}{dt^2} = \frac{-2mgl}{\left(4I_{zz}^{cm} + ml^2\right)}\sin{\theta}
\end{equation}

The moment of inertia of the bar relative to its center of mass is $I_{zz}^{cm} = \frac{ml^2}{12}$ (for a revision on moment of inertia see [this notebook](CenterOfMassAndMomentOfInertia.ipynb)).  So, the equation of motion of the pendulum is:

\begin{equation}
    \frac{d^2\theta}{dt^2} = \frac{-3g}{2l}\sin{\theta}
\end{equation}

## 4) Inverted Pendulum

This example we analyze the inverted pendulum. It consists of a bar with its lowest extremity linked to a hinge.

 The free-body diagram of the bar is depicted below. At the point where the bar is linked to the hinge, a force $\vec{\bf{F_1}}$ acts at the bar due to the restraint of the translation imposed to the point O by the hinge. Additionally, the gravitational force acts at the center of mass of the bar.

Similarly to pendulum, in this case we can find the equation of motion of the bar by using the second Newton-Euler law. To do that we must find the sum of the moments and the resultant force being applied to the bar. The sum of moments could be computed relative to any point, but if we choose the fixed point O, it is easier because we can ignore the force $\vec{\bf{F_1}}$.

The moment around the fixed point is:

\begin{equation}
    \vec{\bf{M_O}} = \vec{\bf{r_{cm/O}}} \times (-mg\hat{\bf{j}})
\end{equation}

The resultant force applied in the bar is:

\begin{equation}
        \vec{\bf{F}} = -mg\hat{\bf{j}} + \vec{\bf{F_1}}
\end{equation}

The angular momentum derivative of the bar around point O is:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = I_{zz}^{cm} \frac{d^2\theta}{dt^2} \hat{\bf{k}}  + m \vec{\bf{r_{cm/O}}} \times \vec{\bf{a_{cm}}}
\end{equation}

Until this part, the equations are exactly the same of the pendulum example. The difference is in the kinematics of the bar. Now,the vector from point O to the center of mass is:

\begin{equation}
    \vec{\bf{r_{cm/O}}} = -\frac{l}{2}\sin{\theta}\hat{\bf{i}}+\frac{l}{2}\cos{\theta}\hat{\bf{j}}
\end{equation}

The position of the center of mass of the bar is equal to the vector $\vec{\bf{r_{cm/O}}}$, since the point O is with velocity zero relative to the global reference frame. So the center of mass acceleration can be obtained by deriving this vector twice:

\begin{equation}
    \vec{\bf{v_{cm}}} = \frac{\vec{\bf{r_{cm/O}}}}{dt} = -\frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d\theta}{dt} \rightarrow \vec{\bf{a_{cm}}} = \frac{l}{2}(\sin{\theta}\hat{\bf{i}}-\cos{\theta}\hat{\bf{j}})\left(\frac{d\theta}{dt}\right)^2 - \frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d^2\theta}{dt^2}
\end{equation}

So, the moment around the point O is:

\begin{equation}
    \vec{\bf{M_O}} = \left(-\frac{l}{2}\sin{\theta}\hat{\bf{i}}+\frac{l}{2}\cos{\theta}\hat{\bf{j}}\right) \times (-mg\hat{\bf{j}}) = \frac{mgl}{2}\sin{\theta} \hat{\bf{k}}
\end{equation}

And the derivative of the angular momentum is:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = I_{zz}^{cm} \frac{d^2\theta}{dt^2} \hat{\bf{k}}  + m \left(-\frac{l}{2}\sin{\theta}\hat{\bf{i}}+\frac{l}{2}\cos{\theta}\hat{\bf{j}}\right) \times \left[\frac{l}{2}(\sin{\theta}\hat{\bf{i}}-\cos{\theta}\hat{\bf{j}})\left(\frac{d\theta}{dt}\right)^2 - \frac{l}{2}(\cos{\theta}\hat{\bf{i}}+\sin{\theta}\hat{\bf{j}})\frac{d^2\theta}{dt^2}\right] = I_{zz}^{cm} \frac{d^2\theta}{dt^2} \hat{\bf{k}}  + m \frac{l^2}{4}\frac{d^2\theta}{dt^2}\hat{\bf{k}} = \left(I_{zz}^{cm} +  \frac{ml^2}{4}\right)\frac{d^2\theta}{dt^2}\hat{\bf{k}}
\end{equation}

By using the Newton-Euler laws, we can find the equation of motion of the bar:

\begin{equation}
    \frac{d\vec{\bf{H_O}}}{dt} = \vec{\bf{M_O}} \rightarrow \left(I_{zz}^{cm} +  \frac{ml^2}{4}\right)\frac{d^2\theta}{dt^2} = \frac{mgl}{2}\sin{\theta} \rightarrow \frac{d^2\theta}{dt^2} = \frac{2mgl}{\left(4I_{zz}^{cm} + ml^2\right)}\sin(\theta)
\end{equation}

The moment of inertia of the bar is $I_{zz}^{cm} = \frac{ml^2}{12}$. So, the equation of motion of the bar is:

\begin{equation}
    \frac{d^2\theta}{dt^2} = \frac{3g}{2l}\sin(\theta)
\end{equation}

## 5) Human quiet standing

A very simple model of the human quiet standing (and still today frequently used) is to use an inverted pendulum to model the human body. On [this notebook](http://nbviewer.jupyter.org/github/demotu/BMC/blob/master/notebooks/IP_Model.ipynb) there is a more comprehensive explanation of human standing.

Adapted from [Elias, Watanabe and Kohn (2014)](http://dx.doi.org/10.1371/journal.pcbi.1003944)

Below is depicted the free-body diagram of the foot and the rest of the body. At the ankle joint there is a constraint to the translation of the ankle joint. So, a force $\vec{\bf{F_1}}$ is applied to the body at the ankle joint. By the third Newton law, a force  $-\vec{\bf{F_1}}$ is applied to the foot at the ankle joint. At the center of mass of the body and of the foot, gravitational forces are applied. Additionally, a ground reaction force is applied to the foot at the center of pressure of the forces applied at the foot.

Additionally, a moment $\vec{\bf{T_A}}$ is applied at the body and its reaction is applied to the foot. This moment $\vec{\bf{T_A}}$ comes from the muscles around the ankle joint. It is usual in Biomechanics to represent the net torque generated by all the muscles on a single joint as a single moment applied to the body.

The process to obtain the equation of motion of the angle is very similar. The moment around the ankle being applied to the body is:

\begin{equation}
    \vec{\bf{M_A}} = \vec{\bf{T_A}} + \vec{\bf{r_{cm/A}}} \times (-m_Bg\hat{\bf{j}})
\end{equation}

And the derivative of the angular momentum is:

\begin{equation}
    \frac{d\vec{\bf{H_A}}}{dt} = I_{zz}^{cm}\frac{d\theta_A}{dt} + m\vec{\bf{r_{cm/A}}} \times \vec{\bf{a_{cm}}}
\end{equation}

To find the kinematics of the bar, we could do the same procedure we have used in the pendulum and inverted pendulum examples, but this time we will use polar coordinates (for a revision on polar coordinates, see [Polar coordinates notebook](PolarCoordinates.ipynb)).

\begin{equation}
    \vec{\bf{r_{cm/A}}} = \vec{\bf{r_{cm}}} = h_G\hat{\bf{e_r}} \rightarrow \vec{\bf{v_{cm}}} = h_G\frac{d\theta_A}{dt}\hat{\bf{e_\theta}} \rightarrow \vec{\bf{a_{cm}}} = -h_G\left(\frac{d\theta_A}{dt}\right)^2\hat{\bf{e_r}} + h_G\frac{d^2\theta_A}{dt^2}\hat{\bf{e_\theta}}
\end{equation}

where $\hat{\bf{e_r}} = -\sin(\theta_A)\hat{\bf{i}} + \cos(\theta_A)\hat{\bf{j}}$ and $\hat{\bf{e_\theta}} = -\cos(\theta_A)\hat{\bf{i}} - \sin(\theta_A)\hat{\bf{j}}$

Having the kinematics computed, we can go back to the moment and derivative of the angular momentum:

\begin{equation}
     \vec{\bf{M_A}} =  \vec{\bf{T_A}} + h_G\hat{\bf{e_r}} \times (-m_Bg\hat{\bf{j}}) = T_A\hat{\bf{k}} + h_Gm_Bg\sin(\theta_A)\hat{\bf{k}}
\end{equation}

\begin{equation}
    \frac{d\vec{\bf{H_A}}}{dt} = I_{zz}^{cm}\frac{d^2\theta_A}{dt^2} \hat{\bf{k}} + mh_G\hat{\bf{e_r}} \times \left(-h_G\left(\frac{d^2\theta_A}{dt^2}\right)^2\hat{\bf{e_r}} + h_G\frac{d^2\theta_A}{dt^2}\hat{\bf{e_\theta}}\right) = I_{zz}^{cm}\frac{d^2\theta_A}{dt^2}\hat{\bf{k}} + mh_G^2\frac{d^2\theta_A}{dt^2}\hat{\bf{k}} = \left(I_{zz}^{cm} + mh_G^2\right)\frac{d^2\theta_A}{dt^2}\hat{\bf{k}}
\end{equation}

By using the Newton-Euler equations, we can now find the equation of motion of the body during quiet standing:

\begin{equation}
    \vec{\bf{M_A}}=\frac{d\vec{\bf{H_A}}}{dt} \rightarrow  \left(I_{zz}^{cm} + mh_G^2\right)\frac{d^2\theta_A}{dt^2} =  T_A + h_Gm_B g\sin(\theta_A) \rightarrow \frac{d^2\theta_A}{dt^2} = \frac{h_Gm_B g}{I_{zz}^{cm} + mh_G^2}\sin(\theta_A)+ \frac{T_A}{I_{zz}^{cm} + m_Bh_G^2}
\end{equation}

If we consider the body as a rigid uniform bar, the moment of inertia is $I_{zz}^{cm} = \frac{m_Bh_G^2}{3}$. So, the equation of motion of the body is:

\begin{equation}
    \frac{d^2\theta_A}{dt^2} = \frac{3 g}{4h_g}\sin(\theta_A)+ \frac{3 T_A}{4m_bh_G^2}
\end{equation}

## 6) Force platform

From the same example above, now we will find the position of the center of pressure during the quiet standing. The center of pressure is the point where the ground reaction force is being applied. The point O is linked to the ground in a way that it constraints the translation and rotation movement of the platform. Also, it is in the point O that sensors of force and moments are located.

The free-body diagram of the foot and the force platform is depicted below.

In the platform , the distance $y_0$ is known. As the platform is in equilibrium its derivative of the angular momentum is zero. Now, we must find the sum of the moments. As we can choose any point, we will choose, the point where the force $\vec{\bf{F}}$ is being applied, and equal it to zero.

\begin{equation}
    \vec{\bf{M}} + \vec{\bf{r_{COP/O}}} \times (-\vec{\bf{GRF}}) = 0
\end{equation}

The vector $\vec{\bf{r_{COP/O}}}$ is:

\begin{equation}
    \vec{\bf{r_{COP/O}}} = COP_x \hat{\bf{i}} + y_0 \hat{\bf{j}}
\end{equation}

So, from the equation of the sum of the moments we can isolate the postion of the center of pressure:

\begin{equation}
    M - COP_x GRF_y + y_0 GRF_x = 0 \rightarrow COP_x = \frac{M+y_0 GRF_x}{GRF_y}
\end{equation}

Using the expression above, we can track the position of the center of pressure during an experiment.

## 7) Segway

The horizontal acceleration of the point A $a_A$ of the segway is known. There is a motor at point A that generates a moment $\vec{\bf{T}}$ at the bar.

The free-body diagrams of the upper part of the segway and the cart are depicted below. At the upper part are applied the moment $\vec{\bf{T}}$ due to the motor, the gravitational force at the center of mass of the bar and the constraint force at the hinge at the point A. At the cart are applied the ground reaction force, the reaction of $\vec{\bf{F_1}}$ at the hinge and the reaction of the moment due to the motor. As the cart has an acceleration, there is a force $\vec{\bf{F}}$ to produce an acceleration.

The equation of motion of the bar can be obtained by using the Newton-Euler equations.

Considering the  bar, the moment around the point A is:

\begin{equation}
    \vec{\bf{M_A}} = \vec{\bf{r_{cm/A}}} \times (-mg\hat{\bf{j}}) + \vec{\bf{T}}
\end{equation}

And the derivative of the angular momentum around the point A is:

\begin{equation}
        \frac{d\vec{H_A}}{dt} = I_{zz}^{cm}\frac{d^2\theta}{dt^2}+m\vec{\bf{r_{cm/A}}}\times \vec{\bf{a_{cm}}}
\end{equation}

The vector from the point A to the center of mass is:

\begin{equation}
    \vec{\bf{r_{cm/A}}} = \frac{l}{2}\hat{\bf{e_R}}
\end{equation}

and the acceleration of the center of mass is obtained by deriving the position of the center of mass twice:

\begin{equation}
    \vec{\bf{r_{cm}}} = \vec{\bf{r_{A}}} + \vec{\bf{r_{cm/A}}} = \vec{\bf{r_{A}}} + \frac{l}{2}\hat{\bf{e_R}} \rightarrow  \vec{\bf{v_{cm}}} = \vec{\bf{v_{A}}} + \frac{l}{2}\frac{d\theta}{dt}\hat{\bf{e_\theta}} \rightarrow  \vec{\bf{a_{cm}}} = \vec{\bf{a_{A}}} - \frac{l}{2}\left(\frac{d\theta}{dt}\right)^2\hat{\bf{e_R}}+\frac{l}{2}\frac{d^2\theta}{dt^2}\hat{\bf{e_\theta}}
\end{equation}

where $\hat{\bf{e_R}} = -\sin(\theta)\hat{\bf{i}}+\cos(\theta)\hat{\bf{j}}$ and $\hat{\bf{e_\theta}} = -\cos(\theta)\hat{\bf{i}}-\sin(\theta)\hat{\bf{j}}$.

Using the Newton-Euler laws, we can find the equation of motion of the upper part of the segway:

\begin{equation}
    \frac{d\vec{H_A}}{dt} = \vec{\bf{M_A}} \rightarrow  I_{zz}^{cm}\frac{d^2\theta}{dt^2}\hat{\bf{k}}+m\vec{\bf{r_{cm/A}}}\times \vec{\bf{a_{cm}}} =  \vec{\bf{r_{cm/A}}} \times (-mg\hat{\bf{j}}) + \vec{\bf{T}} \rightarrow I_{zz}^{cm}\frac{d^2\theta}{dt^2}+m\frac{l}{2}\hat{\bf{e_R}}\times\left(a_{A}\hat{\bf{i}} - \frac{l}{2}\left(\frac{d\theta}{dt}\right)^2\hat{\bf{e_R}}+\frac{l}{2}\frac{d^2\theta}{dt^2}\hat{\bf{e_\theta}}\right) = ( \frac{l}{2}\hat{\bf{e_R}} \times (-mg\hat{\bf{j}}) + \vec{\bf{T}}) \rightarrow I_{zz}^{cm}\frac{d^2\theta}{dt^2}\hat{\bf{k}}-\frac{ml}{2}a_{A}\cos(\theta)\hat{\bf{k}} +\frac{ml^2}{4}\frac{d^2\theta}{dt^2}\hat{\bf{k}} = \frac{mgl}{2}\sin(\theta)\hat{\bf{k}} + T\hat{\bf{k}}
\end{equation}

So, the equation of motion of the upper part of the segway is:

\begin{equation}
    \frac{d^2\theta}{dt^2} = \frac{mgl}{2\left(I_{zz}^{cm}+\frac{ml^2}{4}\right)}\sin(\theta) + \frac{ml}{2\left(I_{zz}^{cm}+\frac{ml^2}{4}\right)}a_{A}\cos(\theta)+ \frac{T}{\left(I_{zz}^{cm}+\frac{ml^2}{4}\right)}
\end{equation}

Considering the moment of inertia of the bar is $I_{zz}^{cm}=\frac{ml^2}{12}$, the equation of motion of the bar is:

\begin{equation}
     \frac{d^2\theta}{dt^2} = \frac{3g}{2l}\sin(\theta) + \frac{3ml}{2ml^2}a_{A}\cos(\theta)+ \frac{3T}{2ml^2}
\end{equation}

## 8) Double pendulum with actuators

The figure below shows a  double pendulum. This model can represent, for example, the arm and forearm of a subject.

The free-body diagrams of both bars are depicted below. There are constraint  forces at all joints and moments from the muscles from both joints.

The versors $\hat{\bf{e_{R_1}}}$, $\hat{\bf{e_{R_2}}}$, $\hat{\bf{e_{\theta_1}}}$ and $\hat{\bf{e_{\theta_2}}}$ are defined as:

\begin{equation}
    \hat{\bf{e_{R_1}}} = \sin(\theta_1) \hat{\bf{i}} - \cos(\theta_1) \hat{\bf{j}}
\end{equation}

\begin{equation}
    \hat{\bf{e_{\theta_1}}} = \cos(\theta_1) \hat{\bf{i}} + \sin(\theta_1) \hat{\bf{j}}
\end{equation}

\begin{equation}
    \hat{\bf{e_{R_2}}} = \sin(\theta_2) \hat{\bf{i}} - \cos(\theta_2) \hat{\bf{j}}
\end{equation}

\begin{equation}
    \hat{\bf{e_{\theta_2}}} = \cos(\theta_2) \hat{\bf{i}} + \sin(\theta_2) \hat{\bf{j}}
\end{equation}

First, we can analyze the sum of the moments and the derivative of the angular momentum at the upper bar. They will be computed relative to the point O1.

\begin{equation}
    \vec{\bf{M_{O1}}} = \vec{\bf{r_{C1/O1}}} \times (-m_1g\hat{\bf{j}}) + \vec{\bf{r_{O2/O1}}} \times (-\vec{\bf{F_{12}}}) + \vec{\bf{M_{1}}} - \vec{\bf{M_{12}}} = \frac{l_1}{2}  \hat{\bf{e_{R_1}}}  \times (-m_1g\hat{\bf{j}}) + l_1\hat{\bf{e_{R_1}}} \times (-\vec{\bf{F_{12}}}) + \vec{\bf{M_{1}}} - \vec{\bf{M_{12}}} = \frac{-m_1gl_1}{2}\sin(\theta_1)\hat{\bf{k}} + l_1\hat{\bf{e_{R_1}}} \times (-\vec{\bf{F_{12}}}) + \vec{\bf{M_{1}}} - \vec{\bf{M_{12}}}
\end{equation}

\begin{equation}
    \frac{d\vec{\bf{H_{O1}}}}{dt} = I_{zz}^{C1} \frac{d^2\theta_1}{dt^2} + m_1 \vec{\bf{r_{C1/O1}}} \times \vec{\bf{a_{C1}}} = I_{zz}^{C1} \frac{d^2\theta_1}{dt^2} + m_1 \frac{l_1}{2}  \hat{\bf{e_{R_1}}} \times \left[-\frac{l_1}{2}\left(\frac{d\theta_1}{dt}\right)^2 \hat{\bf{e_{R_1}}} + \frac{l_1}{2}\frac{d^2\theta_1}{dt^2} \hat{\bf{e_{\theta_1}}}\right] = \left( I_{zz}^{C1}  + m_1 \frac{l_1^2}{4}\right)\frac{d^2\theta_1}{dt^2}
\end{equation}

Note that the force $\vec{\bf{F_{12}}}$ is unknown. We will take care of this later.

Now, we analyze the the sum of the moments and the derivative of the angular momentum at the lower bar relative to the point O2.

\begin{equation}
    \vec{\bf{M_{O2}}} = \vec{\bf{r_{C2/O2}}} \times (-m_2g\hat{\bf{j}}) + \vec{\bf{M_{12}}} =  \frac{l_2}{2}   \hat{\bf{e_{R_2}}} \times (-m_2g\hat{\bf{j}}) + \vec{\bf{M_{12}}} = -\frac{m_2gl_2}{2}\sin(\theta_2)\hat{\bf{k}} + \vec{\bf{M_{12}}}
\end{equation}

\begin{equation}
    \frac{d\vec{\bf{H_{O2}}}}{dt} = I_{zz}^{C2} \frac{d^2\theta_2}{dt^2} + m_2 \vec{\bf{r_{C2/O2}}} \times \vec{\bf{a_{C2}}} = I_{zz}^{C2} \frac{d^2\theta_2}{dt^2} + m_2 \frac{l_2}{2}  \hat{\bf{e_{R_2}}} \times \left[-l_1\left(\frac{d\theta_1}{dt}\right)^2 \hat{\bf{e_{R_1}}} + l_1\frac{d^2\theta_1}{dt^2} \hat{\bf{e_{\theta_1}}} -\frac{l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \hat{\bf{e_{R_2}}} + \frac{l_2}{2}\frac{d^2\theta_2}{dt^2} \hat{\bf{e_{\theta_2}}}\right]
\end{equation}

To complete the computation of the derivative of the angular momentum we must find the products $\hat{\bf{e_{R_2}}} \times \hat{\bf{e_{R_1}}} $,  $\hat{\bf{e_{R_2}}} \times \hat{\bf{e_{\theta_1}}}$ and $\hat{\bf{e_{R_1}}} \times \hat{\bf{e_{\theta_2}}}$:

\begin{equation}
    \hat{\bf{e_{R_2}}} \times \hat{\bf{e_{\theta_1}}} = \left[\begin{array}{c}\sin(\theta_2)\\-\cos(\theta_2)\end{array}\right] \times \left[\begin{array}{c}\cos(\theta_1)\\\sin(\theta_1)\end{array}\right] = \sin(\theta_1)\sin(\theta_2)+\cos(\theta_1)\cos(\theta_2) = \cos(\theta_1-\theta_2)
\end{equation}

\begin{equation}
    \hat{\bf{e_{R_2}}} \times \hat{\bf{e_{R_1}}} = \left[\begin{array}{c}\sin(\theta_2)\\-\cos(\theta_2)\end{array}\right] \times \left[\begin{array}{c}\sin(\theta_1)\\-\cos(\theta_1)\end{array}\right] = -\sin(\theta_2)\cos(\theta_1)+\cos(\theta_2)\sin(\theta_1) = \sin(\theta_1-\theta_2)
\end{equation}

\begin{equation}
    \hat{\bf{e_{R_1}}} \times \hat{\bf{e_{\theta_2}}} =\left[\begin{array}{c}\sin(\theta_1)\\-\cos(\theta_1)\end{array}\right] \times \left[\begin{array}{c}\cos(\theta_2)\\\sin(\theta_2)\end{array}\right] = \sin(\theta_2)\sin(\theta_1)+\cos(\theta_2)\cos(\theta_1) = \cos(\theta_1-\theta_2)
\end{equation}

So, the derivative of the angular momentum is:

\begin{align}
\begin{split}
    \frac{d\vec{\bf{H_{O2}}}}{dt} &=  I_{zz}^{C2} \frac{d^2\theta_2}{dt^2} - \frac{m_2l_1l_2}{2}\left(\frac{d\theta_1}{dt}\right)^2 \sin(\theta_1-\theta_2) + \frac{m_2l_1l_2}{2}\frac{d^2\theta_1}{dt^2} \cos(\theta_1-\theta_2) + \frac{m_2l_2^2}{4}\frac{d^2\theta_2}{dt^2} \\
    &=  \frac{m_2l_1l_2}{2}\cos(\theta_1-\theta_2)\frac{d^2\theta_1}{dt^2} + \left(I_{zz}^{C2} + \frac{m_2l_2^2}{4} \right)\frac{d^2\theta_2}{dt^2}- \frac{m_2l_1l_2}{2}\left(\frac{d\theta_1}{dt}\right)^2 \sin(\theta_1-\theta_2)
    \end{split}
\end{align}

It remains to find the force $\vec{\bf{F_{12}}}$ that is in the sum of moment of the upper bar. It can be found by finding the resultant force in the lower bar and use the First Newton-Euler law:

\begin{equation}
    \vec{\bf{F_{12}}} - m_2g \hat{\bf{j}} = m\vec{\bf{a_{C2}}} \rightarrow \vec{\bf{F_{12}}}  =  m_2g \hat{\bf{j}}  + m_2\left[-l_1\left(\frac{d\theta_1}{dt}\right)^2 \hat{\bf{e_{R_1}}} + l_1\frac{d^2\theta_1}{dt^2} \hat{\bf{e_{\theta_1}}} -\frac{l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \hat{\bf{e_{R_2}}} + \frac{l_2}{2}\frac{d^2\theta_2}{dt^2} \hat{\bf{e_{\theta_2}}}\right]
\end{equation}

Now, we can go back to the moment $\vec{\bf{M_{O1}}}$:

\begin{align}
    \begin{split}
    \vec{\bf{M_{O1}}} &= \frac{-m_1gl_1}{2}\sin(\theta_1)\hat{\bf{k}} + l_1\hat{\bf{e_{R_1}}} \times (-\vec{\bf{F_{12}}}) + \vec{\bf{M_{1}}} - \vec{\bf{M_{12}}} \\
    &= -\frac{m_1gl_1}{2}\sin(\theta_1)\hat{\bf{k}} - l_1\hat{\bf{e_{R_1}}} \times   m_2\left[g \hat{\bf{j}}-l_1\left(\frac{d\theta_1}{dt}\right)^2 \hat{\bf{e_{R_1}}} + l_1\frac{d^2\theta_1}{dt^2} \hat{\bf{e_{\theta_1}}} -\frac{l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \hat{\bf{e_{R_2}}} + \frac{l_2}{2}\frac{d^2\theta_2}{dt^2} \hat{\bf{e_{\theta_2}}}\right]  + \vec{\bf{M_{1}}} - \vec{\bf{M_{12}}} \\
    &= -\frac{m_1gl_1}{2}\sin(\theta_1)\hat{\bf{k}} - m_2l_1g \sin(\theta_1)\hat{\bf{k}}  - m_2l_1^2\frac{d^2\theta_1}{dt^2}\hat{\bf{k}}  + \frac{m_2l_1l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \sin(\theta_1-\theta_2)\hat{\bf{k}}  - \frac{m_2l_1l_2}{2}\frac{d^2\theta_2}{dt^2} \cos(\theta_1-\theta_2)\hat{\bf{k}}  + M_1\hat{\bf{k}}  - M_{12}\hat{\bf{k}}
    \end{split}
\end{align}

Finally, using the second Newton-Euler law for both bars, we can find the equation of motion of both angles:

\begin{equation}
    \frac{d\vec{\bf{H_{O1}}}}{dt} = \vec{\bf{M_{O1}}} \rightarrow  \left( I_{zz}^{C1}  + m_1 \frac{l_1^2}{4}\right)\frac{d^2\theta_1}{dt^2} =  -\frac{m_1gl_1}{2}\sin(\theta_1)- m_2l_1g \sin(\theta_1)  - m_2l_1^2\frac{d^2\theta_1}{dt^2}  + \frac{m_2l_1l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \sin(\theta_1-\theta_2)  - \frac{m_2l_1l_2}{2}\frac{d^2\theta_2}{dt^2} \cos{(\theta_1-\theta_2)}  + M_1 - M_{12}
\end{equation}

\begin{equation}
    \frac{d\vec{\bf{H_{O2}}}}{dt} = \vec{\bf{M_{O2}}} \rightarrow \frac{m_2l_1l_2}{2}\cos(\theta_1-\theta_2)\frac{d^2\theta_1}{dt^2} + \left(I_{zz}^{C2} + \frac{m_2l_2^2}{4} \right)\frac{d^2\theta_2}{dt^2}- \frac{m_2l_1l_2}{2}\left(\frac{d\theta_1}{dt}\right)^2 \sin(\theta_1-\theta_2) = -\frac{m_2gl_2}{2}\sin(\theta_2) + M_{12}
\end{equation}

Considering the moment of inertia of the bars as $I_{zz}^{C1}=\frac{m_1l_1^2}{12}$ and $I_{zz}^{C2}=\frac{m_2l_2^2}{12}$ the differential equations above are:

\begin{equation}
     \left(\frac{m_1l_1^2}{3} +m_2l_1^2\right)\frac{d^2\theta_1}{dt^2} + \frac{m_2l_1l_2}{2} \cos{(\theta_1-\theta_2)\frac{d^2\theta_2}{dt^2}}  =  -\frac{m_1gl_1}{2}\sin(\theta_1)- m_2l_1g \sin(\theta_1)   + \frac{m_2l_1l_2}{2}\left(\frac{d\theta_2}{dt}\right)^2 \sin(\theta_1-\theta_2)  + M_1 - M_{12}
\end{equation}

\begin{equation}
    \frac{m_2l_1l_2}{2}\cos(\theta_1-\theta_2)\frac{d^2\theta_1}{dt^2} + \frac{m_2l_2^2}{3}\frac{d^2\theta_2}{dt^2} = -\frac{m_2gl_2}{2}\sin(\theta_2) + \frac{m_2l_1l_2}{2}\left(\frac{d\theta_1}{dt}\right)^2 \sin(\theta_1-\theta_2)+ M_{12}
\end{equation}

We could isolate the angular accelerations but this would lead to long equations.

### Problems

- Solve the problems 6, 7 and 8 from [this Notebook](FreeBodyDiagram.ipynb).

- Solve the problems 16.2.9, 18.3.26, 18.3.29 from Ruina and Pratap book.

### References

- Ruina A., Rudra P. (2015) [Introduction to Statics and Dynamics](http://ruina.tam.cornell.edu/Book/index.html). Oxford University Press.

- Duarte M. (2017) [Free body diagram](FreeBodyDiagram.ipynb)

- Elias, L A, Watanabe, R N, Kohn, A F.(2014) [Spinal Mechanisms May Provide a Combination of Intermittent and Continuous Control of Human Posture: Predictions from a Biologically Based Neuromusculoskeletal Model.](http://dx.doi.org/10.1371/journal.pcbi.1003944) PLOS Computational Biology (Online)

- Winter D. A., (2009) Biomechanics and motor control of human movement. John Wiley and Sons.

- Duarte M. (2017) [The inverted pendulum model of the human standing](http://nbviewer.jupyter.org/github/demotu/BMC/blob/master/notebooks/IP_Model.ipynb)