## Dependencies and Setup

### Install Process Mining for Python

```python
!pip install pm4py
!pip install jellyfish

```

    Collecting pm4py
      Downloading pm4py-2.2.11.1-py3-none-any.whl (1.5 MB)
         |████████████████████████████████| 1.5 MB 8.1 MB/s
    Collecting pulp<=2.1
      Downloading PuLP-2.1-py3-none-any.whl (40.6 MB)
         |████████████████████████████████| 40.6 MB 70 kB/s
    Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from pm4py) (4.41.1)
    Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from pm4py) (3.2.2)
    Requirement already satisfied: cvxopt in /usr/local/lib/python3.7/dist-packages (from pm4py) (1.2.6)
    Requirement already satisfied: graphviz in /usr/local/lib/python3.7/dist-packages (from pm4py) (0.10.1)
    Requirement already satisfied: numpy!=1.19.4 in /usr/local/lib/python3.7/dist-packages (from pm4py) (1.19.5)
    Requirement already satisfied: pytz in /usr/local/lib/python3.7/dist-packages (from pm4py) (2018.9)
    Collecting deprecation
      Downloading deprecation-2.1.0-py2.py3-none-any.whl (11 kB)
    Requirement already satisfied: sympy in /usr/local/lib/python3.7/dist-packages (from pm4py) (1.7.1)
    Requirement already satisfied: scikit-learn in /usr/local/lib/python3.7/dist-packages (from pm4py) (0.22.2.post1)
    Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (from pm4py) (1.4.1)
    Collecting jsonpickle
      Downloading jsonpickle-2.0.0-py2.py3-none-any.whl (37 kB)
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from pm4py) (1.1.5)
    Requirement already satisfied: networkx in /usr/local/lib/python3.7/dist-packages (from pm4py) (2.5.1)
    Requirement already satisfied: intervaltree in /usr/local/lib/python3.7/dist-packages (from pm4py) (2.1.0)
    Requirement already satisfied: pydotplus in /usr/local/lib/python3.7/dist-packages (from pm4py) (2.0.2)
    Collecting stringdist
      Downloading StringDist-1.0.9.tar.gz (7.4 kB)
    Requirement already satisfied: lxml in /usr/local/lib/python3.7/dist-packages (from pm4py) (4.2.6)
    Collecting pyvis
      Downloading pyvis-0.1.9-py3-none-any.whl (23 kB)
    Requirement already satisfied: pyparsing>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from pulp<=2.1->pm4py) (2.4.7)
    Requirement already satisfied: packaging in /usr/local/lib/python3.7/dist-packages (from deprecation->pm4py) (21.0)
    Requirement already satisfied: sortedcontainers in /usr/local/lib/python3.7/dist-packages (from intervaltree->pm4py) (2.4.0)
    Requirement already satisfied: importlib-metadata in /usr/local/lib/python3.7/dist-packages (from jsonpickle->pm4py) (4.6.1)
    Requirement already satisfied: typing-extensions>=3.6.4 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata->jsonpickle->pm4py) (3.7.4.3)
    Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata->jsonpickle->pm4py) (3.5.0)
    Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->pm4py) (0.10.0)
    Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->pm4py) (2.8.1)
    Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->pm4py) (1.3.1)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from cycler>=0.10->matplotlib->pm4py) (1.15.0)
    Requirement already satisfied: decorator<5,>=4.3 in /usr/local/lib/python3.7/dist-packages (from networkx->pm4py) (4.4.2)
    Requirement already satisfied: ipython>=5.3.0 in /usr/local/lib/python3.7/dist-packages (from pyvis->pm4py) (5.5.0)
    Requirement already satisfied: jinja2>=2.9.6 in /usr/local/lib/python3.7/dist-packages (from pyvis->pm4py) (2.11.3)
    Requirement already satisfied: pickleshare in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (0.7.5)
    Requirement already satisfied: pexpect in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (4.8.0)
    Requirement already satisfied: prompt-toolkit<2.0.0,>=1.0.4 in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (1.0.18)
    Requirement already satisfied: pygments in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (2.6.1)
    Requirement already satisfied: simplegeneric>0.8 in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (0.8.1)
    Requirement already satisfied: setuptools>=18.5 in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (57.2.0)
    Requirement already satisfied: traitlets>=4.2 in /usr/local/lib/python3.7/dist-packages (from ipython>=5.3.0->pyvis->pm4py) (5.0.5)
    Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.7/dist-packages (from jinja2>=2.9.6->pyvis->pm4py) (2.0.1)
    Requirement already satisfied: wcwidth in /usr/local/lib/python3.7/dist-packages (from prompt-toolkit<2.0.0,>=1.0.4->ipython>=5.3.0->pyvis->pm4py) (0.2.5)
    Requirement already satisfied: ipython-genutils in /usr/local/lib/python3.7/dist-packages (from traitlets>=4.2->ipython>=5.3.0->pyvis->pm4py) (0.2.0)
    Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.7/dist-packages (from pexpect->ipython>=5.3.0->pyvis->pm4py) (0.7.0)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->pm4py) (1.0.1)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy->pm4py) (1.2.1)
    Building wheels for collected packages: stringdist
      Building wheel for stringdist (setup.py) ... done
      Created wheel for stringdist: filename=StringDist-1.0.9-cp37-cp37m-linux_x86_64.whl size=23588 sha256=96514fa72d030bf1db0a6608559ecc4d6774c9e9d123796c00bf659c5e798815
      Stored in directory: /root/.cache/pip/wheels/d7/9c/d4/63bc3d8931de0980b9e4a724dea290bb40b8b1b2bd6227c8da
    Successfully built stringdist
    Installing collected packages: jsonpickle, stringdist, pyvis, pulp, deprecation, pm4py
    Successfully installed deprecation-2.1.0 jsonpickle-2.0.0 pm4py-2.2.11.1 pulp-2.1 pyvis-0.1.9 stringdist-1.0.9
    Collecting jellyfish
      Downloading jellyfish-0.8.4-py3-none-any.whl (10 kB)
    Installing collected packages: jellyfish
    Successfully installed jellyfish-0.8.4

```python
from pprint import pprint
import pm4py
import copy
import numpy
import jellyfish
import math
```

### Import dataset and save as event log

```python

from google.colab import drive
drive.mount('/content/drive')
log_file = '/content/drive/MyDrive/pathToXesLogFile'
log = pm4py.read_xes(log_file)
```

    Mounted at /content/drive

    HBox(children=(FloatProgress(value=0.0, description='parsing log, completed traces :: ', max=150370.0, style=P…

## Preprocessing

### Search for string attributes and save the according max string lenght for every attribute

```python
# All string keys

seen_keys = []

for t in log:
    for i in range(len(t)):
        event = t[i]
        keys = event.keys()
        for key in keys:
            if (type(event[key]) == str):
                seen_keys.append(key)
                seen_keys = list(dict.fromkeys(seen_keys))

string_keys = dict.fromkeys(seen_keys)

keys2 = string_keys.keys()
for key in keys2:
    string_keys[key] = 0

#max string_keys
for t in log:
    for i in range(len(t)):
        for key in keys2:
            if key in t[i]:
                if len(t[i][key]) > string_keys[key]:
                    string_keys[key] = len(t[i][key])
```

```python
for t in log:
  for i in range(len(t)):
    event = t[i]
    keys = event.keys()
    for key in keys:
      if (type(event[key]) == str):
        seen_keys.append(key)
        seen_keys = list(dict.fromkeys(seen_keys))
```

### Search for all numeric attributes and save the attribute names

```python

# All numeric keys

seen_keys = []

for t in log:
  for i in range(len(t)):
    event = t[i]
    keys = event.keys()
    for key in keys:
      if (type(event[key]) == float or type(event[key]) == int):
        seen_keys.append(key)
        seen_keys = list(dict.fromkeys(seen_keys))

seen_keys = dict.fromkeys(seen_keys)
```

### Calculate the mean values of all numeric attributes

```python
# mean values
sum = 0

seen_keys = []

for t in log:
  for i in range(len(t)):
    event = t[i]
    keys = event.keys()
    for key in keys:
      if (type(event[key]) == float or type(event[key]) == int):
        seen_keys.append(key)
        seen_keys = list(dict.fromkeys(seen_keys))

mean_keys = dict.fromkeys(seen_keys)
keys2 = mean_keys.keys()
for key in keys2:
  mean_keys[key] = 0

counter_keys = copy.deepcopy(mean_keys)

for t in log:
  for i in range(len(t)):
    for key in keys2:
      if key in t[i]  :
        mean_keys[key] += t[i][key]
        counter_keys[key] += 1

for key in keys2:
  mean_keys[key] = mean_keys[key] / counter_keys[key]
  print(mean_keys[key])
```

    63.6816560395868
    17.815603519745885
    113.2466582430006
    0.0788787657112456
    11.948984392276309
    46.41002731923214
    0.0

### Calculate the std. deviation of all numeric values

```python

# std deviation of values

deviation_keys = dict.fromkeys(seen_keys)
keys2 = deviation_keys.keys()
for key in keys2:
  deviation_keys[key] = []

for t in log:
  for i in range(len(t)):
    for key in keys2:
      if key in t[i]:
        deviation_keys[key].append(t[i][key])

for key in keys2:
  deviation_keys[key] = numpy.std(deviation_keys[key])
```

    89.67779636777881
    36.77930126804085
    68.21924855798667
    0.5776847046687658
    4.023171014461607
    39.345307223637775
    0.0

### Apply the z score normalisation to the log

```python
# z score of values

numeric_keys = dict.fromkeys(seen_keys)

z_log = copy.deepcopy(log)

for t in z_log:
  for i in range(len(t)):
    for key in numeric_keys.keys():
      if key in t[i] and deviation_keys[key] != 0:
        t[i][key] = (t[i][key] - mean_keys[key] ) / deviation_keys[key]
```

### Calculate the minimum and maximum timestamp per trace

```python
# timestamp min max values per trace

min_timestamps = []

max_timestamps = []

for t in log:
  temp_min_timestamp = t[0]['time:timestamp']
  temp_max_timestamp = t[0]['time:timestamp']
  for i in range(len(t)):
    if temp_min_timestamp > t[i]['time:timestamp']:
      temp_min_timestamp = t[i]['time:timestamp']
    if temp_max_timestamp < t[i]['time:timestamp']:
      temp_max_timestamp = t[i]['time:timestamp']
  min_timestamps.append(temp_min_timestamp)
  max_timestamps.append(temp_max_timestamp)
```

### Apply min-max normalisation on timestamps

```python
# normalize timestamps

normalised_log = copy.deepcopy(z_log)

for y in range (len(log)):
  for i in range (len(log[y])):
    if max_timestamps[y] == min_timestamps[y]:
      normalised_time = 0
    else:
      normalised_time = (log[y][i]['time:timestamp'] - min_timestamps[y]) / (max_timestamps[y] - min_timestamps[y])
    normalised_log[y][i]['time:timestamp'] = normalised_time
```

### Define distance algorithm

```python
# distance algorithm

def similarity(dict1, dict2, treshhold, weights):
    longest_value = 0
    distance_score = 0
    distance_sum = 0
    d1_keys = (dict1.keys())
    d2_keys = (dict2.keys())

    # Combine the keys from both events to calculate the vector distance between both of them
    both_keys = list(dict.fromkeys(dict1))
    for key in d2_keys:
      if not (key in both_keys):
        both_keys.append(key)

    both_keys = dict.fromkeys(both_keys)
    for key in both_keys:
      ## Timestamp distance
      if (key == 'time:timestamp'):
        diff = (dict1[key] - dict2[key])
        both_keys[key] = pow(diff, 2)

      # If no value is present for the key set a default value, otherwise take value from event

      # Check if value is numeric
      elif key in numeric_keys:
        if key not in dict1:
          d1_score = 0 # is 0 the best solution here?
        else:
          d1_score = dict1[key]
        if key not in dict2:
          d2_score = 0 # is 0 the best solution here?
        else:
          d2_score = dict2[key]

        diff = d1_score - d2_score
        both_keys[key] = pow(diff, 2)
        if(key == 'totalPaymentAmount'):
          both_keys[key] = both_keys[key]

      # Otherwise value must be a string
      else:
        if key not in dict1:
          d1_string = '' # is '' the best solution here?
        else:
          d1_string = dict1[key]
        if key not in dict2:
          d2_string = '' # is '' the best solution here?
        else:
          d2_string = dict2[key]

        # calculate levenstein distance
        lev_dist = jellyfish.levenshtein_distance(d1_string, d2_string)
        # execute normalization
        distance_percentage = lev_dist / string_keys[key]
        both_keys[key] = pow(distance_percentage, 2)

    # include weights and calculate euclidean distance
    for key in both_keys:
      distance_sum +=  weights[key] * both_keys[key]

    distance_score = math.sqrt(distance_sum)

    # If distanz is under treshhold return true
    if distance_score < treshhold:
      #print(distance_score)
      return True
    else:
      return False
```

### Create weight dictionary

```python
seen_keys = []

for t in log:
  for i in range(len(t)):
    event = t[i]
    keys = event.keys()
    for key in keys:
        seen_keys.append(key)
        seen_keys = list(dict.fromkeys(seen_keys))

weights = dict.fromkeys(seen_keys)
for key in seen_keys:
    weights[key] = 1
```

    ['amount', 'org:resource', 'dismissal', 'concept:name', 'vehicleClass', 'totalPaymentAmount', 'lifecycle:transition', 'time:timestamp', 'article', 'points', 'expense', 'notificationType', 'lastSent', 'paymentAmount', 'matricola']

## Example execution with weights and treshhold of 0.1

```python
weights1 = copy.deepcopy(weights)
#f.i. weights1['attribute']=10

treshhold = 0.1

for x in range(len(normalised_log)):
  t = normalised_log[x]
  for i in range(len(t)):
    event_outer = t[i]
    for y in range(i+1, len(t)):
      event_inner = t[y]
      flag = similarity(event_outer, event_inner, treshhold, weights1)
      if flag == True:
        print(t)

```


```python
import pandas as pd
import ms3
from  ms3.utils import *
import os
from ms3 import Score
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# include directory
home_dir = '/home/nulpe/Desktop/Tresillo/'
```

# Towards a Rhythmical Definition of the Tressilio beat and a Tracing of it in Popular Music

## 1) Introduction & Research Question

### 1.1) Research question:
"Can we compute to which extent a defined rhythm, which we refer to as Tresillo rhythm, is used in a given pop song and if so can we measure the intensity of Tresillo rhythm use in billboard songs over the past twenty years?"

**Discussion:**
In our project we would like to discuss the use of a rhythm, which we refer to as 'Tresillo rhythm', in the popular music of the last 20 years. We define this rhythm in our project, given secondary literature, and thus obtain a precise notation and formulization of the Tresillo rhythm. Given this definition we can then compute the similarity between the Tresillo rhythm and the rhythm of a given pop song. Thus, we hope to obtain a similarity coefficient which measures how similar the rythm of a given pop song is to our self defined Tresillo rythm. Given the computed similarity coefficients, we hope to measure the use of the Tresillo rhythm in the top 20 billboard songs of the past 20 years (1999-2019).

### 1.2) Assumptions

### 1.3) Data Representation
Initially our data is represented in the MIDI file format. The representation of music in the MIDI format has the advantage, that often several voices of different instruments are represented in such files. In contrast to musescore, where often only the voice of one instrument (mostly piano) is notated.
However, to obtain a list of onsets of every musical event in a given song, we have to convert our MIDI (.midi) files to Musescore (.mscx) files.
To convert and further analyze our files, we will use the [ms3](https://pypi.org/project/ms3/) python library.
To convert a directory of .midi files to .mscx files we use following command:

```python
path_midi = '/home/nulpe/Desktop/Tresillo/dataset/project_midi/tresillo/'
target = '/home/nulpe/Desktop/Tresillo/dataset/project_mscx/mscx_tresillos_billboard/'

dir_list = os.listdir(path_midi)

for el in dir_list:
    convert(path_midi+el, target+el[:-4]+'.mscx', MS='musescore3')
```

Given the Musescore representation, we can now obtain a list of all musical events in a given song. Given additional information about bars and timing of the onsets, this data representation comprises all information we need for our analysis. The code below exemplifies how our final data representation looks like:

```python
dir_mscx = '/home/nulpe/Desktop/Tresillo/dataset/project_mscx/mscx_tresillos_billboard/Shape of you-Ed Sheran.mscx'
s = Score(dir_mscx)
df = s.mscx.notes

df.head(5)
```

      mc
      mn
      mc_onset
      mn_onset
      timesig
      staff
      voice
      duration
      nominal_duration
      scalar
      tied
      tpc
      midi
      volta
      chord_id

      0
      1
      1
      0
      0
      4/4
      2
      1
      1/8
      1/8
      1
      <NA>
      7
      61
      <NA>
      6

      1
      1
      1
      0
      0
      4/4
      6
      1
      1/8
      1/8
      1
      <NA>
      7
      61
      <NA>
      12

      2
      1
      1
      0
      0
      4/4
      9
      1
      1/8
      1/8
      1
      <NA>
      7
      61
      <NA>
      18

      3
      1
      1
      0
      0
      4/4
      9
      1
      1/8
      1/8
      1
      <NA>
      4
      64
      <NA>
      18

      4
      1
      1
      0
      0
      4/4
      2
      1
      1/8
      1/8
      1
      <NA>
      8
      68
      <NA>
      6

**TA instructions**: Precision of Research Question

- State the final version of your research question as you understand it now.

-  Make all assumptions and hypotheses explicit.

- In case Milestone 2 did not include the final data representation that you are analyzing, present the representation you finally used and the necessary steps to get to it.

**Thoughts Aurel:**
 - Reasearch Question: Move to a fuzzy definition of Tresillio-ness: E.g.:
    Are pop songs increasingly using a rythm pattern which is similar to a rythm pattern which we reffer to as 'Tresillio rythm'?

 - Assumptions are very important for them. Here we have to note our definition(s) of the Tresillio rythm pattern and how we derive them (incl sources). Furthermore, we have to discuss cases where there are Rythms which are similar to the Tresillio rythm but not equivalent (e.g.: reggaeton) and how we deal with them computationally.

 - Here we have to discuss the conversion of our MIDI files to the musescore3 file format. Furthermore, we have to discuss what the musescore3 format offers us, and why it is the better choice for our analysis.

  **Toughts Florian:**
    -Assumptions:

    -30 second piece of pop song is suitable to identfy the main rythm

    -Main rythm can be identified by counting the onsets

    -the great majority of pop songs comes in 4/4 (not shure if we really need that assumption)

## 2) Methods

### 2.1) Definition of the Tresillo rhythm
ToDo Florian (cite relevant literature to define tresillo)

### 2.2) Rhythm histograms and vectors

To be able to measure the similarity between rhythm we must have a clear definition and thus following representation of rhythm. In general, one can define rhythm as "a series of onsets and durations of musical events.” (Rohrmeier, 2020). In our specific case however, we are interested in the dominat and repeating rhythm of a given song. Therefore we prefer a narrower definition of rhythm as “repeated recurrences in alternate heavy and light beats” (Chin and Wu, 1992). To furthermore simplify our data, we assume that the main rhythm of a song can be defined by the onsets  of its musical events (notes).
To obtain a representation of the dominate rhythm of a song, we preceed to aggregate all musical onset to one bar. Collapsing all musical onsets to one bar and thus obtaining onset 'histograms' is a common pratice and has been used beside others to analyze western classical music (Palmer and Krumhansl, 1990) and american folk music (Huron and Ommen, 2006).
With the onset histogram of a song we can compile a n dimensional vector for each song, which we refer to as a 'rhythm vector'. Given that the number of songs with meters other than 4/4 is negible, we only consider songs with a 4/4 meter in our analysis.  Given that we only consider songs with 4/4 meters, we obtain for each song a 16 dimensional vector.

### 2.3) Naive approach: Rhythm simularity measured with cosine simularity
Given the 16 dimensional rhythm vectors we obtain following the methode described above, we can now compute simple similarity metrics.
In rhythm analysis a common similarity metric which is used to calculate the similarity between two rhythm vectors is the cosine distance (see: anteli et al., 2014; Parry and Essa, 2003).  The cosine similarity metric is scale invariante, which as such is interesting for rhythm similarity given that thus only relative frequencies of onsets are important and not absolute frequencies.
The cosine distance between two vectors A and B is defined as following:
\begin{equation}
\cos ({\bf A},{\bf B})= {{\bf A}*{\bf B} \over \|{\bf A}\| \|{\bf B}\|} = \frac{ \sum_{i=1}^{n}{{\bf A}_i{\bf B}_i} }{ \sqrt{\sum_{i=1}^{n}{({\bf A}_i)^2}} \sqrt{\sum_{i=1}^{n}{({\bf B}_i)^2}} }
\end{equation}

Given the definition of the cosine similarity we can now compute the similarity between our self defined Tresillo rhythm and the billboard songs.
First, however we will validate this similarity metric by testing it on our self compiled list of songs which do comprise a Tresillo rhythm and songs which do not comprise a Tresillo rhythm. We then compute the mean 'Tresillo-ness' (similarity to Tresillo rhythm) of both samples. By employing the Bootstrapping method we can also obtain a measurement of uncertainty, as provided by 2.5% and 97.5% confidence intervals.

### 2.4) Reducing noise: (pushkar)

**TA instructions**:
- How did you deal with the problems you mentioned in Milestone 2?

- Which methods did you use to obtain the final results? Provide self-contained explanations and make sure to cite relevant literature where appropriate.

- Explain your core calculations using equations.

- Do not describe all the methods you tried out but only those that lead to the results: the final analysis is not an exploratory analysis anymore.

- Specify any adjustments you made to pre-existing methods

**Thoughts Aurel**:
 -  Talk about how we got to the bar representation of our music. Furthermore, also discuss how we get to a 'perfect Tresillio Histogram'

 - Here I propose we try out several things and compare the results of several methods:

  a) A first big topics is how we define the perfect Tresillo:
   1. Given predefined rythm patterns (by Florian)
   2. Given songs with high Tresillio-ness
    - All instruments collapsed
    - Only key instruments
    - Certain instruments

  a) The second big question is how do we measure Tresillio-ness in the pop songs, I would suggest three approaches. All aproaches require that we first obtain 16 dimensional Rythm vectors of each song of interest:
   1. Compare our vanilla self defined Tresillio rythm vector with all vectors of our pop songs. Measure distance or simularity with some commonly used distance measure in the literature
   2. Very similar to 1) but this time use the Tresillio rythm vector as defined by our songs
   3. Prior clustering of the rythm vectors. Obtaining centroid and measuring with it Tresillio ness in the charts (method as proposed by Pushkar)

- Equations should be included in the prior part
- Discuss critically any outliers, problems and limitations of our methodology. Extra focus on the question how we deal with related but not the same rythm (e.g.: Reggaeton)

**Toughts Florian**
- Following the last paper discussion one measuurement we could use is the Cosin distance
- Not sure about an instrument selection. We can mention that a reduction seems not favorable as the tresillo rythm  is presented with different instruments throughout the dataset

## 3) Final Results

### 3.1) Onset histograms and rhythm vectors
In this first part we will use onset histograms to compute rhythm vectors.
To obtain the onset histogram of a given song, we use the notes representation provided by the [ms3](https://pypi.org/project/ms3/) libary and colapse all musical onsets to one bar. In the example below we will compile the histograms for our self defined 'Vanilla Tresillo' and for the example song 'shape of you' by Ed Sheran. Then we will proceed to compute the rhythm vectors for both songs

```python
#paths to both examples
shape_of_you = home_dir+'dataset/project_mscx/mscx_tresillos_billboard/Shape of you-Ed Sheran.mscx'
vanilla_tresillo = home_dir+'dataset/project_mscx/mscx_tresillos/Vanilla_Tresillo.mscx'

# get the note scores of both examples
df_shape_of_you = Score(shape_of_you).mscx.notes
df_vanilla_tresillo = Score(vanilla_tresillo).mscx.notes

#calculate quarter note position for each note
df_shape_of_you['quarter_beats'] = (df_shape_of_you.mc_onset*16).astype('int32')
df_vanilla_tresillo['quarter_beats'] = (df_vanilla_tresillo.mc_onset*16).astype('int32')

fig, ax = plt.subplots(1,2, figsize=(12,3))

ax[0].hist(df_shape_of_you['quarter_beats'], bins=16)
ax[1].hist(df_vanilla_tresillo['quarter_beats'], bins=16)
ax[0].set_xlabel('quarter_beats')
ax[1].set_xlabel('quarter_beats')
ax[0].xaxis.set_ticks(np.arange(0, 16, 1))
ax[1].xaxis.set_ticks(np.arange(0, 16, 1))
ax[0].set_ylabel('count')
ax[0].set_title('Shape of you')
ax[1].set_title('Vanilla Tresillo')

#mention somewhere that we are working with quarter notes
```

In a next step we want to compile rhythm of each song given this notion of histograms. E.g: every dimension incorporates the absolute frequency of onsets on one given quarter note. This is done as follows:

```python
rhythm_vector_shape_you = df_shape_of_you.groupby(['quarter_beats'])['mn'].agg(['count'])
rhythm_vector_shape_you  = rhythm_vector_shape_you.reindex(list(range(0,16)),fill_value=0).T
rhythm_vector_shape_you
```

      quarter_beats
      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

      count
      206
      33
      44
      119
      72
      37
      169
      12
      200
      29
      75
      112
      75
      27
      167
      10

In the assumption that the rhythm vectors of distinct voices within a song might include information we want to preserve, we compiled one rhythm vector per instrument in a song as follows:

```python
# Define instruments
shape_of_you_score = Score(shape_of_you)
instrument_dict = {}
for key in shape_of_you_score.mscx.metadata['parts']:
    for staff in shape_of_you_score.mscx.metadata['parts'][key].keys():
            instrument_dict[staff] = key

#staff to voice/instruments
df_shape_of_you['instrument'] = [instrument_dict[el] if el in instrument_dict else 'na' for el in df_shape_of_you.staff]

#compute rhythm vectors per voice
rhythm_vector_shape_you_instruments = df_shape_of_you.groupby(['instrument','quarter_beats'])['mn'].agg(['count'])
rhythm_vector_shape_you_instruments = rhythm_vector_shape_you_instruments.groupby(level=0).apply(lambda x: x.reset_index(level = 0).drop(['instrument'],axis=1).reindex(list(range(0,16)),fill_value=0).T)
rhythm_vector_shape_you_instruments = rhythm_vector_shape_you_instruments.reset_index()
rhythm_vector_shape_you_instruments
```

      quarter_beats
      instrument
      level_1
      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15

      0
      Grand Piano
      count
      66
      0
      0
      0
      0
      0
      0
      0
      66
      0
      0
      0
      0
      0
      0
      0

      1
      Marimba, untitled
      count
      66
      0
      0
      53
      14
      0
      63
      0
      68
      0
      0
      57
      17
      0
      63
      0

      2
      Melodic Drum
      count
      0
      0
      33
      36
      13
      19
      39
      0
      0
      0
      50
      28
      10
      19
      38
      0

      3
      Overdrive Gtr
      count
      6
      2
      1
      1
      4
      4
      6
      2
      6
      1
      6
      2
      6
      1
      6
      0

      4
      Percussion
      count
      24
      23
      0
      2
      19
      2
      22
      2
      21
      21
      1
      2
      19
      2
      23
      2

      5
      Tenor Sax
      count
      20
      8
      10
      8
      16
      12
      16
      8
      16
      7
      18
      6
      15
      5
      14
      8

      6
      Woodblock
      count
      2
      0
      0
      2
      0
      0
      2
      0
      2
      0
      0
      1
      2
      0
      2
      0

      7
      Xylophone
      count
      22
      0
      0
      17
      6
      0
      21
      0
      21
      0
      0
      16
      6
      0
      21
      0

If we want to compile rhythm vectors (per voice) for all mscx files in one directory we can use following loop:

```python
def rythm_vectors(in_dir, out_dir):
    list_sheet_music = os.listdir(in_dir)
    df_rythm_vectors =[]

    for idx, el in enumerate(list_sheet_music):
        if el[-4:] == 'mscx':

            #Get notes with onsets
            s = Score(dir_sheet_music+el)
            df = s.mscx.notes

            # Define instruments
            instrument_dict = {}
            for key in s.mscx.metadata['parts']:
                for staff in s.mscx.metadata['parts'][key].keys():
                    instrument_dict[staff] = key

            #staff to instruments
            df['instrument'] = [instrument_dict[el] if el in instrument_dict else 'na' for el in df.staff]

            # define quarter beat
            df['quarter_beats'] = (df.mc_onset*16).astype('int32')

            #make rythm matrix & data frame
            df_histogram = df.groupby(['instrument','quarter_beats'])['mn'].agg(['count'])
            df_histogram = df_histogram.groupby(level=0).apply(lambda x: x.reset_index(level = 0).drop(['instrument'],axis=1).reindex(list(range(0,16)),fill_value=0).T)
            df_histogram = df_histogram.reset_index()

            df_histogram.insert(loc=0, column='song_artist', value=el[:-5])

            #concat to big rythm vector df
            if len(df_rythm_vectors) == 0: df_rythm_vectors = df_histogram

            df_rythm_vectors = pd.concat([df_rythm_vectors,df_histogram], axis=0)

    df_rythm_vectors.to_csv(out_dir, index = False)

dir_sheet_music = home_dir + '/dataset/project_mscx/mscx_billboard/'
out_dir = home_dir + '/dataset/rythm_vectors/rythm_vectors_billboard.csv'
rythm_vectors(dir_sheet_music, out_dir)
```

**TA instructions**:
- Present your results in relation to your research question.
- Present them in a logical order that does not have to be the order in which you achieved them.

**Thoughts Aurel:**
- This will be our big code part. I would propose following order:
 1. Histograms of the self defined Tresillio & critical discussion reasoning
 2. Histogram of the Tresillio as defined by popular songs. This includes variations of taking only some instruments. etc
 3. Comparison of Tresillio Histograms to histograms where we can be sure that there is no Tresillio
 4. Discussion of the clustering method, either k-mean clustering or something we dont have to set the cluster number
 5. Finding Tresillio-ness in the pop charts with all three methods a) Vanilla Tresillio-ness b) Tresillio songs vector c)

## 4) Outlook on final interpretation

Points to discuss as stated by TAs:
- Interpreting your results is the final step that you will do in preparing Milestone 4 (your presentations). Please end your submission by giving a first,preliminary outlook on this final step: what aspects of your results do you find interesting with respect to your hypotheses and previous literature? What do you think might be the main points to elaborate upon in the discussion?

```python

```

```python

```


# Tutorial 2: Introduction to GANs and Density Ratio Estimation Perspective of GANs

**Week 2, Day 5: Generative Models**

**By Neuromatch Academy**

__Content creators:__ Kai Xu, Seungwook Han, Akash Srivastava

__Content reviewers:__ Polina Turishcheva, Melvin Selim Atay, Hadi Vafaei, Deepak Raya, Charles J Edelson, Kelson Shilling-Scrivo

__Content editors:__ Charles J Edelson, Kelson Shilling-Scrivo, Spiros Chavlis

__Production editors:__ Arush Tagade, Spiros Chavlis

**Our 2021 Sponsors, including Presenting Sponsor Facebook Reality Labs**

---

## Tutorial Objectives

The goal of this tutorial is two-fold; first you will be introduced to GANs training, and you will be able to understand how GANs are connected to other generative models that we have been before.

By the end of the first part of this tutorial you will be able to:
- Understand, at a high level, how GANs are implemented.
- Understand the training dynamics of GANs.
- Know about a few failure modes of GAN training.
- Understand density ratio estimation using a binary classifier
- Understand the connection between GANs and other generative models.
- Implement a GAN.

```python
# @title Tutorial slides

# @markdown These are the slides for the videos in this tutorial

# @markdown If you want to locally download the slides, click [here](https://osf.io/dftym/download)
from IPython.display import IFrame
IFrame(src=f"https://mfr.ca-1.osf.io/render?url=https://osf.io/dftym/?direct%26mode=render%26action=download%26mode=render", width=854, height=480)
```

---
# Setup

```python
# @title Install dependencies
!pip install git+https://github.com/NeuromatchAcademy/evaltools --quiet
from evaltools.airtable import AirtableForm

# generate airtable form
atform = AirtableForm('appn7VdPRseSoMXEG','W2D5_T2','https://portal.neuromatchacademy.org/api/redirect/to/9c55f6cb-cdf9-4429-ac1c-ec44fe64c303')
```

      Building wheel for evaltools (setup.py) ... done

```python
# Imports
import torch
import numpy as np
import matplotlib.pyplot as plt
```

```python
# @title Figure settings
import ipywidgets as widgets       # interactive display
%config InlineBackend.figure_format = 'retina'
plt.style.use("https://raw.githubusercontent.com/NeuromatchAcademy/content-creation/main/nma.mplstyle")
```

```python
# @title Plotting functions

ld_true = [-7.0066e-01, -2.6368e-01, -2.4250e+00, -2.0247e+00, -1.1795e+00,
        -4.5558e-01, -7.1316e-01, -1.0932e-01, -7.8608e-01, -4.5838e-01,
        -1.0530e+00, -9.1201e-01, -3.8020e+00, -1.7787e+00, -1.2246e+00,
        -6.5677e-01, -3.6001e-01, -2.2313e-01, -1.8262e+00, -1.2649e+00,
        -3.8330e-01, -8.8619e-02, -9.2357e-01, -1.3450e-01, -8.6891e-01,
        -5.9257e-01, -4.8415e-02, -3.3197e+00, -1.6862e+00, -9.8506e-01,
        -1.1871e+00, -7.0422e-02, -1.7378e+00, -1.3099e+00, -1.8926e+00,
        -3.4508e+00, -1.5696e+00, -7.2787e-02, -3.2420e-01, -2.9795e-01,
        -6.4189e-01, -1.4120e+00, -5.3684e-01, -3.4066e+00, -1.9753e+00,
        -1.4178e+00, -2.0399e-01, -2.3173e-01, -1.2792e+00, -7.2990e-01,
        -1.9872e-01, -2.9378e-03, -3.5890e-01, -5.6643e-01, -1.8003e-01,
        -1.5818e+00, -5.2227e-01, -2.1862e+00, -1.8743e+00, -1.4200e+00,
        -3.1988e-01, -3.5513e-01, -1.5905e+00, -4.2916e-01, -2.5556e-01,
        -8.2807e-01, -6.5568e-01, -4.8475e-01, -2.1049e-01, -2.0104e-02,
        -2.1655e+00, -1.1496e+00, -3.6168e-01, -8.9624e-02, -6.7098e-02,
        -6.0623e-02, -5.1165e-01, -2.7302e+00, -6.0514e-01, -1.6756e+00,
        -3.3807e+00, -5.7368e-02, -1.2763e-01, -6.6959e+00, -5.2157e-01,
        -8.7762e-01, -8.7295e-01, -1.3052e+00, -3.6777e-01, -1.5904e+00,
        -3.8083e-01, -2.8388e-01, -1.5323e-01, -3.7549e-01, -5.2722e+00,
        -1.7393e+00, -2.8814e-01, -5.0310e-01, -2.2077e+00, -1.5507e+00,
        -6.8569e-01, -1.4620e+00, -9.2639e-02, -1.4160e-01, -3.6734e-01,
        -1.0053e+00, -6.7353e-01, -2.2676e+00, -6.0812e-01, -1.0005e+00,
        -4.2908e-01, -5.1369e-01, -2.2579e-02, -1.8496e-01, -3.4798e-01,
        -7.3089e-01, -1.1962e+00, -1.6095e+00, -1.7558e-01, -3.3166e-01,
        -1.1445e+00, -2.4674e+00, -5.0600e-01, -2.0727e+00, -5.4371e-01,
        -8.0499e-01, -3.0521e+00, -3.6835e-02, -2.0485e-01, -4.6747e-01,
        -3.6399e-01, -2.6883e+00, -1.9348e-01, -3.1448e-01, -1.6332e-01,
        -3.2233e-02, -2.3336e-01, -2.6564e+00, -1.2841e+00, -1.3561e+00,
        -7.4717e-01, -2.7926e-01, -8.7849e-01, -3.3715e-02, -1.4933e-01,
        -2.7738e-01, -1.6899e+00, -1.5758e+00, -3.2608e-01, -6.5770e-01,
        -1.7136e+00, -5.8316e+00, -1.1988e+00, -8.3828e-01, -1.8033e+00,
        -2.3017e-01, -8.9936e-01, -1.1917e-01, -1.6659e-01, -2.7669e-01,
        -1.2955e+00, -1.2076e+00, -2.2793e-01, -1.0528e+00, -1.4894e+00,
        -5.7428e-01, -7.3208e-01, -9.5673e-01, -1.6617e+00, -3.9169e+00,
        -1.2182e-01, -3.8092e-01, -1.1924e+00, -2.4566e+00, -2.7350e+00,
        -2.8332e+00, -9.1506e-01, -6.7432e-02, -7.8965e-01, -2.0727e-01,
        -3.4615e-02, -2.8868e+00, -2.1218e+00, -1.2368e-03, -9.0038e-01,
        -5.3746e-01, -5.4080e-01, -3.1625e-01, -1.1786e+00, -2.2797e-01,
        -1.1498e+00, -1.3978e+00, -1.9515e+00, -1.1614e+00, -5.1456e-03,
        -1.9316e-01, -1.3849e+00, -9.2799e-01, -1.1649e-01, -2.3837e-01]

def plotting_ld(ld, true=ld_true):
  fig, ax = plt.subplots(figsize=(7, 7))
  ax.plot([-6, 1], [-6, 1], label="Ground Truth")
  ax.scatter(true, ld, marker="x",
             label="Your implementation")
  ax.set_xlabel("Loss from oracle implementation")
  ax.set_ylabel("Loss from your implementation")
  ax.legend()
  ax.set_title("Discriminator Loss")

lg_true = [-7.0066e-01, -2.6368e-01, -2.4250e+00, -2.0247e+00, -1.1795e+00,
        -4.5558e-01, -7.1316e-01, -1.0932e-01, -7.8608e-01, -4.5838e-01,
        -1.0530e+00, -9.1201e-01, -3.8020e+00, -1.7787e+00, -1.2246e+00,
        -6.5677e-01, -3.6001e-01, -2.2313e-01, -1.8262e+00, -1.2649e+00,
        -3.8330e-01, -8.8619e-02, -9.2357e-01, -1.3450e-01, -8.6891e-01,
        -5.9257e-01, -4.8415e-02, -3.3197e+00, -1.6862e+00, -9.8506e-01,
        -1.1871e+00, -7.0422e-02, -1.7378e+00, -1.3099e+00, -1.8926e+00,
        -3.4508e+00, -1.5696e+00, -7.2787e-02, -3.2420e-01, -2.9795e-01,
        -6.4189e-01, -1.4120e+00, -5.3684e-01, -3.4066e+00, -1.9753e+00,
        -1.4178e+00, -2.0399e-01, -2.3173e-01, -1.2792e+00, -7.2990e-01,
        -1.9872e-01, -2.9378e-03, -3.5890e-01, -5.6643e-01, -1.8003e-01,
        -1.5818e+00, -5.2227e-01, -2.1862e+00, -1.8743e+00, -1.4200e+00,
        -3.1988e-01, -3.5513e-01, -1.5905e+00, -4.2916e-01, -2.5556e-01,
        -8.2807e-01, -6.5568e-01, -4.8475e-01, -2.1049e-01, -2.0104e-02,
        -2.1655e+00, -1.1496e+00, -3.6168e-01, -8.9624e-02, -6.7098e-02,
        -6.0623e-02, -5.1165e-01, -2.7302e+00, -6.0514e-01, -1.6756e+00,
        -3.3807e+00, -5.7368e-02, -1.2763e-01, -6.6959e+00, -5.2157e-01,
        -8.7762e-01, -8.7295e-01, -1.3052e+00, -3.6777e-01, -1.5904e+00,
        -3.8083e-01, -2.8388e-01, -1.5323e-01, -3.7549e-01, -5.2722e+00,
        -1.7393e+00, -2.8814e-01, -5.0310e-01, -2.2077e+00, -1.5507e+00]

def plotting_lg(lg, true=lg_true):
  fig, ax = plt.subplots(figsize=(7, 7))
  ax.plot([-6, 1], [-6, 1], label="Ground Truth")
  ax.scatter(true, lg, marker="x",
             label="Your implementation")
  ax.set_xlabel("Loss from oracle implementation")
  ax.set_ylabel("Loss from your implementation")
  ax.legend()
  ax.set_title("Generator loss")

def plotting_ratio_impl(ax, x_real, x_fake, ratio, yscale="linear"):
  dist_p = torch.distributions.normal.Normal(loc=0, scale=1)
  dist_q = torch.distributions.normal.Normal(loc=-2, scale=1)
  x = torch.linspace(-3, 5, 100)
  prob_p = torch.exp(dist_p.log_prob(x))
  prob_q = torch.exp(dist_q.log_prob(x))
  trueRatio = prob_p / prob_q
  ax.plot(x, trueRatio, label="True tatio")

  x = torch.cat([x_real, x_fake])
  ax.scatter(x[:,0][::10], ratio[:,0][::10], marker="x",
             label="Ratio from discriminator")
  ax.hist(x_real[:,0], density=True, bins=50, histtype="step", label="Real")
  ax.hist(x_fake[:,0], density=True, bins=50, histtype="step", label="Fake")
  ax.set_yscale(yscale)
  title = "Densities and the ratio from discriminator"
  if yscale == "log":
    title += " in log scale"
  ax.set_title(title)
  ax.legend()

def plotting_ratio(x_real, x_fake, ratio):
  fig, axes = plt.subplots(1, 2, figsize=(2 * 7, 7))
  plotting_ratio_impl(axes[0], x_real, x_fake, ratio, yscale="linear")
  plotting_ratio_impl(axes[1], x_real, x_fake, ratio, yscale="log")

class Interactive:
  def display_widgets(self):
    for widget in self.widgets:
      display(widget)

  def __init__(self, widgets, handler):
    def handler_with_extra_steps(b):
      clear_output(wait=True)
      handler(*map(lambda w: w.value, widgets))
      self.display_widgets()
    self.widgets = widgets
    for widget in self.widgets:
      widget.observe(handler_with_extra_steps,
                     names=['value'])
    handler(*map(lambda w: w.value, widgets))
    self.display_widgets()

# Using Interactive
# All widgets: https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html

def make_plot(xmax, title, ftype):
  fig, ax = plt.subplots()
  x = np.linspace(-2, xmax, 100)
  if ftype == "sin":
    y = np.sin(x)
  if ftype == "cos":
    y = np.cos(x)
  if ftype == "tanh":
    y = np.tanh(x)
  ax.scatter(x, y)
  ax.set_xlim(-2.1, 2.1)
  ax.set_ylim(-2, 2)
  if title:
    ax.set_title(f"Range from -1 to {xmax}")
  return fig
```

```python
# @title Set random seed

# @markdown Executing `set_seed(seed=seed)` you are setting the seed

# for DL its critical to set the random seed so that students can have a
# baseline to compare their results to expected results.
# Read more here: https://pytorch.org/docs/stable/notes/randomness.html

# Call `set_seed` function in the exercises to ensure reproducibility.
import random
import torch

def set_seed(seed=None, seed_torch=True):
  if seed is None:
    seed = np.random.choice(2 ** 32)
  random.seed(seed)
  np.random.seed(seed)
  if seed_torch:
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True

  print(f'Random seed {seed} has been set.')

# In case that `DataLoader` is used
def seed_worker(worker_id):
  worker_seed = torch.initial_seed() % 2**32
  np.random.seed(worker_seed)
  random.seed(worker_seed)
```

```python
# @title Set device (GPU or CPU). Execute `set_device()`
# especially if torch modules used.

# inform the user if the notebook uses GPU or CPU.

def set_device():
  device = "cuda" if torch.cuda.is_available() else "cpu"
  if device != "cuda":
    print("WARNING: For this notebook to perform best, "
        "if possible, in the menu under `Runtime` -> "
        "`Change runtime type.`  select `GPU` ")
  else:
    print("GPU is enabled in this notebook.")

  return device
```

```python
SEED = 2021
set_seed(seed=SEED)
DEVICE = set_device()
```

    Random seed 2021 has been set.
    GPU is enabled in this notebook.

---
# Section 1: How to train GANs

*Time estimate: ~15mins*

```python
# @title Video 1: Generative Adversarial Networks
from ipywidgets import widgets

out2 = widgets.Output()
with out2:
  from IPython.display import IFrame
  class BiliVideo(IFrame):
    def __init__(self, id, page=1, width=400, height=300, **kwargs):
      self.id=id
      src = "https://player.bilibili.com/player.html?bvid={0}&page={1}".format(id, page)
      super(BiliVideo, self).__init__(src, width, height, **kwargs)

  video = BiliVideo(id=f"BV1o64y1i7xA", width=854, height=480, fs=1)
  print("Video available at https://www.bilibili.com/video/{0}".format(video.id))
  display(video)

out1 = widgets.Output()
with out1:
  from IPython.display import YouTubeVideo
  video = YouTubeVideo(id=f"FmUbll93kms", width=854, height=480, fs=1, rel=0)
  print("Video available at https://youtube.com/watch?v=" + video.id)
  display(video)

out = widgets.Tab([out1, out2])
out.set_title(0, 'Youtube')
out.set_title(1, 'Bilibili')

# add event to airtable
atform.add_event('Video 1: Generative Adversarial Networks')

display(out)
```

    Tab(children=(Output(), Output()), _titles={'0': 'Youtube', '1': 'Bilibili'})

GANs consist two networks: A critic or discriminator (`disc`) and a generator (`gen`) that are trained by alternating between the following two steps:
- In step 1, we update the parameters (`disc.params`) of the discriminator by backpropagating through the discriminator loss (BCE loss) `disc.loss`.
- In step 2, we update the parameters (`gen.params`) of the generator by backpropagating through the generator loss, `gen.loss` (-1 * BCE loss).

We will now implement a simple GAN training loop!

## Coding Exercise 1: The GAN training loop

To get you started we have implemented a simple GAN in pseudocode. All you have to do is to implement the training loop.

__Your goal__ is to arrange the functions given below in the correct order in the `train_gan_iter` function
- `disc.loss(x_real, x_fake)`: Discriminator loss
- `disc.classify(x)`: Classify `x` as real or fake
- `gen.loss(x_fake, disc_fn)`: Generator loss
- `disc_fn(x)` is a function to check `x` is real or fake.
- `gen.sample(num_samples)`: Generate samples from the generator
- `backprop(loss, model)`: Compute gradient of `loss` wrt `model`
- `model` is either `disc` or `gen`

We have already taken care of most of these functions. So you only have to figure out the placement of `disc.loss` and `gen.loss` functions.

__We highly recommend studying `train_gan_iter` function to understand how the GAN training loop is structured.__

```python
# @markdown *Execute this cell to enable helper functions*

def get_data():
  return "get_data"

class Disc:

  def loss(self, x_real, x_fake):
    assert x_real == "get_data" and x_fake == "gen.sample","Inputs to disc.loss is wrong"

  def classify(self, x):
    return "disc.classify"

class Gen:

  def loss(self, x_fake, disc_fn):
    assert x_fake == "gen.sample" and disc_fn(None) == "disc.classify", "Inputs to gen.loss is wrong"

  def sample(self, num_samples):
    return "gen.sample"

def backprop(loss, model):
  pass

def update(model, grad):
  pass
```

```python
def train_gan_iter(data, disc, gen):
  """Update the discriminator (`disc`) and the generator (`gen`) using `data`

  Args:
    data (ndarray): An array of shape (N,) that contains the data
    disc (Disc): The discriminator
    gen (Gen): The generator

  Returns:
  """
  #################################################
  # Intructions for students:                        #
  # Fill out ... in the function and remove below #
  #################################################

  # Number of samples in the data batch
  num_samples = 200

  # The data is the real samples
  x_real = data

  ## Discriminator training

  # Ask the generator nicely to generate some fake samples
  x_fake = gen.sample(num_samples)

  #################################################
  ## TODO for students: details of what they should do ##
  # Fill out function and remove
  # raise NotImplementedError("Student exercise: Write code to compute disc_loss")
  #################################################
  # Compute the discriminator loss
  disc_loss = disc.loss(x_real, x_fake)

  # Compute the gradient for discriminator
  disc_grad = backprop(disc_loss, disc)

  # Update the discriminator
  update(disc, disc_grad)

  ## Generator training

  # Ask the generator to generate some fake samples
  x_fake = gen.sample(num_samples)

  #################################################
  ## TODO for students: details of what they should do ##
  # Fill out function and remove
  # raise NotImplementedError("Student exercise: Write code to compute gen_loss")
  #################################################
  # Compute the generator loss
  gen_loss = gen.loss(x_fake, disc.classify)

  # Compute the gradient for generator
  gen_grad = backprop(gen_loss, gen)

  # Update the generator
  update(gen, gen_grad)

  print("Your implementation passes the check!")

  return None

# add event to airtable
atform.add_event('Coding Exercise 1: The GAN training loop')

data = get_data()
disc = Disc()
gen = Gen()
# Uncomment below to check your function
train_gan_iter(data, disc, gen)
```

    Your implementation passes the check!

```python
# to_remove solution
def train_gan_iter(data, disc, gen):
  """Update the discriminator (`disc`) and the generator (`gen`) using `data`

  Args:
    data (ndarray): An array of shape (N,) that contains the data
    disc (Disc): The discriminator
    gen (Gen): The generator

  Returns:
  """

  # Number of samples in the data batch
  num_samples = 200

  # The data is the real samples
  x_real = data

  ## Discriminator training

  # Ask the generator to generate some fake samples
  x_fake = gen.sample(num_samples)

  # Compute the discriminator loss
  disc_loss = disc.loss(x_real, x_fake)

  # Compute the gradient for discriminator
  disc_grad = backprop(disc_loss, disc)

  # Update the discriminator
  update(disc, disc_grad)

  ## Generator training

  # Ask the generator to generate some fake samples
  x_fake = gen.sample(num_samples)

  # Compute the generator loss
  gen_loss = gen.loss(x_fake, disc.classify)

  # Compute the gradient for generator
  gen_grad = backprop(gen_loss, gen)

  # Update the generator
  update(gen, gen_grad)

  print("Your implementation passes the check!")

  return None

# add event to airtable
atform.add_event('Coding Exercise 1: The GAN training loop')

data = get_data()
disc = Disc()
gen = Gen()
## Uncomment below to check your function
train_gan_iter(data, disc, gen)
```

---
# Section 2: GAN Training Objective

*Time estimate: ~20mins*

The training objective of GANs consists of the losses for generators and discriminators respectively. In this section we will be implementing these objectives.

```python
# @title Video 2: Principles of GANs
from ipywidgets import widgets

out2 = widgets.Output()
with out2:
  from IPython.display import IFrame
  class BiliVideo(IFrame):
    def __init__(self, id, page=1, width=400, height=300, **kwargs):
      self.id=id
      src = "https://player.bilibili.com/player.html?bvid={0}&page={1}".format(id, page)
      super(BiliVideo, self).__init__(src, width, height, **kwargs)

  video = BiliVideo(id=f"BV1bo4y1U7YT", width=854, height=480, fs=1)
  print("Video available at https://www.bilibili.com/video/{0}".format(video.id))
  display(video)

out1 = widgets.Output()
with out1:
  from IPython.display import YouTubeVideo
  video = YouTubeVideo(id=f"U_4z5-hX1Kg", width=854, height=480, fs=1, rel=0)
  print("Video available at https://youtube.com/watch?v=" + video.id)
  display(video)

out = widgets.Tab([out1, out2])
out.set_title(0, 'Youtube')
out.set_title(1, 'Bilibili')

# add event to airtable
atform.add_event('Video 2: Principles of GANs')

display(out)
```

    Tab(children=(Output(), Output()), _titles={'0': 'Youtube', '1': 'Bilibili'})

## Section 2.1:  Discriminator Loss

The critic or the discriminator in a vanilla GAN is trained as a binary classifier using the BCE criteria. In this section, we will implement the training objective for the discriminator.

\begin{equation}
\text{BCE}_\omega = \mathbb{E}_{x \sim p}[\log(\sigma(D_\omega(x)))] + \mathbb{E}_{x \sim q}[\log(1 - \sigma(D_\omega(x)))]
\end{equation}

Here, $p$ is the data distribution and $q$ is the generator distribution. $D_\omega$ is the logit, which represents $\log \frac{p}{q}$. $\sigma$ is the sigmoid function and therfore, $\sigma(D_\omega)$ represents $\frac{p}{p+q}$.

### Coding Exercise 2.1: Implement Discriminator Loss

To get you started we have implemented a simple GAN in pseudocode and partially implemented the discriminator training objective.

**Your goal** is to complete the missing part in the training objective of the discriminator in the function `loss_disc`.

`loss_disc` also allows you evaluate the loss function on some random samples.
If your implementation is correct, you will see a plot where the loss values from your implementation will match the ground truth loss values.

In practice, given $N$ samples, we estimate BCE as

\begin{equation}
\text{BCE}_\omega = -\frac{1}{N} \sum_{i=1}^N y_i \log(\sigma(D_\omega(x_i)) + (1-y_i) \log(1-\sigma(D_\omega(x_i))).
\end{equation}

Here, $y$ is the label. $y=1$ when $x \sim p$ (real data) and $y=0$ when $x \sim q$ (i.e., fake data).

Please note, `disc.classify` = $\sigma(D_\omega)$ in `loss_disc`.

```python
# @markdown *Execute this cell to enable helper functions*

def get_data(num_samples=100, seed=0):
  set_seed(seed)
  return torch.randn([num_samples, 1])

class DummyGen:
  def sample(self, num_samples=100, seed=1):
    set_seed(seed)
    return torch.randn([num_samples, 1]) + 2

class DummyDisc:
  def classify(self, x, seed=0):
    set_seed(seed)
    return torch.rand([x.shape[0], ])
```

```python
def loss_disc(disc, x_real, x_fake):
  """Compute the discriminator loss for `x_real` and `x_fake` given `disc`

  Args:
    disc: The discriminator
    x_real (ndarray): An array of shape (N,) that contains the real samples
    x_fake (ndarray): An array of shape (N,) that contains the fake samples

  Returns:
    ndarray: The discriminator loss
  """

  label_real = 1
  #################################################
  # TODO for students: Loss for real data
  # raise NotImplementedError("Student exercise: Implement loss for real samples")
  #################################################
  loss_real = label_real * torch.log(disc.classify(x_real))

  label_fake = 0
  #################################################
  # TODO for students: Loss for fake data
  #raise NotImplementedError("Student exercise: Implement loss for fake samples")
  #################################################
  loss_fake = (1 - label_fake) * torch.log(1 - disc.classify(x_fake))

  return torch.cat([loss_real, loss_fake])

# add event to airtable
atform.add_event('Coding Exercise 3.1: Implement Discriminator Loss')

disc = DummyDisc()
gen = DummyGen()

x_real = get_data()
x_fake = gen.sample()

# Uncomment to check your function
ld = loss_disc(disc, x_real, x_fake)
plotting_ld(ld)
```

```python
# to_remove solution
def loss_disc(disc, x_real, x_fake):
  """Compute the discriminator loss for `x_real` and `x_fake` given `disc`

  Args:
    disc: The discriminator
    x_real (ndarray): An array of shape (N,) that contains the real samples
    x_fake (ndarray): An array of shape (N,) that contains the fake samples

  Returns:
    ndarray: The discriminator loss
  """

  # Loss for real data
  label_real = 1
  loss_real = label_real * torch.log(disc.classify(x_real))

  # Loss for fake data
  label_fake = 0
  loss_fake = (1 - label_fake) * torch.log(1 - disc.classify(x_fake))

  return torch.cat([loss_real, loss_fake])

# add event to airtable
atform.add_event('Coding Exercise 3.1: Implement Discriminator Loss')

disc = DummyDisc()
gen = DummyGen()

x_real = get_data()
x_fake = gen.sample()

## Uncomment to check your function
ld = loss_disc(disc, x_real, x_fake)
with plt.xkcd():
  plotting_ld(ld)
```

**A note on numerical stability**

It is common that functions like $\log$ throw a numerical error.
For $\log$, it happens when $x$ in $\log(x)$ is very close to 0.
The most common practice is to always add some very small value $\epsilon$ to $x$, i.e. use $\log(x + \epsilon)$ instead.
Most build-in functions in modern DL frameworks like TensorFlow or PyTorch handle such things in their build-in loss already, e.g., `torch.nn.BCE`, which is equivalent to the loss you implemented above.

## Section 2.3:  The generator loss

Now that we have a trained critic, lets see how to train the generator using it.

### Coding Exercise 2.3: The generator loss

We will now implement the generator loss function and evaluate it on some fixed points.

**Your goal** is to complete the implementation of the function `loss_gen` using the optimal critic from above.

Upon correct implementation, you shall see a plot where the loss values from generator samples align with the "Correct" values.

**HINT:** You simply need to change the labels.

```python
def loss_gen(disc, x_fake):
  """Compute the generator loss for `x_fake` given `disc`

  Args:
    disc: The generator
    x_fake (ndarray): An array of shape (N,) that contains the fake samples

  Returns:
    ndarray: The generator loss
  """

  #################################################
  # TODO for students: Loss for fake data
  # raise NotImplementedError("Student exercise: Implement loss for fake data")
  #################################################
  label_fake = 1
  loss_fake = label_fake * torch.log(disc.classify(x_fake))

  return loss_fake

# add event to airtable
atform.add_event('Coding Exercise 2.3: The generator loss')

disc = DummyDisc()
gen = DummyGen()

x_fake = gen.sample()
# Uncomment below to check your function
lg = loss_gen(disc, x_fake)
plotting_lg(lg)
```

```python
# to_remove solution
def loss_gen(disc, x_fake):
  """Compute the generator loss for `x_fake` given `disc`

  Args:
    disc: The generator
    x_fake (ndarray): An array of shape (N,) that contains the fake samples

  Returns:
    ndarray: The generator loss
  """

  # Loss for fake data
  label_fake = 1
  loss_fake = label_fake * torch.log(disc.classify(x_fake))

  return loss_fake

# add event to airtable
atform.add_event('Coding Exercise 2.3: The generator loss')

disc = DummyDisc()
gen = DummyGen()

x_fake = gen.sample()
## Uncomment below to check your function
lg = loss_gen(disc, x_fake)
with plt.xkcd():
  plotting_lg(lg)
```

**Did you notice?**

The loss you implemented for generator is essentially the part for real data in `loss_disc`, i.e., it is saying, "*the data I am feeding to you is real and not fake*".

---
# Section 3: The difficulty of GAN training.

*Time estimate: ~10mins*

In this section we will develop an intuition for the training dynamics of GANs.

## Interactive Demo 2: Failure modes of GAN training

GAN training is notoriously difficult because
it is very sensitive to hyper-parameters such as learning rate and model architecture. To help you develop a sense of this here is a very simple GAN training demo that we have borrowed from [Andrej Karpathy's website](https://cs.stanford.edu/people/karpathy/gan/).

The generator $G$, pictured in red, takes inputs sampled from a uniform distribution, $z$. It attempts to transform these to match a data distribution, shown below in blue. Meanwhile, the discriminator $D$ attempts to determine whether a sample is from the data distribution or the generating distribution. In the demo, the green curve represents the output of the discriminator. Its value is high where the discriminator is more confident that a sample with that value is drawn from the data distribution.

Even though the GAN in this demo is very simple and operates in either 1D or 2D spaces, it is however very sensitive to the learning rate. Try it for yourself!

```python
# @title Video 3: GAN generator Learning Idea
from ipywidgets import widgets

out2 = widgets.Output()
with out2:
  from IPython.display import IFrame
  class BiliVideo(IFrame):
    def __init__(self, id, page=1, width=400, height=300, **kwargs):
      self.id=id
      src = "https://player.bilibili.com/player.html?bvid={0}&page={1}".format(id, page)
      super(BiliVideo, self).__init__(src, width, height, **kwargs)

  video = BiliVideo(id=f"BV1aL411J7SE", width=854, height=480, fs=1)
  print("Video available at https://www.bilibili.com/video/{0}".format(video.id))
  display(video)

out1 = widgets.Output()
with out1:
  from IPython.display import YouTubeVideo
  video = YouTubeVideo(id=f"Iqqz2_USUGs", width=854, height=480, fs=1, rel=0)
  print("Video available at https://youtube.com/watch?v=" + video.id)
  display(video)

out = widgets.Tab([out1, out2])
out.set_title(0, 'Youtube')
out.set_title(1, 'Bilibili')

# add event to airtable
atform.add_event('Video 3: GAN generator Learning Idea')

display(out)
```

    Tab(children=(Output(), Output()), _titles={'0': 'Youtube', '1': 'Bilibili'})

```python
# @title GAN training demo
# @markdown Make sure you execute this cell to enable the widget!

from IPython.display import IFrame
IFrame(src='https://xukai92.github.io/gan_demo/index.html', width=900, height=600)
```

## Think! 2: What makes GANs hard to train?

You have played with the demo and it's time to think about a few questions

1. Which target is more stable to train, 1D or 2D?
2. If you keep increasing the learning rate, what happens? Does it happen in both the cases, i.e., 1D/2D targets?
3. Can you think of some drawbacks of using small learning rates?

```python
# @title Student Response
from ipywidgets import widgets

text=widgets.Textarea(
   value='Type your answer here and click on `Submit!`',
   placeholder='Type something',
   description='',
   disabled=False
)

button = widgets.Button(description="Submit!")

display(text,button)

def on_button_clicked(b):
   atform.add_answer('q1', text.value)
   print("Submission successful!")

button.on_click(on_button_clicked)
```

```python
# to_remove explanation

"""
  1. 2D as it is simply a more difficult distribution to model compared to the
     unimodal 1D example.
    2. The training becomes unstable and eventually diverges. Yes.
  3. Training is too slow and takes longer to convege.

In general, when training a GAN we need to ensure a certain balance between
the critic and generator training. As such, tuninig the learning rate
is crucial for succesfully training GANs.
"""
```

```python
# @title Video 4: GAN Failure Models
from ipywidgets import widgets

out2 = widgets.Output()
with out2:
  from IPython.display import IFrame
  class BiliVideo(IFrame):
    def __init__(self, id, page=1, width=400, height=300, **kwargs):
      self.id=id
      src = "https://player.bilibili.com/player.html?bvid={0}&page={1}".format(id, page)
      super(BiliVideo, self).__init__(src, width, height, **kwargs)

  video = BiliVideo(id=f"BV17M4y1L7w9", width=854, height=480, fs=1)
  print("Video available at https://www.bilibili.com/video/{0}".format(video.id))
  display(video)

out1 = widgets.Output()
with out1:
  from IPython.display import YouTubeVideo
  video = YouTubeVideo(id=f"fmU2UM_QzLo", width=854, height=480, fs=1, rel=0)
  print("Video available at https://youtube.com/watch?v=" + video.id)
  display(video)

out = widgets.Tab([out1, out2])
out.set_title(0, 'Youtube')
out.set_title(1, 'Bilibili')

# add event to airtable
atform.add_event('Video 4: GAN Failure Models')

display(out)
```

    Tab(children=(Output(), Output()), _titles={'0': 'Youtube', '1': 'Bilibili'})

---
# Section 4: GAN training in action!

*Time estimate: ~4mins*

In this section we will be playing with a complete implementation of GAN.

## Interactive Demo 4: GAN training in action

```python
# @title GanLab
from IPython.display import HTML
HTML('')
```

---
# Summary

Through this tutorial, we have learned

- How to implement the training loop of GANs.
- Developed an intuition about the training dynamics of GANs.
- How to implement the training objectives for the generator and discriminator of GANs.
- How are GANs connected to density ratio estimation.

Next tutorial will cover conditional GANs and ethical issues of DL.

```python
# @title Airtable Submission Link
from IPython import display as IPydisplay
IPydisplay.HTML(
   f"""
 <div>
   <a href= "{atform.url()}" target="_blank">
   </a>
   </div>""" )
```

  <a href= "https://portal.neuromatchacademy.org/api/redirect/to/9c55f6cb-cdf9-4429-ac1c-ec44fe64c303?data=eyJmb3JtX2lkIjogImFwcG43VmRQUnNlU29NWEVHIiwgInRhYmxlX25hbWUiOiAiVzJENV9UMiIsICJhbnN3ZXJzIjoge30sICJldmVudHMiOiBbeyJldmVudCI6ICJpbml0IiwgInRzIjogMTYyODg0OTY2Ni41MzE2NjQ4fSwgeyJldmVudCI6ICJWaWRlbyAxOiBHZW5lcmF0aXZlIEFkdmVyc2FyaWFsIE5ldHdvcmtzIiwgInRzIjogMTYyODg0OTY3MS4wODE3NTg1fSwgeyJldmVudCI6ICJDb2RpbmcgRXhlcmNpc2UgMTogVGhlIEdBTiB0cmFpbmluZyBsb29wIiwgInRzIjogMTYyODg1MDQ3MC44NTYzMDF9LCB7ImV2ZW50IjogIkNvZGluZyBFeGVyY2lzZSAxOiBUaGUgR0FOIHRyYWluaW5nIGxvb3AiLCAidHMiOiAxNjI4ODUwNDg1LjE0MzExNTh9LCB7ImV2ZW50IjogIkNvZGluZyBFeGVyY2lzZSAxOiBUaGUgR0FOIHRyYWluaW5nIGxvb3AiLCAidHMiOiAxNjI4ODUwNTY0Ljg2NjExMzR9LCB7ImV2ZW50IjogIlZpZGVvIDI6IFByaW5jaXBsZXMgb2YgR0FOcyIsICJ0cyI6IDE2Mjg4NTA2MTEuMTMxOTI3NX0sIHsiZXZlbnQiOiAiQ29kaW5nIEV4ZXJjaXNlIDMuMTogSW1wbGVtZW50IERpc2NyaW1pbmF0b3IgTG9zcyIsICJ0cyI6IDE2Mjg4NTEwMTEuNzY4MTUyNX0sIHsiZXZlbnQiOiAiQ29kaW5nIEV4ZXJjaXNlIDMuMTogSW1wbGVtZW50IERpc2NyaW1pbmF0b3IgTG9zcyIsICJ0cyI6IDE2Mjg4NTEwMTkuODg0MzJ9LCB7ImV2ZW50IjogIkNvZGluZyBFeGVyY2lzZSAzLjE6IEltcGxlbWVudCBEaXNjcmltaW5hdG9yIExvc3MiLCAidHMiOiAxNjI4ODUxMDI4LjYzMDE3N30sIHsiZXZlbnQiOiAiQ29kaW5nIEV4ZXJjaXNlIDIuMzogVGhlIGdlbmVyYXRvciBsb3NzIiwgInRzIjogMTYyODg1MTI0MC44NTA1Mjk0fSwgeyJldmVudCI6ICJDb2RpbmcgRXhlcmNpc2UgMi4zOiBUaGUgZ2VuZXJhdG9yIGxvc3MiLCAidHMiOiAxNjI4ODUxMjU3LjYwNTc1NTN9LCB7ImV2ZW50IjogIkNvZGluZyBFeGVyY2lzZSAyLjM6IFRoZSBnZW5lcmF0b3IgbG9zcyIsICJ0cyI6IDE2Mjg4NTEyODUuMzA4MDQ4NX0sIHsiZXZlbnQiOiAiVmlkZW8gMzogR0FOIGdlbmVyYXRvciBMZWFybmluZyBJZGVhIiwgInRzIjogMTYyODg1MTQxNi42NzE0MjV9LCB7ImV2ZW50IjogIlZpZGVvIDQ6IEdBTiBGYWlsdXJlIE1vZGVscyIsICJ0cyI6IDE2Mjg4NTE5NjcuNDA0Mjk2NH0sIHsiZXZlbnQiOiAidXJsIGdlbmVyYXRlZCIsICJ0cyI6IDE2Mjg4NTI1NzguMTI5MTQwOX1dfQ%3D%3D" target="_blank">

```python

```


```python
from IPython.display import HTML
hide_me = ''
HTML('''
To toggle on/off the raw code, click <a href="javascript:code_toggle()">here</a>.''')
```

```python
hide_me

import ipywidgets as widgets
from IPython.display import display, Math, Latex, HTML, IFrame
from ipywidgets import IntSlider, Label
from ipywidgets import interact, interactive
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pandas as pd
```

Grade 11
# Uniform Motion and Uniformly Accelerated Motion

>
>
  https://giphy.com/gifs/usain-bolt-sgjJkkJutglMY

## Introduction

Everything in the universe is constantly moving. Objects can be moving incredibly slow, so slow that they appear to be at rest, or so incredibly fast that you may not even see it. Even if you're standing still on Earth, you're  moving and incomprehensible speeds. On Earth, you are moving around the Sun at approximately 108,000 km/h, and the Sun is orbiting galactic center at approximately 720,000 km/h. But that's not it: our galaxy the Milky Way is moving at approximately 2,268,000 km/h.  As motion is so universal, understanding motion is an important topic of physics. Motion is defined by the change of position of an object with respect to other surrounding objects. For example a car is moving with respect to trees on the roadside. Motion can be described using three important quantities: velocity, speed and acceleration. In this notebook, we will familiarize ourselves with two types of motion: uniform motion, and uniformly accelerated motion.

## Concepts of Uniform Motion

Motion is described by three variables: distance ($d$), velocity ($\vec{v}$), and acceleration ($\vec{a}$). Let's define and explore these quantities below.

### Distance Vs. Displacement
To begin, let us outline the difference between distance and displacement.
> Distance describes the length of the actual path traveled to travel from one point to another.

> Displacement is identical to distance as it describes the amount of space between two points. However, displacement is a vector quantity which means it also specifies the _direction_ of travel, as well as the amount of space between two points.

Below is a video which demonstrates the difference between distance and displacement:

```python
hide_me

from IPython.display import HTML
# Youtube
HTML('')
```

**Practise**

Calculate the distance and displacement based on the image below. Imagine you start at  point $\textrm{A}$ and you move around the field in the following order $\textrm{A} \rightarrow \textrm{B} \rightarrow \textrm{C} \rightarrow \textrm{D} \rightarrow \textrm{E} \rightarrow \textrm{A}$:

```python
hide_me

def q_1(val):
    if val == "Distance: 2m and Displacement: 2m East":
        display(Latex("Correct!"))
        display(Latex("Displacement contains both measurement and direction"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Calculate the distance and displacement at point B?"))

a1 = 'Distance: 2m and Displacement: 2m'
a2 = "Distance: 2m and Displacement: 2m East"
a3 = "Distance: 2m North and Displacement: 2m"
interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

```python
hide_me

def q_2(val):
    if val == "Distance: 9m North and Displacement: 2.2m NW":
        display(Latex("Correct!"))
        display(Latex("Distance: Actual path covered and Displacement: Shortest path covered with direction"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Calculate the distance and displacement at point E?"))

a1 = 'Distance: 9m and Displacement: 7m'
a2 = "Distance: 3m and Displacement: 2.2m NW"
a3 = "Distance: 9m North and Displacement: 2.2m NW"
interact(q_2, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

### Speed Vs. Velocity

Analogous to distance and displacement are speed and velocity, however now these quantities also imply that the object's distance/displacement is _changing_. Let's take a look at how speed and velocity are defined.

Speed is the rate of change of distance over time.

\begin{equation}
\textrm{speed } = \frac{\textrm{change} \ \textrm{of} \ \textrm{distance (m)}}{\textrm{time (s)}}
\end{equation}

Velocity is the rate of change of displacement over time.

\begin{equation}
\textrm{velocity, } \vec{v} = \frac{\textrm{change} \textrm{ of} \textrm{ displacement (m)}}{\textrm{time (s)}} \\
\textrm{m} = \textrm{meter } \textrm{and} \textrm{ s} = \textrm{second}
\end{equation}

**Practise**

Now, let's repeat a similar question as we did with displacement and distance, but now include velocity. The required time of one point to another point is given below:

* $\textrm{A} \rightarrow \textrm{C}: 4 \textrm{ sec}$
* $\textrm{A} \rightarrow \textrm{D}: 10 \textrm{ sec}$
* $\textrm{A} \rightarrow \textrm{E}: 16 \textrm{ sec}$
* $\textrm{A} \rightarrow \textrm{A}: 20 \textrm{ sec}$

```python
hide_me

def q_1(val):
    if val == "Speed: 0.75 m/s and Velocity: 0.55 m/s NE":
        display(Latex("Correct!"))
        display(Latex("Velocity contains both measurement and direction as it relates to displacement"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Calculate the speed and velocity at point C?"))

a1 = 'Speed: 0.75 m/s and Velocity: 0.55 m/s NE'
a2 = "Speed: 0.55 m/s and Velocity: 0.75 m/s NE"
a3 = "Speed: 1 m/s and Velocity: 0.55 m/s NE"
interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

```python
hide_me

def q_2(val):
    if val == "Speed: 0.60 m/s and Velocity: 0.20 m/s South":
        display(Latex("Correct!"))
        display(Latex("Speed and Velocity are not always equivalent"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Calculate the speed and velocity at point D?"))

a1 = 'Speed: 0.80 m/s and Velocity: 0.60 m/s South'
a2 = "Speed: 0.60 m/s and Velocity: 0.20 m/s South"
a3 = "Speed: 0.60 m/s and Velocity: 0.60 m/s South"
interact(q_2, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

### Acceleration

Velocity can also change with respect to time. A change in velocity requires _acceleration_, which is defined as rate of change of velocity over time. As acceleration depends on the change in the vector quantity velocity, acceleration is also a vector. Below is an example of acceleration.

 https://giphy.com/gifs/cell-concept-acceleration-139Qnnkbg2pefe

 Acceleration is the rate of change of velocity over time. Acceleration is a vector quantity as it depends on velocity.

\begin{equation}
\textrm{acceleration, } \vec{a} = \frac{\textrm{change} \textrm{ of} \textrm{ velocity } (\frac{\text{m}}{\text{s}}) } {\textrm{time (s)}}
\end{equation}

 [Here is an interactive animation demonstrating acceleration further.](https://faraday.physics.utoronto.ca/PVB/Harrison/Flash/ClassMechanics/MotionDiagram/MotionDiagram.html)

**Practice**

Let's go back to our field example and think about where we may see acceleration. Once again we are traveling from $\textrm{A} \rightarrow \textrm{B} \rightarrow \textrm{C} \rightarrow \textrm{D} \rightarrow \textrm{E} \rightarrow \textrm{A}$:

```python
hide_me

def q_1(val):
    if val == "Acceleration: 0 m/s\u00b2 East":
        display(Latex("Correct!"))
        display(Latex("No change in velocity and direction is also straight line"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Calculate the acceleration at point B?"))

a1 = 'Acceleration: 0 m/s\u00b2 East'
a2 = "Acceleration: 1 m/s\u00b2 East"
a3 = "Acceleration: 2 m/s\u00b2 East"
interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

```python
hide_me

def q_1(val):
    if val == "Yes":
        display(Latex("Correct!"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Does a change in direction imply that there was acceleration?"))

a1 = 'Yes'
a2 = "No"

interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2 ],value = ' ',description = 'Choose One:',disabled = False));

def q_2(val):
    if val == 'To change direction is to change your velocity, and this requires acceleration':
        display(Latex("Correct!"))
        display(Latex("Velocity is a vector quantity. To change your direction requires acceleration in another direction."))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("Why?"))

a1 = 'To change direction is to change your velocity, and this requires acceleration'
a2 = 'When you change direction you gain speed, and this requires acceleration'

interact(q_2, val = widgets.Dropdown(options=[' ',a1 ,a2],value = ' ',description = 'Choose One:',disabled = False));
```

### Uniform Motion

Now that we have an understanding of distance, displacement, velocity and acceleration, let's use those quantities to describe "uniform motion".

Uniform motion has the following two properties:

* Motion is constant or steady that means object covers equal distance in equal time interval.

* The object travels in a straight line.

Consider the blue car in the animation below

 http://www.ninetyeast.net/physics/grade-9-10-gcse-hsc/forces/newtons-laws-of-motion/newtons-first-law-of-motion

The blue car is travelling at a velocity of $10 \textrm{ ms}^{-1}$ to the right. That means every second the car is travelling $10 \textrm{ m}$. If we record this car's displacement and velocity for $10 \textrm{ sec}$, we get the following table.

| Time $(sec$)  |  Displacement ($m$)   | Velocity ($ms^{-1}$)|
|:-------------:|:-----------------:|:--------:|
| $ 1$          | $ 10$             | $10$     |
| $ 2$          |$ 20$             | $10$     |
| $ 3$          |$30$              | $10$     |
| $4$           |$40$              | $10$     |
| $ 5$          | $ 50$            | $10$     |
| $ 6$          |$ 60$             | $10$     |
| $ 7$          |$70$              | $10$     |
| $8$           |$80$              | $10$     |
| $ 9$          |$90$              | $10$     |
| $10$           |$100$              | $10$     |

We can also use this table to create the animation below:

```python
hide_me

# Data
t = np.linspace(0,10,11)
d = np.linspace(0,100,11)
v = np.linspace(10,10,11)

# Create a figure with two subplots
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(10, 4), dpi= 90, facecolor='w', edgecolor='k')

# Same X axis limt and grid initalizations
for ax in [ax1, ax2]:
    ax.set_xlim(0, 10)
    ax.grid()

ax1.set_ylim(0,100)
ax2.set_ylim(0,20)

# Initialize the plot
l1, = ax1.plot([],[], 'go-', label='Displacement', linewidth=2)
leg = ax1.legend(loc='best')
l2, = ax2.plot([],[],  'rs-', label='Velocity')
leg = ax2.legend(loc='best')

fig.suptitle('Uniform Motion')
ax1.set_xlabel('Time (sec)')
ax2.set_xlabel('Time (sec)')
ax1.set_ylabel('Displacement (m)')
ax2.set_ylabel (r'$\mathrm{Velocity} \ (\mathrm{ ms}^{-1})$')

# Initiate the animation
def animate(i):
    l1.set_data(t[:i+1], d[:i+1])
    l2.set_data(t[:i+1], v[:i+1])

ani = FuncAnimation(fig, animate, interval = 800, frames=len(t))
plt.close()

# Convert animation to video
from IPython.display import HTML
HTML(ani.to_html5_video())

```

From the table and animations, we find that the blue car's displacement is changing and its velocity is constant. This is an example of uniform motion. The car travels equal distance in equal time.

\begin{equation}
\textrm{velocity, } \vec{v} = \frac{\textrm{change} \ \textrm{of} \ \textrm{displacement (m)}}{\textrm{time (s)}} = \textrm{constant}
\end{equation}

**Based on your knowledge of uniform motion, what is the blue car's acceleration?**

```python
hide_me

def q_1(val):
    if val == "0":
        display(Latex("Correct!"))
        display(Latex("Speed is constant and direction is in a straight line"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

a1 = '0'
a2 = 'Constant'
a3 = 'Variable'

interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

### Uniform Accelelerated Motion

```python
hide_me

from IPython.display import HTML
# Youtube
HTML('')
```

Uniformly accelerated motion is a little different than the uniform motion we discussed earlier. In the case of uniformly accelerated motion, your velocity is increasing constantly and equally in time. Your velocity is changing in a way similar to how displacement changes if you're traveling at constant velocity. Suppose you start at rest and begin running in order to catch a ball. In this scenario, you will need to _accelerate_. Suppose after each second, you are traveling 2 $\frac{\textrm{m}}{\textrm{s}}$ faster than the second previous. In such a case, you have a uniform acceleration of 2 $\frac{\textrm{m}}{\textrm{s}^2}$ (the seconds are squared in the units of acceleration as you are increasing your velocity constantly - i.e. you gain more velocity per second). Let's write down displacement, velocity and acceleration as you try to catch this ball in a table:

| Time $(\textrm{sec}$)  |  Displacement ($\textrm{m}$)   | Velocity ($\textrm{ms}^{-1}$)| Acceleration ($\textrm{ms}^{-2}$)
|:-------------:|:-----------------:|:--------:|:--------:|
| $ 0$          | $ 0$             | $0$     | $2$      |
| $ 1$          |$ 1$             | $2$     | $2$      |
| $ 2$          |$4$              | $4$     | $2$      |
| $3$           |$9$              | $6$     | $2$      |
| $ 4$          |$16$              | $8$     | $2$     |
| $5$           |$25$              | $10$     | $2$    |
| $6$           |$36$              | $12$     | $2$      |
| $ 7$          |$49$              | $14$     | $2$     |
| $8$           |$64$              | $16$     | $2$    |

Using the table, we can also create an animation

```python
hide_me

# Data
t = np.linspace(0,8,9);
d = ([0,1,4,9,16,25,36,49,64]);
v = ([0,2,4,6,8,10,12,14,16]);
a = np.linspace(2,2,9);

# Create a figure with three subplots
fig, (ax1, ax2, ax3) = plt.subplots(1,3, figsize=(12.5, 4), dpi= 80, facecolor='w', edgecolor='k')

# Same X axis limt and grid initalizations
for ax in [ax1, ax2, ax3]:
    ax.set_xlim(0, 8);
    ax.grid();

ax1.set_ylim(0,70);
ax2.set_ylim(0,16);
ax3.set_ylim(0,5);

# Initialize the plot
l, = ax1.plot([],[], 'go-', label='Displacement', linewidth=2);
leg = ax1.legend(loc='best');
l1, = ax2.plot([],[],  'rs-', label='Velocity');
leg = ax2.legend(loc='best');
l2, = ax3.plot([],[], 'b*-', label='Acceleration', linewidth=2);
leg = ax3.legend(loc='best');

fig.suptitle('Uniformly Accelerated Motion');
ax1.set_xlabel('Time (sec)');
ax2.set_xlabel('Time (sec)');
ax3.set_xlabel('Time (sec)');
ax1.set_ylabel('Displacement (m)');
ax2.set_ylabel (r'$\mathrm{Velocity} \ (\mathrm{ ms}^{-1})$');
ax3.set_ylabel (r'$\mathrm{Acceleration} \ (\mathrm{ ms}^{-2})$');

# Initiate the animation
def animate(i):
    l.set_data(t[:i+1], d[:i+1]);
    l1.set_data(t[:i+1], v[:i+1]);
    l2.set_data(t[:i+1], a[:i+1]);

ani=FuncAnimation(fig, animate, interval = 800, frames=len(t));
plt.close()

# Convert animation to video
from IPython.display import HTML
HTML(ani.to_html5_video())
```

Notice how with constant acceleration, your velocity increases _linearly_. As well, when you're accelerating, your displacement changes _parabolically_. These relationships are explained further with the equations of motion below.

## Equations of Motion

Using our relationships between displacement, velocity, acceleration and time, we can define "equations of motion" for a moving object. There are four such equations relevant to the principles of uniform motion and uniform acceleration.

Suppose an object with initial velocity $\vec{\textrm{v}}_i$ $\textrm{ms}^{-1}$ is travelling with uniform acceleration $\vec{\textrm{a}}$ $\textrm{ms}^{-2}$. After traveling a displacement $\vec{\textrm{s}}$ in time $\textrm{t}$ the object's final velocity would be $\vec{\textrm{v}_f}$ $\textrm{ms}^{-1}$, these quantities are described by the following equations

\begin{align}
   \vec{\textrm{v}_f}=\vec{\textrm{v}_i}+\vec{\textrm{a}} \textrm{t} \ \ \ \ \ \ \ \  (1) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \  \ \ \ \ \ \
   \vec{\textrm{s}} = (\frac{\vec{\textrm{v}_i}+\vec{\textrm{v}_f}}{2})\textrm{t} \; \; (2)
\end{align}

\begin{align}
\vec{\textrm{s}} = \vec{\textrm{v}_i}\textrm{t}+\frac{1}{2}\vec{\textrm{a}}\textrm{t}^{2} \; (3) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \  \ \
\vec{\textrm{v}_f} = \textrm{v}_i^{2}+2 \vec{\textrm{a}} \; \vec{\textrm{s}} \; \; \; \;  (4)
\end{align}

The equations above describe both uniform motion and uniformly accelerated motion. Equation (1) describes your final velocity given an intial velocity, and an acceleration over time. Notice how this is the equation of a line. This is what we see in the center plot of the animation above. Equation (3) describes your displacement moving at some initial velocity and accelerating uniformly for some time. Notice how time is squared in this equation. This means that this is the equation of a parabola. Equation (3) is what we see in the first plot of the animation above.

### Mathematical Problems

As we work through the problems below, we will outline the following steps as we come to the solution. Breaking the problem down in this way makes the problem less intimidating, and makes the path to solution more straight foreward.

> 1. Identify and write what information is given in the problem, and the answer that we are asked for.
2. Using the information we know, and and the problem identified in step one, identify which equation(s) of motion we need to use to solve the problem.
3. Ensure that all the values are in the correct units and fill them in the selected equation.
4. Quote the answer and check the units.

#### Problem 1

A bus  accelerates from rest at $4  \textrm{ms}^{-2}$ until it reaches a final velocity of $40 \ \textrm{ms}^{-1}$. For how many seconds was the bus accelerating?

#### Solution

**Step 1:**

Given,

initial velocity, $\vec{\textrm{v}_i} = 0 \ \textrm{ms}^{-1}$

final velocity, $\vec{\textrm{v}_f} = 40 \ \textrm{ms}^{-1}$

acceleration, $\vec{\textrm{a}} = 4 \ \textrm{ms}^{-2}$

time, $\textrm{t} = ?$

**Step 2:**

In this problem, the value of $\vec{\textrm{v}_i}$, $\vec{\textrm{v}_f}$ and $\vec{\textrm{a}}$ are given and $\textrm{t}$ is required. Now, check the above $4$ motion equations, find the equation is related to$\vec{\textrm{v}_i}$, $\vec{\textrm{v}_f}$ and $\vec{\textrm{a}}$ and $\textrm{t}$. We find the equation and it is $\vec{\textrm{v}_f} = \vec{\textrm{v}_i}+\vec{\textrm{a}}\textrm{t}$.

**Step 3:** After checking all the units of the given variable $\textrm{v}_i$, $\textrm{v}_f$ and $\textrm{a}$, we find that they are correct. Then fill them in selected equation:

\begin{equation}
\vec{\textrm{v}_f} = \vec{\textrm{v}_i}+\vec{\textrm{a}}\textrm{t} \\
\Rightarrow \vec{\textrm{a}}\textrm{t} = \vec{\textrm{v}_f} - \vec{\textrm{v}_i} \\
\Rightarrow \textrm{t} = \frac{\vec{\textrm{v}_f}-\vec{\textrm{v}_i}}{\vec{\textrm{a}}} \\
\Rightarrow \textrm{t} = \frac{40\textrm{ms}^{-1}-0\textrm{ms}^{-1}}{4\textrm{ms}^{-2}} \\
\Rightarrow \textrm{t} = 10\textrm{ s}
\end{equation}

**Step 4:**

time = $10\textrm{ s}$ $(Ans)$

This answer can be seen graphically in the animation below:

```python
hide_me

# Data frame to create table
df = pd.DataFrame()
df['Velocity'] = np.arange(0,41,4)
df['Time'] = df['Velocity']/4

# Create a figure with two subplots
fig,(ax1,ax2) = plt.subplots(1,2,figsize=(6.8, 4), dpi= 100)

# Limit and label axis
ax1.set_ylim(0,10)
ax1.set_xlim(0,40)
ax1.set_xlabel('Velocity ($ms^{-1}$)')
ax1.set_ylabel('Time (s)')
ax1.grid()

# Initiate table properties
font_size=14
bbox=[0, 0, 1, 1]
ax2.axis('off')

# Initialize the plot
l, = ax1.plot([],[], 'go-', label='Time', linewidth=2)
leg = ax1.legend(loc='best')

# Initiate the animation
def animate(i):
    l.set_data(df['Velocity'][:i+1], df['Time'][:i+1])
    table = ax2.table(cellText = df.values[:i+1],bbox=bbox, colLabels=df.columns)

ani = FuncAnimation(fig, animate, interval = 800, frames=len(df.index))
plt.close()

# Convert animation to video
from IPython.display import HTML
HTML(ani.to_html5_video())
```

#### Problem 2

A car accelerates uniformly from $16 \ \textrm{ms}^{-1}$ to $38.8 \ \textrm{ms}^{-1}$ in $3.1$ seconds. Calculate the distance traveled by the car.

#### Solution

**Step 1:**

Given,

$\textrm{initial} \ \textrm{velocity}, \vec{\textrm{v}_i} = 16 \ \textrm{ms}^{-1}$

$\textrm{final} \ \textrm{velocity}, \vec{\textrm{v}_f} = 38.8 \ \textrm{ms}^{-1}$

$\textrm{time, t} = 3.1 \ \textrm{s}$

$\textrm{displacement, } \vec{\textrm{s}} = ?$

**Step 2:**

Try yourself!

```python
hide_me

display(Latex('Which equation can be chosen to solve this problem?'))

#Create the box to select m=Motion of Equations
a=widgets.Checkbox(
    value=False,
    description=r"$\vec{\textrm{v}_f}=\vec{\textrm{v}_i}+\vec{\textrm{a}} \textrm{t}$",
    disabled=False
)

b=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{s}} = (\frac{\vec{\textrm{v}_i}+\vec{\textrm{v}_f}}{2})\textrm{t}$')

c=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{s}} = \vec{\textrm{v}_i}\textrm{t}+\frac{1}{2}\vec{\textrm{a}}\textrm{t}^{2}$'
)
d=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{v}_f} = \vec{\textrm{v}_i}^{2}+2 \vec{\textrm{a}} \; \vec{\textrm{s}}$',
    disabled=False
)

#Display the check box
display(a)
display(b)
display(c)
display(d)

#create a button to check the answer
button_check = widgets.Button(description="check")
display(button_check)

#Check the answer
def check_button(x):
    if a.value==False and b.value==True and c.value==False and d.value==False:
        display(Latex("Correct. Well done!"))
    else:
        display(Latex("Wrong one!"))

button_check.on_click(check_button)

```

```python
hide_me

def q_1(val):
    if val == "84.94 m":
        display(Latex("Correct!"))
        display(Latex(r'$\vec{\textrm{s}} = (\frac{\vec{\textrm{v}_i}+\vec{\textrm{v}_f}}{2})\textrm{t}$'))
        display(Latex(r'$\vec{\textrm{s}} = (\frac{(16 + 38.8)} {2} \times 3.1) \textrm{ m}$'))
        display(Latex(r'$\vec{\textrm{s}} = 84.94 \textrm{ m}$'))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("What is your final displacement?"))

a1 = '84.94 m'
a2 = '65.26 m'
a3 = '89.5 m'

interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

#### Problem 3

A racing car is traveling with a velocity of $72 \ \textrm{kmh}^{-1}$ N and accelerates at $5 \ \textrm{ms}^{-2}$ for $10 \ \textrm{s}$ N. What is the final velocity of the car and how far will it travel as it accelerates?

#### Solution

**Step 1:**

Given
$\textrm{initial} \ \textrm{velocity,} \vec{\textrm{v}_i} = 72 \ \textrm{kmh}^{-1}$ N

$\textrm{acceleration, } \vec{\textrm{a}} = 5 \ \textrm{ms}^{-2}$ N

$\textrm{time}, \textrm{t} = 10 \ \textrm{s}$

$\textrm{final} \ \textrm{velocity}, \vec{\textrm{v}_f} = ?$

$\textrm{displacement}, \vec{\textrm{s}} = ?$

**Step 2:** Let's try,

```python
hide_me

display(Latex('Which equations can be chosen to solve this problem?'))

#Create the box to select m=Motion of Equations
a=widgets.Checkbox(
    value=False,
    description=r"$\vec{\textrm{v}_f}=\vec{\textrm{v}_i}+\vec{\textrm{a}} \textrm{t}$",
    disabled=False
)

b=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{s}} = (\frac{\vec{\textrm{v}_i}+\vec{\textrm{v}_f}}{2})\textrm{t}$')

c=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{s}} = \vec{\textrm{v}_i}\textrm{t}+\frac{1}{2}\vec{\textrm{a}}\textrm{t}^{2}$'
)
d=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{v}_f} = \vec{\textrm{v}_i}^{2}+2 \vec{\textrm{a}} \; \vec{\textrm{s}}$',
    disabled=False
)

#Display the check box
display(a)
display(b)
display(c)
display(d)

#create a button to check the answer
button_check = widgets.Button(description="check")
display(button_check)

#Check the answer
def check_button(x):
    if a.value==True and b.value==True and c.value==True and d.value==True:
        display(Latex("Correct. Well done!"))
        display(Latex("yes, all the equations can be used. It depends on which one you are picking."))
    else:
        display(Latex("Try Again!"))
        display(Latex('Hint: More than one answers'))

button_check.on_click(check_button)

```

**Step 3:**

\begin{equation}
\textrm{initial} \ \textrm{velocity,} \vec{\textrm{v}_i} = 72 \ \textrm{kmh}^{-1} N = \frac{72\times1000}{3600} \ \textrm{ms}^{-1} = 20 \ \textrm{ms}^{-1} \; N
\end{equation}

Calculate final velocity,

\begin{equation}
\vec{\textrm{v}_f} = \vec{\textrm{v}_i}+\vec{\textrm{a}}\textrm{t} \\
\vec{\textrm{v}_f}= 20+(5\times10) \\
\vec{\textrm{v}_f}= 70 \ \textrm{ms}^{-1} N
\end{equation}

Calculate displacement,

\begin{equation}
\vec{\textrm{s}} = \vec{\textrm{v}_i}\textrm{t}+\frac{1}{2}{\vec{\textrm{a}}}{\textrm{t}^{2}} \\
\vec{\textrm{s}}=20\times10+\frac{1}{2}{5\times}{10^{2}} \\
\vec{\textrm{s}}=(200+250)\ \textrm{m} \\
\vec{\textrm{s}}=450 \ \textrm{m} \; N
\end{equation}

**Step 4:**

\begin{align}
\textrm{final} \ \textrm{velocity}, \vec{\textrm{v}_f}=70 \ \textrm{ms}^{-1}\ \; N \;\;(Ans) \\
\textrm{displacement} = 84.94 \ \textrm{m} \ \; N \;\; (Ans)
\end{align}

#### Problem 4

An Air Canada plane requires a takeoff speed of $78.5 \ \textrm{ms}^{-1}$ and $1690 \ \textrm{m}$ of runway to reach that speed. Determine the acceleration of this plane and the time required to reach this speed.

#### Solution

**Step 1:** Let's try,

```python
hide_me

display(Latex('Which values are given in this problem?'))

#Create the box to select m=Motion of Equations
a=widgets.Checkbox(
    value=False,
    description=r"$\vec{\textrm{v}_i}$",
    disabled=False
)

b=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{v}_f}$')

c=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{a}}$'
)
d=widgets.Checkbox(
    value=False,
    description=r'$\vec{\textrm{s}}$',
    disabled=False
)

e=widgets.Checkbox(
    value=False,
    description=r'$t$',
    disabled=False
)

#Display the check box
display(a)
display(b)
display(c)
display(d)
display(e)

#create a button to check the answer
button_check = widgets.Button(description="check")
display(button_check)

#Check the answer
def check_button(x):
    if a.value==True and b.value==True and c.value==False and d.value==True and e.value==False:
        display(Latex("Correct. Well done!"))
        display(Latex("Initial velocity is also given as initially plane will start from $0$ $ms^{-1}$"))
    else:
        display(Latex("Wrong one!"))

button_check.on_click(check_button)

```

**Step 2:**

\begin{align}
\textrm{Motion} \ \textrm{equation,  } \vec{\textrm{v}_f} = \vec{\textrm{v}_i}^{2}+2 \vec{\textrm{a}} \; \vec{\textrm{s}} \textrm{ and }
\vec{\textrm{v}_f}=\vec{\textrm{v}_i}+\vec{\textrm{a}} \textrm{t}
\end{align}

**Step 3**

Let's calculate,

```python
hide_me

def q_1(val):
    if val == "acceleration = 1.82 ms\u00b2 and time = 43.06 s":
        display(Latex("Correct!"))
    elif val == ' ':
        None
    else:
        display(Latex("Try Again!"))

display(Latex("What is the plane's required acceleration, and how long was it accelerating?"))

a1 = 'acceleration = 1.91 ms\u00b2 and time = 48.1 s'
a2 = 'acceleration = 1.82 ms\u00b2 and time = 43.06 s'
a3 = 'acceleration = 1.82 ms\u00b2 and time = 45.4 s'

interact(q_1, val = widgets.Dropdown(options=[' ',a1 ,a2, a3 ],value = ' ',description = 'Choose One:',disabled = False));
```

### Exercise

 **Question 1**:
 A car starting from rest in straight moves with uniform acceleration of $5 \ \textrm{ms}^{-2}$. What will be the velocity while crossing a person at a distance $40 \ \textrm{m}$? (Ans: $20 \ \textrm{ms}^{-1}$)

 **Question 2**:
 A bike accelerates uniformly from rest to a speed of $1 \ \frac{\textrm{km}}{\textrm{min}}$ over a distance of $65 \ \textrm{m}$. Determine the acceleration of the bike. (Ans: $2.137 \ \textrm{ms}^{-1}$)

 **Question 3**:
 A bullet leaves a rifle with a velocity of $521 \ \textrm{ms}^{-1}$. While accelerating through the barrel of the rifle, the bullet moves a distance of $0.840 \ \textrm{m}$. Determine the acceleration of the bullet (Consider a uniform acceleration). (Ans: $1.62 \times 10^{5} \ \textrm{ms}^{-1}$)

 **Question 4**:
 The Velocity of a jeep decreases uniformly from $35 \ \textrm{ms}^{-1}$ and after $8 \ \textrm{s}$ it becomes $10 \ \textrm{ms}^{-1}$. Find the acceleration of the car? (Ans: $-3.125 \ \textrm{ms}^{-2}$, You do not have worry about the '-' sign. It defines the direction as velocity is dreceasing)

## Conclusion

In this notebook, we introduced two important concepts of motion: Uniform and Uniformly accelerated motion. We demonstrated how motion is related to distance, speed, velocity and acceleration. We also showed the various linear relationships between displacement, velocity and uniform acceleration using both the equations of motion, and the graphs of those functions. Using these equations, we then demonstrated several cases where they we can apply the ideas of uniform motion and uniformly accelerated motion to solve classic physics problems. This notebook serves as an introduction to the concept of uniform and uniformly accelerated motion, and will allow you to solve a great many problems using these concepts, and should act as a reasonable primer for more complex kinematic problems.

[](https://github.com/callysto/curriculum-notebooks/blob/master/LICENSE.md)