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

To toggle on/off the raw code, click here.

```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
```

# Uniform Motion and Uniformly Accelerated Motion

Grade 11

  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 travelled 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
%%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("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));
```

    'Calculate the distance and displacement at point B?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Distance: 2m and Displacement: 2m', '…

```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("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));
```

    'Calculate the distance and displacement at point E?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Distance: 9m and Displacement: 7m', '…

### 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("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));
```

    'Calculate the speed and velocity at point C?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Speed: 0.75 m/s and Velocity: 0.55 m/…

```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("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));
```

    'Calculate the speed and velocity at point D?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Speed: 0.80 m/s and Velocity: 0.60 m/…

### 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("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));
```

    'Calculate the acceleration at point B?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Acceleration: 0 m/s² East', 'Accelera…

```python
hide_me

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

display("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));
```

    'Does a change in direction imply that there was acceleration?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'Yes', 'No'), value=' '), Output()), _…

Why?

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'To change direction is to change your…

### 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{m/s})$')

# 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));
```

    interactive(children=(Dropdown(description='Choose One:', options=(' ', '0', 'Constant', 'Variable'), value=' …

### Uniform Accelerated Motion

```python
hide_me

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

```python
%%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{m/s})$');
ax3.set_ylabel (r'$\mathrm{Acceleration} \ (\mathrm{m/s}^{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{m/s}$ to $38.8 \ \textrm{m/s}$ in $3.1$ seconds. Calculate the distance travelled by the car.

#### Solution

**Step 1:**

Given,

$\textrm{initial} \ \textrm{velocity}, \vec{\textrm{v}_i} = 16 \ \textrm{m/s}$

$\textrm{final} \ \textrm{velocity}, \vec{\textrm{v}_f} = 38.8 \ \textrm{m/s}$

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

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

**Step 2:**

Try yourself!

```python
hide_me

display('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)

```

    'Which equation can be chosen to solve this problem?'

    Checkbox(value=False, description='$\\vec{\\textrm{v}_f}=\\vec{\\textrm{v}_i}+\\vec{\\textrm{a}} \\textrm{t}$'…

    Checkbox(value=False, description='$\\vec{\\textrm{s}} = (\\frac{\\vec{\\textrm{v}_i}+\\vec{\\textrm{v}_f}}{2}…

    Checkbox(value=False, description='$\\vec{\\textrm{s}} = \\vec{\\textrm{v}_i}\\textrm{t}+\\frac{1}{2}\\vec{\\t…

    Checkbox(value=False, description='$\\vec{\\textrm{v}_f} = \\vec{\\textrm{v}_i}^{2}+2 \\vec{\\textrm{a}} \\; \…

    Button(description='check', style=ButtonStyle())

```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("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));
```

    'What is your final displacement?'

    interactive(children=(Dropdown(description='Choose One:', options=(' ', '84.94 m', '65.26 m', '89.5 m'), value…

#### Problem 3

A racing car is traveling with a velocity of $72 \ \textrm{km/h}$ N and accelerates at $5 \ \textrm{m/s}^{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{km/h}$ N

$\textrm{acceleration, } \vec{\textrm{a}} = 5 \ \textrm{m/s}^{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('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)

```

    'Which equations can be chosen to solve this problem?'

    Checkbox(value=False, description='$\\vec{\\textrm{v}_f}=\\vec{\\textrm{v}_i}+\\vec{\\textrm{a}} \\textrm{t}$'…

    Checkbox(value=False, description='$\\vec{\\textrm{s}} = (\\frac{\\vec{\\textrm{v}_i}+\\vec{\\textrm{v}_f}}{2}…

    Checkbox(value=False, description='$\\vec{\\textrm{s}} = \\vec{\\textrm{v}_i}\\textrm{t}+\\frac{1}{2}\\vec{\\t…

    Checkbox(value=False, description='$\\vec{\\textrm{v}_f} = \\vec{\\textrm{v}_i}^{2}+2 \\vec{\\textrm{a}} \\; \…

    Button(description='check', style=ButtonStyle())

**Step 3:**

$$
\begin{equation}
\textrm{initial} \ \textrm{velocity,} \vec{\textrm{v}_i} = 72 \ \textrm{km/h} N = \frac{72\times1000}{3600} \ \textrm{m/s} = 20 \ \textrm{m/s} \; 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{m/s} 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{m/s}\ \; 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{m/s}$ 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('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)

```

    'Which values are given in this problem?'

    Checkbox(value=False, description='$\\vec{\\textrm{v}_i}$')

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

    Checkbox(value=False, description='$\\vec{\\textrm{a}}$')

    Checkbox(value=False, description='$\\vec{\\textrm{s}}$')

    Checkbox(value=False, description='$t$')

    Button(description='check', style=ButtonStyle())

**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("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));
```

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

    interactive(children=(Dropdown(description='Choose One:', options=(' ', 'acceleration = 1.91 ms² and time = 48…

### 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 decreasing)

## 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)


##### Copyright 2020 The TensorFlow Authors.

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

# Quantum data

    View on TensorFlow.org

    Run in Google Colab

    View source on GitHub

    Download notebook

Building off of the comparisons made in the [MNIST](https://www.tensorflow.org/quantum/tutorials/mnist) tutorial, this tutorial explores the recent work of [Huang et al.](https://arxiv.org/abs/2011.01938) that shows how different datasets affect performance comparisons. In the work, the authors seek to understand how and when classical machine learning models can learn as well as (or better than) quantum models. The work also showcases an empirical performance separation between classical and quantum machine learning model via a carefully crafted dataset. You will:

1.   Prepare a reduced dimension Fashion-MNIST dataset.
2.   Use quantum circuits to re-label the dataset and compute Projected Quantum Kernel features (PQK).
3.   Train a classical neural network on the re-labeled dataset and compare the performance with a model that has access to the PQK features.

## Setup

```python
!pip -q install tensorflow==2.3.1 tensorflow-quantum
```

```python
import cirq
import sympy
import numpy as np
import tensorflow as tf
import tensorflow_quantum as tfq

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
np.random.seed(1234)
```

## 1. Data preparation

You will begin by preparing the fashion-MNIST dataset for running on a quantum computer.

### 1.1 Download fashion-MNIST

The first step is to get the traditional fashion-mnist dataset. This can be done using the `tf.keras.datasets` module.

```python
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

# Rescale the images from [0,255] to the [0.0,1.0] range.
x_train, x_test = x_train[..., np.newaxis]/255.0, x_test[..., np.newaxis]/255.0

print("Number of original training examples:", len(x_train))
print("Number of original test examples:", len(x_test))
```

    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
    32768/29515 [=================================] - 0s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
    26427392/26421880 [==============================] - 1s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
    8192/5148 [===============================================] - 0s 0us/step
    Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
    4423680/4422102 [==============================] - 0s 0us/step
    Number of original training examples: 60000
    Number of original test examples: 10000

Filter the dataset to keep just the T-shirts/tops and dresses, remove the other classes. At the same time convert the label, `y`, to boolean: True for 0 and False for 3.

```python
def filter_03(x, y):
    keep = (y == 0) | (y == 3)
    x, y = x[keep], y[keep]
    y = y == 0
    return x,y
```

```python
x_train, y_train = filter_03(x_train, y_train)
x_test, y_test = filter_03(x_test, y_test)

print("Number of filtered training examples:", len(x_train))
print("Number of filtered test examples:", len(x_test))
```

    Number of filtered training examples: 12000
    Number of filtered test examples: 2000

```python
print(y_train[0])

plt.imshow(x_train[0, :, :, 0])
plt.colorbar()
```

### 1.2 Downscale the images

Just like the MNIST example, you will need to downscale these images in order to be within the boundaries for current quantum computers. This time however you will use a PCA transformation to reduce the dimensions instead of a `tf.image.resize` operation.

```python
def truncate_x(x_train, x_test, n_components=10):
  """Perform PCA on image dataset keeping the top `n_components` components."""
  n_points_train = tf.gather(tf.shape(x_train), 0)
  n_points_test = tf.gather(tf.shape(x_test), 0)

  # Flatten to 1D
  x_train = tf.reshape(x_train, [n_points_train, -1])
  x_test = tf.reshape(x_test, [n_points_test, -1])

  # Normalize.
  feature_mean = tf.reduce_mean(x_train, axis=0)
  x_train_normalized = x_train - feature_mean
  x_test_normalized = x_test - feature_mean

  # Truncate.
  e_values, e_vectors = tf.linalg.eigh(
      tf.einsum('ji,jk->ik', x_train_normalized, x_train_normalized))
  return tf.einsum('ij,jk->ik', x_train_normalized, e_vectors[:,-n_components:]), \
    tf.einsum('ij,jk->ik', x_test_normalized, e_vectors[:, -n_components:])
```

```python
DATASET_DIM = 10
x_train, x_test = truncate_x(x_train, x_test, n_components=DATASET_DIM)
print(f'New datapoint dimension:', len(x_train[0]))
```

    New datapoint dimension: 10

The last step is to reduce the size of the dataset to just 1000 training datapoints and 200 testing datapoints.

```python
N_TRAIN = 1000
N_TEST = 200
x_train, x_test = x_train[:N_TRAIN], x_test[:N_TEST]
y_train, y_test = y_train[:N_TRAIN], y_test[:N_TEST]
```

```python
print("New number of training examples:", len(x_train))
print("New number of test examples:", len(x_test))
```

    New number of training examples: 1000
    New number of test examples: 200

## 2. Relabeling and computing PQK features

You will now prepare a "stilted" quantum dataset by incorporating quantum components and re-labeling the truncated fashion-MNIST dataset you've created above. In order to get the most seperation between quantum and classical methods, you will first prepare the PQK features and then relabel outputs based on their values.

### 2.1 Quantum encoding and PQK features
You will create a new set of features, based on `x_train`, `y_train`, `x_test` and `y_test` that is defined to be the 1-RDM on all qubits of:

$V(x_{\text{train}} / n_{\text{trotter}}) ^ {n_{\text{trotter}}} U_{\text{1qb}} | 0 \rangle$

Where $U_\text{1qb}$ is a wall of single qubit rotations and $V(\hat{\theta}) = e^{-i\sum_i \hat{\theta_i} (X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1})}$

First, you can generate the wall of single qubit rotations:

```python
def single_qubit_wall(qubits, rotations):
  """Prepare a single qubit X,Y,Z rotation wall on `qubits`."""
  wall_circuit = cirq.Circuit()
  for i, qubit in enumerate(qubits):
    for j, gate in enumerate([cirq.X, cirq.Y, cirq.Z]):
      wall_circuit.append(gate(qubit) ** rotations[i][j])

  return wall_circuit
```

You can quickly verify this works by looking at the circuit:

```python
SVGCircuit(single_qubit_wall(
    cirq.GridQubit.rect(1,4), np.random.uniform(size=(4, 3))))
```

Next you can prepare $V(\hat{\theta})$ with the help of `tfq.util.exponential` which can exponentiate any commuting `cirq.PauliSum` objects:

```python
def v_theta(qubits):
  """Prepares a circuit that generates V(\theta)."""
  ref_paulis = [
      cirq.X(q0) * cirq.X(q1) + \
      cirq.Y(q0) * cirq.Y(q1) + \
      cirq.Z(q0) * cirq.Z(q1) for q0, q1 in zip(qubits, qubits[1:])
  ]
  exp_symbols = list(sympy.symbols('ref_0:'+str(len(ref_paulis))))
  return tfq.util.exponential(ref_paulis, exp_symbols), exp_symbols
```

This circuit might be a little bit harder to verify by looking at, but you can still examine a two qubit case to see what is happening:

```python
test_circuit, test_symbols = v_theta(cirq.GridQubit.rect(1, 2))
print(f'Symbols found in circuit:{test_symbols}')
SVGCircuit(test_circuit)
```

    Symbols found in circuit:[ref_0]

Now you have all the building blocks you need to put your full encoding circuits together:

```python
def prepare_pqk_circuits(qubits, classical_source, n_trotter=10):
  """Prepare the pqk feature circuits around a dataset."""
  n_qubits = len(qubits)
  n_points = len(classical_source)

  # Prepare random single qubit rotation wall.
  random_rots = np.random.uniform(-2, 2, size=(n_qubits, 3))
  initial_U = single_qubit_wall(qubits, random_rots)

  # Prepare parametrized V
  V_circuit, symbols = v_theta(qubits)
  exp_circuit = cirq.Circuit(V_circuit for t in range(n_trotter))

  # Convert to `tf.Tensor`
  initial_U_tensor = tfq.convert_to_tensor([initial_U])
  initial_U_splat = tf.tile(initial_U_tensor, [n_points])

  full_circuits = tfq.layers.AddCircuit()(
      initial_U_splat, append=exp_circuit)
  # Replace placeholders in circuits with values from `classical_source`.
  return tfq.resolve_parameters(
      full_circuits, tf.convert_to_tensor([str(x) for x in symbols]),
      tf.convert_to_tensor(classical_source*(n_qubits/3)/n_trotter))
```

Chooe some qubits and prepare the data encoding circuits:

```python
qubits = cirq.GridQubit.rect(1, DATASET_DIM + 1)
q_x_train_circuits = prepare_pqk_circuits(qubits, x_train)
q_x_test_circuits = prepare_pqk_circuits(qubits, x_test)
```

Next, compute the PQK features based on the 1-RDM of the dataset circuits above and store the results in `rdm`, a `tf.Tensor` with shape `[n_points, n_qubits, 3]`. The entries in `rdm[i][j][k]` = $\langle \psi_i | OP^k_j | \psi_i \rangle$ where `i` indexes over datapoints, `j` indexes over qubits and `k` indexes over $\lbrace \hat{X}, \hat{Y}, \hat{Z} \rbrace$ .

```python
def get_pqk_features(qubits, data_batch):
  """Get PQK features based on above construction."""
  ops = [[cirq.X(q), cirq.Y(q), cirq.Z(q)] for q in qubits]
  ops_tensor = tf.expand_dims(tf.reshape(tfq.convert_to_tensor(ops), -1), 0)
  batch_dim = tf.gather(tf.shape(data_batch), 0)
  ops_splat = tf.tile(ops_tensor, [batch_dim, 1])
  exp_vals = tfq.layers.Expectation()(data_batch, operators=ops_splat)
  rdm = tf.reshape(exp_vals, [batch_dim, len(qubits), -1])
  return rdm
```

```python
x_train_pqk = get_pqk_features(qubits, q_x_train_circuits)
x_test_pqk = get_pqk_features(qubits, q_x_test_circuits)
print('New PQK training dataset has shape:', x_train_pqk.shape)
print('New PQK testing dataset has shape:', x_test_pqk.shape)
```

    New PQK training dataset has shape: (1000, 11, 3)
    New PQK testing dataset has shape: (200, 11, 3)

### 2.2 Re-labeling based on PQK features
Now that you have these quantum generated features in `x_train_pqk` and `x_test_pqk`, it is time to re-label the dataset. To achieve maximum seperation between quantum and classical performance you can re-label the dataset based on the spectrum information found in `x_train_pqk` and `x_test_pqk`.

Note: This preparation of your dataset to explicitly maximize the seperation in performance between the classical and quantum models might feel like cheating, but it provides a **very** important proof of existance for datasets that are hard for classical computers and easy for quantum computers to model. There would be no point in searching for quantum advantage in QML if you couldn't first create something like this to demonstrate advantage.

```python
def compute_kernel_matrix(vecs, gamma):
  """Computes d[i][j] = e^ -gamma * (vecs[i] - vecs[j]) ** 2 """
  scaled_gamma = gamma / (
      tf.cast(tf.gather(tf.shape(vecs), 1), tf.float32) * tf.math.reduce_std(vecs))
  return scaled_gamma * tf.einsum('ijk->ij',(vecs[:,None,:] - vecs) ** 2)

def get_spectrum(datapoints, gamma=1.0):
  """Compute the eigenvalues and eigenvectors of the kernel of datapoints."""
  KC_qs = compute_kernel_matrix(datapoints, gamma)
  S, V = tf.linalg.eigh(KC_qs)
  S = tf.math.abs(S)
  return S, V
```

```python
S_pqk, V_pqk = get_spectrum(
    tf.reshape(tf.concat([x_train_pqk, x_test_pqk], 0), [-1, len(qubits) * 3]))

S_original, V_original = get_spectrum(
    tf.cast(tf.concat([x_train, x_test], 0), tf.float32), gamma=0.005)

print('Eigenvectors of pqk kernel matrix:', V_pqk)
print('Eigenvectors of original kernel matrix:', V_original)
```

    Eigenvectors of pqk kernel matrix: tf.Tensor(
    [[ 0.02077353 -0.015596   -0.01320663 ... -0.05652976 -0.01059049
       0.02284898]
     [ 0.01186311 -0.04885919  0.02122526 ...  0.00108375 -0.71264434
       0.03107676]
     [ 0.02042142  0.01409236  0.0090412  ...  0.05545552  0.01048413
       0.02915451]
     ...
     [-0.0792932  -0.00414834 -0.02020384 ...  0.11810818 -0.03445855
       0.04563508]
     [-0.07588085 -0.02885237  0.00121487 ...  0.06278482 -0.06508292
       0.04250367]
     [-0.06047752 -0.02858609  0.01928959 ... -0.02261813 -0.01793749
       0.04086718]], shape=(1200, 1200), dtype=float32)
    Eigenvectors of original kernel matrix: tf.Tensor(
    [[ 0.03835681  0.0283473  -0.01169789 ...  0.02343717  0.0211248
       0.03206972]
     [-0.04018159  0.00888097 -0.01388255 ...  0.00582427  0.717551
       0.02881948]
     [-0.0166719   0.01350376 -0.03663862 ...  0.02467175 -0.00415936
       0.02195409]
     ...
     [-0.03015648 -0.01671632 -0.01603392 ...  0.00100583 -0.00261221
       0.02365689]
     [ 0.0039777  -0.04998879 -0.00528336 ...  0.01560401 -0.04330755
       0.02782002]
     [-0.01665728 -0.00818616 -0.0432341  ...  0.00088256  0.00927396
       0.01875088]], shape=(1200, 1200), dtype=float32)

Now you have everything you need to re-label the dataset! Now you can consult with the flowchart to better understand how to maximize performance seperation when re-labeling the dataset:

In order to maximize the seperation between quantum and classical models, you will attempt to maximize the geometric difference between the original dataset and the PQK features kernel matrices $g(K_1 || K_2) = \sqrt{ || \sqrt{K_2} K_1^{-1} \sqrt{K_2} || _\infty}$ using `S_pqk, V_pqk` and `S_original, V_original`. A large value of $g$ ensures that you initially move to the right in the flowchart down towards a prediction advantage in the quantum case.

Note: Computing quantities for $s$ and $d$ are also very useful when looking to better understand performance seperations. In this case ensuring a large $g$ value is enough to see performance seperation.

```python
def get_stilted_dataset(S, V, S_2, V_2, lambdav=1.1):
  """Prepare new labels that maximize geometric distance between kernels."""
  S_diag = tf.linalg.diag(S ** 0.5)
  S_2_diag = tf.linalg.diag(S_2 / (S_2 + lambdav) ** 2)
  scaling = S_diag @ tf.transpose(V) @ \
            V_2 @ S_2_diag @ tf.transpose(V_2) @ \
            V @ S_diag

  # Generate new lables using the largest eigenvector.
  _, vecs = tf.linalg.eig(scaling)
  new_labels = tf.math.real(
      tf.einsum('ij,j->i', tf.cast(V @ S_diag, tf.complex64), vecs[-1])).numpy()
  # Create new labels and add some small amount of noise.
  final_y = new_labels > np.median(new_labels)
  noisy_y = (final_y ^ (np.random.uniform(size=final_y.shape) > 0.95))
  return noisy_y
```

```python
y_relabel = get_stilted_dataset(S_pqk, V_pqk, S_original, V_original)
y_train_new, y_test_new = y_relabel[:N_TRAIN], y_relabel[N_TRAIN:]
```

## 3. Comparing models
Now that you have prepared your dataset it is time to compare model performance. You will create two small feedforward neural networks and compare performance when they are given access to the PQK features found in `x_train_pqk`.

### 3.1 Create PQK enhanced model
Using standard `tf.keras` library features you can now create and a train a model on the `x_train_pqk` and `y_train_new` datapoints:

```python
#docs_infra: no_execute
def create_pqk_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(32, activation='sigmoid', input_shape=[len(qubits) * 3,]))
    model.add(tf.keras.layers.Dense(16, activation='sigmoid'))
    model.add(tf.keras.layers.Dense(1))
    return model

pqk_model = create_pqk_model()
pqk_model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(learning_rate=0.003),
              metrics=['accuracy'])

pqk_model.summary()
```

    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense (Dense)                (None, 32)                1088
    _________________________________________________________________
    dense_1 (Dense)              (None, 16)                528
    _________________________________________________________________
    dense_2 (Dense)              (None, 1)                 17
    =================================================================
    Total params: 1,633
    Trainable params: 1,633
    Non-trainable params: 0
    _________________________________________________________________

```python
#docs_infra: no_execute
pqk_history = pqk_model.fit(tf.reshape(x_train_pqk, [N_TRAIN, -1]),
          y_train_new,
          batch_size=32,
          epochs=1000,
          verbose=0,
          validation_data=(tf.reshape(x_test_pqk, [N_TEST, -1]), y_test_new))
```

### 3.2 Create a classical model
Similar to the code above you can now also create a classical model that doesn't have access to the PQK features in your stilted dataset. This model can be trained using `x_train` and `y_label_new`.

```python
#docs_infra: no_execute
def create_fair_classical_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(32, activation='sigmoid', input_shape=[DATASET_DIM,]))
    model.add(tf.keras.layers.Dense(16, activation='sigmoid'))
    model.add(tf.keras.layers.Dense(1))
    return model

model = create_fair_classical_model()
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(learning_rate=0.03),
              metrics=['accuracy'])

model.summary()
```

    Model: "sequential_1"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense_3 (Dense)              (None, 32)                352
    _________________________________________________________________
    dense_4 (Dense)              (None, 16)                528
    _________________________________________________________________
    dense_5 (Dense)              (None, 1)                 17
    =================================================================
    Total params: 897
    Trainable params: 897
    Non-trainable params: 0
    _________________________________________________________________

```python
#docs_infra: no_execute
classical_history = model.fit(x_train,
          y_train_new,
          batch_size=32,
          epochs=1000,
          verbose=0,
          validation_data=(x_test, y_test_new))
```

### 3.3 Compare performance
Now that you have trained the two models you can quickly plot the performance gaps in the validation data between the two. Typically both models will achieve > 0.9 accuaracy on the training data. However on the validation data it becomes clear that only the information found in the PQK features is enough to make the model generalize well to unseen instances.

```python
#docs_infra: no_execute
plt.figure(figsize=(10,5))
plt.plot(classical_history.history['accuracy'], label='accuracy_classical')
plt.plot(classical_history.history['val_accuracy'], label='val_accuracy_classical')
plt.plot(pqk_history.history['accuracy'], label='accuracy_quantum')
plt.plot(pqk_history.history['val_accuracy'], label='val_accuracy_quantum')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
```

Success: You have engineered a stilted quantum dataset that can intentionally defeat classical models in a fair (but contrived) setting. Try comparing results using other types of classical models. The next step is to try and see if you can find new and interesting datasets that can defeat classical models without needing to engineer them yourself!

## 4. Important conclusions

There are several important conclusions you can draw from this and the [MNIST](https://www.tensorflow.org/quantum/tutorials/mnist) experiments:

1. It's very unlikely that the quantum models of today will beat classical model performance on classical data. Especially on today's classical datasets that can have upwards of a million datapoints.

2. Just because the data might come from a hard to classically simulate quantum circuit, doesn't necessarily make the data hard to learn for a classical model.

3. Datasets (ultimately quantum in nature) that are easy for quantum models to learn and hard for classical models to learn do exist, regardless of model architecture or training algorithms used.


```python
# This cell is added by sphinx-gallery
# It can be customized to whatever you like
%matplotlib inline
```

Basic tutorial: qubit rotation
==============================

.. meta::
    :property="og:description": To see how PennyLane allows the easy construction and optimization
        of quantum functions, let's consider the 'hello world' of QML: qubit rotation.
    :property="og:image": https://pennylane.ai/qml/_images/bloch.png

.. related::

   tutorial_plugins_hybrid Plugins and Hybrid computation
   tutorial_gaussian_transformation Gaussian transformation
   tutorial_state_preparation Training a quantum circuit with PyTorch

*Author: PennyLane dev team. Last updated: 19 Jan 2021.*

To see how PennyLane allows the easy construction and optimization of quantum functions, let's
consider the simple case of **qubit rotation** the PennyLane version of the 'Hello, world!'
example.

The task at hand is to optimize two rotation gates in order to flip a single
qubit from state $\left|0\right\rangle$ to state $\left|1\right\rangle$.

The quantum circuit
-------------------

In the qubit rotation example, we wish to implement the following quantum circuit:

.. figure:: ../demonstrations/qubit_rotation/rotation_circuit.png
    :align: center
    :width: 40%
    :target: javascript:void(0);

Breaking this down step-by-step, we first start with a qubit in the ground state
$|0\rangle = \begin{bmatrix}1 & 0 \end{bmatrix}^T$,
and rotate it around the x-axis by applying the gate

\begin{align}R_x(\phi_1) = e^{-i \phi_1 \sigma_x /2} =
    \begin{bmatrix} \cos \frac{\phi_1}{2} &  -i \sin \frac{\phi_1}{2} \\
                   -i \sin \frac{\phi_1}{2} &  \cos \frac{\phi_1}{2}
    \end{bmatrix},\end{align}

and then around the y-axis via the gate

\begin{align}R_y(\phi_2) = e^{-i \phi_2 \sigma_y/2} =
   \begin{bmatrix} \cos \frac{\phi_2}{2} &  - \sin \frac{\phi_2}{2} \\
                   \sin \frac{\phi_2}{2} &  \cos \frac{\phi_2}{2}
   \end{bmatrix}.\end{align}

After these operations the qubit is now in the state

\begin{align}| \psi \rangle = R_y(\phi_2) R_x(\phi_1) | 0 \rangle.\end{align}

Finally, we measure the expectation value $\langle \psi \mid \sigma_z \mid \psi \rangle$
of the Pauli-Z operator

\begin{align}\sigma_z =
   \begin{bmatrix} 1 &  0 \\
                   0 & -1
   \end{bmatrix}.\end{align}

Using the above to calculate the exact expectation value, we find that

\begin{align}\langle \psi \mid \sigma_z \mid \psi \rangle
    = \langle 0 \mid R_x(\phi_1)^\dagger R_y(\phi_2)^\dagger \sigma_z  R_y(\phi_2) R_x(\phi_1) \mid 0 \rangle
    = \cos(\phi_1)\cos(\phi_2).\end{align}

Depending on the circuit parameters $\phi_1$ and $\phi_2$, the
output expectation lies between $1$ (if $\left|\psi\right\rangle = \left|0\right\rangle$)
and $-1$ (if $\left|\psi\right\rangle = \left|1\right\rangle$).

Let's see how we can easily implement and optimize this circuit using PennyLane.

Importing PennyLane and NumPy
-----------------------------

The first thing we need to do is import PennyLane, as well as the wrapped version
of NumPy provided by PennyLane.

```python
import pennylane as qml
from pennylane import numpy as np
```

.. important::

    When constructing a hybrid quantum/classical computational model with PennyLane,
    it is important to **always import NumPy from PennyLane**, not the standard NumPy!

    By importing the wrapped version of NumPy provided by PennyLane, you can combine
    the power of NumPy with PennyLane:

    * continue to use the classical NumPy functions and arrays you know and love
    * combine quantum functions (evaluated on quantum hardware/simulators) and
      classical functions (provided by NumPy)
    * allow PennyLane to automatically calculate gradients of both classical and
      quantum functions

Creating a device
-----------------

Before we can construct our quantum node, we need to initialize a **device**.

.. admonition:: Definition
    :class: defn

    Any computational object that can apply quantum operations and return a measurement value
    is called a quantum **device**.

    In PennyLane, a device could be a hardware device (such as the IBM QX4, via the
    PennyLane-PQ plugin), or a software simulator (such as Strawberry Fields, via the
    PennyLane-SF plugin).

.. tip::

   *Devices are loaded in PennyLane via the function* :func:`~.pennylane.device`

PennyLane supports devices using both the qubit model of quantum computation and devices
using the CV model of quantum computation. In fact, even a hybrid computation containing
both qubit and CV quantum nodes is possible; see the
`hybrid computation example <hybrid_computation_example>` for more details.

For this tutorial, we are using the qubit model, so let's initialize the ``'default.qubit'`` device
provided by PennyLane; a simple pure-state qubit simulator.

```python
dev1 = qml.device("default.qubit", wires=1)
```

For all devices, :func:`~.pennylane.device` accepts the following arguments:

* ``name``: the name of the device to be loaded
* ``wires``: the number of subsystems to initialize the device with

Here, as we only require a single qubit for this example, we set ``wires=1``.

Constructing the QNode
----------------------

Now that we have initialized our device, we can begin to construct a
**quantum node** (or QNode).

.. admonition:: Definition
    :class: defn

    QNodes are an abstract encapsulation of a quantum function, described by a
    quantum circuit. QNodes are bound to a particular quantum device, which is
    used to evaluate expectation and variance values of this circuit.

.. tip::

   *QNodes can be constructed via the* :class:`~.pennylane.QNode`
   *class, or by using the provided* :func:`~.pennylane.qnode` decorator.

First, we need to define the quantum function that will be evaluated in the QNode:

```python
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=0)
    return qml.expval(qml.PauliZ(0))
```

This is a simple circuit, matching the one described above.
Notice that the function ``circuit()`` is constructed as if it were any
other Python function; it accepts a positional argument ``params``, which may
be a list, tuple, or array, and uses the individual elements for gate parameters.

However, quantum functions are a **restricted subset** of Python functions.
For a Python function to also be a valid quantum function, there are some
important restrictions:

* **Quantum functions must contain quantum operations, one operation per line,
  in the order in which they are to be applied.**

  In addition, we must always specify the subsystem the operation applies to,
  by passing the ``wires`` argument; this may be a list or an integer, depending
  on how many wires the operation acts on.

  For a full list of quantum operations, see :doc:`the documentation <introduction/operations>`.

* **Quantum functions must return either a single or a tuple of measured observables**.

  As a result, the quantum function always returns a classical quantity, allowing
  the QNode to interface with other classical functions (and also other QNodes).

  For a full list of observables, see :doc:`the documentation <introduction/operations>`.
  The documentation also provides details on supported :doc:`measurement return types <introduction/measurements>`.

Note

Certain devices may only support a subset of the available PennyLane
    operations/observables, or may even provide additional operations/observables.
    Please consult the documentation for the plugin/device for more details.

Once we have written the quantum function, we convert it into a :class:`~.pennylane.QNode` running
on device ``dev1`` by applying the :func:`~.pennylane.qnode` decorator.
**directly above** the function definition:

```python
@qml.qnode(dev1)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=0)
    return qml.expval(qml.PauliZ(0))
```

Thus, our ``circuit()`` quantum function is now a :class:`~.pennylane.QNode`, which will run on
device ``dev1`` every time it is evaluated.

To evaluate, we simply call the function with some appropriate numerical inputs:

```python
print(circuit([0.54, 0.12]))
```

    0.8515405859048368

Calculating quantum gradients
-----------------------------

The gradient of the function ``circuit``, encapsulated within the ``QNode``,
can be evaluated by utilizing the same quantum
device (``dev1``) that we used to evaluate the function itself.

PennyLane incorporates both analytic differentiation, as well as numerical
methods (such as the method of finite differences). Both of these are done
automatically.

We can differentiate by using the built-in :func:`~.pennylane.grad` function.
This returns another function, representing the gradient (i.e., the vector of
partial derivatives) of ``circuit``. The gradient can be evaluated in the same
way as the original function:

```python
dcircuit = qml.grad(circuit, argnum=0)
```

The function :func:`~.pennylane.grad` itself **returns a function**, representing
the derivative of the QNode with respect to the argument specified in ``argnum``.
In this case, the function ``circuit`` takes one argument (``params``), so we
specify ``argnum=0``. Because the argument has two elements, the returned gradient
is two-dimensional. We can then evaluate this gradient function at any point in the parameter space.

```python
print(dcircuit([0.54, 0.12]))
```

    [array(-0.51043865), array(-0.1026782)]

**A note on arguments**

Quantum circuit functions, being a restricted subset of Python functions,
can also make use of multiple positional arguments and keyword arguments.
For example, we could have defined the above quantum circuit function using
two positional arguments, instead of one array argument:

```python
@qml.qnode(dev1)
def circuit2(phi1, phi2):
    qml.RX(phi1, wires=0)
    qml.RY(phi2, wires=0)
    return qml.expval(qml.PauliZ(0))
```

When we calculate the gradient for such a function, the usage of ``argnum``
will be slightly different. In this case, ``argnum=0`` will return the gradient
with respect to only the first parameter (``phi1``), and ``argnum=1`` will give
the gradient for ``phi2``. To get the gradient with respect to both parameters,
we can use ``argnum=[0,1]``:

```python
dcircuit = qml.grad(circuit2, argnum=[0, 1])
print(dcircuit(0.54, 0.12))
```

    (array(-0.51043865), array(-0.1026782))

Keyword arguments may also be used in your custom quantum function. PennyLane
does **not** differentiate QNodes with respect to keyword arguments,
so they are useful for passing external data to your QNode.

Optimization
------------

.. admonition:: Definition
    :class: defn

    If using the default NumPy/Autograd interface, PennyLane provides a collection
    of optimizers based on gradient descent. These optimizers accept a cost function
    and initial parameters, and utilize PennyLane's automatic differentiation
    to perform gradient descent.

.. tip::

   *See* :doc:`introduction/optimizers` *for details and documentation of available optimizers*

Next, let's make use of PennyLane's built-in optimizers to optimize the two circuit
parameters $\phi_1$ and $\phi_2$ such that the qubit, originally in state
$\left|0\right\rangle$, is rotated to be in state $\left|1\right\rangle$. This is equivalent to measuring a
Pauli-Z expectation value of $-1$, since the state $\left|1\right\rangle$ is an eigenvector
of the Pauli-Z matrix with eigenvalue $\lambda=-1$.

In other words, the optimization procedure will find the weights
$\phi_1$ and $\phi_2$ that result in the following rotation on the Bloch sphere:

.. figure:: ../demonstrations/qubit_rotation/bloch.png
    :align: center
    :width: 70%
    :target: javascript:void(0);

To do so, we need to define a **cost** function. By *minimizing* the cost function, the
optimizer will determine the values of the circuit parameters that produce the desired outcome.

In this case, our desired outcome is a Pauli-Z expectation value of $-1$. Since we
know that the Pauli-Z expectation is bound between $[-1, 1]$, we can define our
cost directly as the output of the QNode:

```python
def cost(x):
    return circuit(x)
```

To begin our optimization, let's choose small initial values of $\phi_1$ and $\phi_2$:

```python
init_params = np.array([0.011, 0.012])
print(cost(init_params))
```

    0.9998675058299389

We can see that, for these initial parameter values, the cost function is close to $1$.

Finally, we use an optimizer to update the circuit parameters for 100 steps. We can use the built-in
:class:`~.pennylane.GradientDescentOptimizer` class:

```python
# initialise the optimizer
opt = qml.GradientDescentOptimizer(stepsize=0.4)

# set the number of steps
steps = 100
# set the initial parameter values
params = init_params

for i in range(steps):
    # update the circuit parameters
    params = opt.step(cost, params)

    if (i + 1) % 5 == 0:
        print("Cost after step {:5d}: {: .7f}".format(i + 1, cost(params)))

print("Optimized rotation angles: {}".format(params))
```

    Cost after step     5:  0.9961778
    Cost after step    10:  0.8974944
    Cost after step    15:  0.1440490
    Cost after step    20: -0.1536720
    Cost after step    25: -0.9152496
    Cost after step    30: -0.9994046
    Cost after step    35: -0.9999964
    Cost after step    40: -1.0000000
    Cost after step    45: -1.0000000
    Cost after step    50: -1.0000000
    Cost after step    55: -1.0000000
    Cost after step    60: -1.0000000
    Cost after step    65: -1.0000000
    Cost after step    70: -1.0000000
    Cost after step    75: -1.0000000
    Cost after step    80: -1.0000000
    Cost after step    85: -1.0000000
    Cost after step    90: -1.0000000
    Cost after step    95: -1.0000000
    Cost after step   100: -1.0000000
    Optimized rotation angles: [7.15266381e-18 3.14159265e+00]

We can see that the optimization converges after approximately 40 steps.

Substituting this into the theoretical result $\langle \psi \mid \sigma_z \mid \psi \rangle = \cos\phi_1\cos\phi_2$,
we can verify that this is indeed one possible value of the circuit parameters that
produces $\langle \psi \mid \sigma_z \mid \psi \rangle=-1$, resulting in the qubit being rotated
to the state $\left|1\right\rangle$.

Note

Some optimizers, such as :class:`~.pennylane.AdagradOptimizer`, have
    internal hyperparameters that are stored in the optimizer instance. These can
    be reset using the :meth:`reset` method.

Continue on to the next tutorial, `gaussian_transformation`, to see a similar example using
continuous-variable (CV) quantum nodes.


# Stability analysis of ICA components for transcriptomic data

Here we propose a short analysis of the stability and the reproductibility of ICA components extracted from several gene expression data sets. We are mainly interested in studying the behavior of transcriptomic data extracted from ["Defining the Biological Basis of Radiomic Phenotypes in Lung Cancer" Grossman et al. 2017](https://elifesciences.org/articles/23421).

```python
%load_ext autoreload
%autoreload 2
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
```

## 0. Load data sets

### Grossman data set

These data sets were extracted from ["Defining the Biological Basis of Radiomic Phenotypes in Lung Cancer" Grossman et al. 2017](https://elifesciences.org/articles/23421).

df1 contains the expression of 21,766 unique genes for 269 patients with Non-small cell lung cancer (NSCLC) treated at the H. Lee Moffitt Cancer Center, Tampa, Florida, USA. df2 contains the expression of the same 21,766 unique genes for 89 patients with Non-small cell lung cancer (NSCLC)treated at MAASTRO clinical, Maastricht, NL. Gene expression values were measured on a custom Rosetta/Merck Affymetrix 2.0 microarray chipset and normalized with the robust multi-array average (RMA) algorithm.

```python
df1 = pd.read_excel("Data sets/data_USA.xlsx" , sheet_name = 'expression' , index_col= 0).transpose()
df2 = pd.read_excel("Data sets/data_UE.xlsx" , sheet_name = 'expression' , index_col= 0).transpose()

#With xlrd >= 2.0.0 you will need to use a different engine since xrld removed support for anything
#other than .xls files (make sure openpyxl is installed). It will usually take a bit of time.
#df1 = pd.read_excel("Data sets/data_USA.xlsx" , engine = 'openpyxl' , sheet_name = 'expression' , index_col= 0).transpose()
#df2 = pd.read_excel("Data sets/data_UE.xlsx" ,  engine = 'openpyxl' , sheet_name = 'expression' , index_col= 0).transpose()

dic = {}
for col in df1.columns:
    dic[col] = int(col.split('.')[1])

df1 = df1.rename(columns = dic)
df1['Dataset'] = ['Grossman USA']*df1.shape[0]
df1 = df1.reset_index().rename(columns = {'index' : 'Samples'}).set_index(['Dataset' , 'Samples'])

df2 = df2.rename(columns = dic)
df2['Dataset'] = ['Grossman UE']*df2.shape[0]
df2 = df2.reset_index().rename(columns = {'index' : 'Samples'}).set_index(['Dataset' , 'Samples'])

df1.head()
```

      3643
      84263
      7171
      2934
      11052
      1241
      6453
      57541
      9349
      11165
      ...
      643669
      1572
      8551
      26784
      26783
      26782
      26779
      26778
      26777
      100132941

      Dataset
      Samples

      Grossman USA
      RadioGenomic-017
      5.205151
      7.097989
      9.559617
      8.396808
      7.603719
      7.990605
      10.044401
      9.054930
      7.383169
      8.177010
      ...
      6.419273
      3.809826
      6.507880
      6.572121
      5.400848
      5.951391
      3.381860
      9.825584
      2.905091
      5.622438

      RadioGenomic-055
      5.615738
      6.585052
      9.777869
      9.082415
      8.639498
      6.781274
      9.541826
      8.866110
      6.422702
      7.196294
      ...
      5.753828
      4.186127
      6.821582
      7.031406
      4.852417
      6.140850
      2.629760
      9.005145
      3.366466
      5.495330

      RadioGenomic-227
      5.679276
      7.747854
      10.648704
      9.127985
      7.369421
      7.203773
      8.972255
      8.328371
      7.269232
      7.449183
      ...
      5.666999
      4.316130
      6.637855
      6.248824
      4.664228
      5.767970
      2.911470
      8.674466
      3.337194
      6.308605

      RadioGenomic-222
      5.317341
      7.196276
      10.949771
      8.098896
      7.639882
      7.971876
      10.159637
      8.667702
      8.474250
      7.271477
      ...
      5.531060
      3.403776
      7.059419
      6.201873
      4.690005
      6.256286
      4.119688
      9.099659
      3.181781
      5.740033

      RadioGenomic-212
      7.196904
      9.346492
      9.673778
      9.358636
      8.741693
      7.616498
      10.376653
      8.701461
      6.601991
      7.344651
      ...
      5.519642
      3.796049
      7.332635
      6.050121
      4.898523
      6.537895
      3.600895
      8.792510
      2.945391
      5.835411

5 rows × 21766 columns

### Lim NSCL data set

This data set was extracted from ["Data Descriptor: A merged lung cancer transcriptome dataset for clinical predictive modeling" Bin Lim et al. 2018](https://www.nature.com/articles/sdata2018136).
It gathers 10 independent GEO cohorts of patients with Non-small cell lung cancer (NSCLC) or normal lung tissues. In total, it contains the expression of 10077 unique genes for 1118 patients with Non-small cell lung cancer (NSCLC) or normal lung tissues. The 10 GEO data sets use the same chip platform (Affymetrix Human Genome U133 Plus 2.0 Array). The expression values were normalized with the frozen Robust Multiarray Analysis algorithm (fRMA).

**Note :** In order to be able to join this data set with the Grossman data set, we converted the official gene symbols to entrez ids. We used [the Database for Annotation, Visualization and Integrated Discovery(DAVID)](https://david.ncifcrf.gov/)

```python
new_data = pd.read_excel("Data sets/new_data.xlsx" , sheet_name=['expression' , 'clinical'] , index_col=0)

#With xlrd >= 2.0.0 you will need to use a different engine since xrld removed support for anything
#other than .xls files (make sure openpyxl is installed). It will usually take a bit of time.
#new_data = pd.read_excel("Data sets/new_data.xlsx" , engine = 'openpyxl' , sheet_name=['expression' , 'clinical'] , index_col=0)

new_data['expression'] = new_data['expression'].drop(columns = 'gene_name').transpose()

temp = new_data['expression'].join(new_data['clinical'][['Dataset' , 'Histology (ADC: adenocarcinoma; LCC: large cell carcinoma; SCC: squamous cell carcinoma)']])
df3 = temp.reset_index().rename(columns = {'index' : 'Samples'}).set_index(['Dataset' , 'Samples'])

del temp , new_data

df3 = df3[df3['Histology (ADC: adenocarcinoma; LCC: large cell carcinoma; SCC: squamous cell carcinoma)'] != 'Healthy'].drop(columns = 'Histology (ADC: adenocarcinoma; LCC: large cell carcinoma; SCC: squamous cell carcinoma)')
df3.head()
```

      1
      2
      144571
      65985
      13
      201651
      51166
      195827
      79719
      22848
      ...
      9183
      55055
      11130
      7789
      158586
      440590
      79699
      7791
      23140
      26009

      Dataset
      Samples

      GSE33356
      GSM494556
      5.234802
      11.922797
      5.031032
      9.141089
      9.934807
      6.868094
      5.038721
      6.845863
      7.877033
      9.201101
      ...
      7.149422
      7.737312
      8.386004
      5.994052
      7.685643
      4.546110
      8.870484
      7.938569
      7.596789
      8.049103

      GSM494557
      5.069035
      11.146006
      4.737330
      8.926558
      4.419542
      3.276470
      7.517926
      6.945466
      7.387878
      9.279903
      ...
      7.307666
      7.693829
      9.986055
      5.808656
      5.968874
      4.979624
      9.177783
      7.976926
      7.044507
      8.192161

      GSM494558
      5.514972
      12.378934
      6.990395
      7.268946
      4.253987
      2.978153
      6.286073
      7.201963
      7.125318
      10.170862
      ...
      6.886556
      7.927103
      8.124558
      5.526909
      6.000892
      4.951939
      8.613027
      8.642108
      7.747791
      7.947040

      GSM494559
      6.871695
      11.853862
      4.589314
      8.874169
      5.911853
      4.412516
      6.304975
      7.731857
      7.300380
      8.649045
      ...
      7.939107
      8.589020
      9.673297
      6.190925
      6.736635
      4.765782
      9.643763
      7.295604
      7.839339
      8.377581

      GSM494560
      6.761781
      11.200515
      5.001184
      9.027746
      5.356248
      3.515208
      5.545497
      7.552539
      7.309306
      9.229319
      ...
      7.833734
      8.689572
      10.304345
      6.090843
      6.703918
      5.821131
      9.187349
      7.557380
      7.320086
      8.827688

5 rows × 10077 columns

We only keep data sets with more than 89 samples to avoid ending up with very noisy ICA components which will compromise the stability analysis.

```python
dataframe = pd.concat([df1, df2 , df3], join = 'inner')
names = list(dataframe.index.unique(0))

exclude = []
print("Data sets in the study : ")
for name in names:
    n_samples = dataframe.loc[name].shape[0]
    if n_samples < 89:
        exclude.append(name)
    else:
       print(name , n_samples)

del df1 , df2 , df3

names = list(set(names) - set(exclude))
```

    Data sets in the study :
    Grossman USA 262
    Grossman UE 89
    GSE19188 91
    GSE28571 100
    GSE50081 181
    GSE31210 226
    GSE18842 91

## 1. Most Stable Transcriptome Dimension (MSTD)

In order to select the number of ICA components we refer to ["Determining the optimal number of independent components for reproducible transcriptomic data analysis" Kairov et al. 2017](https://bmcgenomics.biomedcentral.com/articles/10.1186/s12864-017-4112-9). The idea is to find a trade-off between a sufficiently large number of components to capture all essential information and a restricted dimension to avoid ending up with a lot of very unstable components which only capture noise.

To do so we plot the stability distribution for a wide range of different number of components M = 5, ... , 100 and we observe which choice of M most satisfies the trade-off the most.

```python
#MSTD plot for the data set GSE31210.
from sica.base import MSTD
X = dataframe.loc['GSE31210'].apply(func = lambda x : x - x.mean() , axis = 1).transpose()
MSTD(X , 5 , 100 , 2 , 20 , max_iter = 3000)
#MSTD(X.values , 5 , 100 , 2 , 20 , max_iter = 3000)
```

```python
MSTD = 45
```

## 2. Stabilized ICA decompostion for each data set

Our stabilized ICA algorithm is a copy of the ICASSO algorithm (implemented in MATLAB) (["ICASSO: software for investigating the reliability of ICA estimates by clustering and visualization" Himber et al.](https://www.cs.helsinki.fi/u/ahyvarin/papers/Himberg03.pd) The main idea is to iterate several times the FastICA algorithm (ex: from sklearn), cluster the results (agglomerative hierarchical clustering) and define the final ICA components as the centrotype of each cluster.

```python
from sica.base import StabilizedICA as sICA
```

```python
Sources = []
decomp = sICA(n_components = MSTD , max_iter = 2000 , n_jobs = -1)

for name in names:
    start = time.time()
    X = dataframe.loc[name].apply(func = lambda x : x - x.mean() , axis = 1).transpose()
    #decomp.fit(X.values , n_runs = 30)
    decomp.fit(X , n_runs = 30)
    Sources.append(decomp.S_ )
    end = time.time()
    minutes, seconds = divmod(end - start, 60)
    print(name + " done !" , "running time (min): " + "{:0>2}:{:05.2f}".format(int(minutes),seconds))
```

    Grossman USA done ! running time (min): 00:25.47
    GSE50081 done ! running time (min): 00:14.74
    GSE28571 done ! running time (min): 00:17.53
    GSE19188 done ! running time (min): 00:16.74
    GSE31210 done ! running time (min): 00:14.51
    GSE18842 done ! running time (min): 00:16.21
    Grossman UE done ! running time (min): 00:15.11

## 3. Study of reproducibility - MNN graph

In order to study the reproducibility of ICA components between the four data sets we use the notion of Reciprocal Best Hit (RBH). We consider that a component $i$ extracted from a data set D1 and another component $j$ extracted from a data set D2 are linked if:

\begin{equation}
|\rho_{ij}| = \max \{ |\rho_{ik}| , \forall k \, \in D2 \} = \max \{ |\rho_{lj}| , \forall l \, \in D1 \}
\end{equation}

Here $\rho_{ij}$ represents the Pearson's correlation coefficient between the components $i$ and $j$. We chose to use Pearson's coefficient over non-parametric coefficients such as Kendall's or Spearman's coefficients since it is strongly influenced by extreme values. Yet in our case these extreme values characterize our ICA components .

```python
from sica.mutualknn import MNNgraph
```

**Note :** The layout of the following RBH network is strongly influenced by a random initialisation (Fruchterman-Reingold force-directed algorithm). One will have to run the following result several times until a satisfying result is obtained.

```python
fig , ax = plt.subplots(figsize = (10 , 7))

cg = MNNgraph(data = Sources , names = names , k=1 , weighted = True)
cg.draw(ax=ax, colors = ['r' , 'k' , 'b' , 'g' , 'yellow' , 'purple' , 'brown'] , spacing = 2)

ax.legend(bbox_to_anchor=(1.25 , 1))
```

```python
cg.export_json("stability.json")
```

We can also draw the bipartite RBH graph for the ICA components of two data sets. We display the Pearson correlation coefficient for each link. In the following we display the bipartite graphs for the couples of sets ('Grossman UE' , 'GSE19188') and ('Grossman UE' , 'GSE50081').

```python
fig , axes = plt.subplots(1 , 2 , figsize=(20 , 10))
for i in range(2):
    cg = MNNgraph(data = [Sources[0] , Sources[i+1]] , names = [names[i] , names[i+1]] , k=1 , weighted = True)
    cg.draw(ax=axes[i] , bipartite_graph = True)
```

## 4. Cytoscape visualization of the stability network

In order to visualize the stability network with cytoscape directly on the jupyter notebook we use the [ipycytoscape package](https://github.com/QuantStack/ipycytoscape). Please install it with:
* conda install -c conda-forge ipycytoscape
* pip install ipycytoscape

```python
import ipycytoscape
import json

with open("stability.json") as fi:
    json_file = json.load(fi)

cytoscapeobj = ipycytoscape.CytoscapeWidget()
cytoscapeobj.graph.add_graph_from_json(json_file['elements'])

cytoscapeobj.set_layout(name = 'cose')
```

```python
cytoscapeobj.set_style([{'selector': 'node',
                            'css': {
                                  'content': 'data(name)',
                                  'font-size': '12px',
                                  'text-valign': 'center',
                                  'text-halign': 'center',
                                  'background-color': 'blue',
                                  'color': 'b',
                                }
                        },
                        {'selector': 'edge',
                            'css': {
                                  'curve-style': 'haystack',
                                  'haystack-radius': '0.5',
                                  'opacity': '0.7',
                                  'line-color': '#bbb',
                                  'width': 'mapData(weight , 0 , 1 , 1 , 15)',
                                  'overlay-padding': '3px'
                                }
                        }])
```

```python
cytoscapeobj
```

    CytoscapeWidget(cytoscape_layout={'name': 'cose'}, cytoscape_style=[{'selector': 'node', 'css': {'content': 'd…


```python
class ClassName():
    def __init__(self,):
        print(hagehage)
```

```python
hagehage = 6

c = ClassName()
```

    6

```python
class Myclass:
    def set_value(self, text):
        self.value = text
    def print_value(self):
        print(self.value)
```

```python
if __name__ == "__main__":
    a = Myclass()               # MyClass のインスタンスを生成
    a.set_value("abc")          # 変数 value に文字列 "abc" を代入
    a.print_value()             # abc
```

    abc

```python
class Oya:
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def return_oya(self):
        oya = self.a + self.b
        return oya

class kodomo(Oya):
    def multiple_oya(self):
        ans = self.a * self.b
        return ans
```

```python
o = Oya(2, 3)

k = kodomo(1,2)
k.multiple_oya()
```

    2

```python
class Cournot_bad:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    def product(self):
        self.q1 = (a-c)/b
        self.q2 = (a-c)/b
    def whole_product(self, q1, q2):
        self.q = self.q1 + self.q2
    def price(self, q):
        self.p = self.a - (self.b * self.q)
    def ans(self):
        return self.q1, self.q2, self.q, self.p
```

```python
class Cournot:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    def formula(self):
        print("P =", self.a, "-", self.b, "Q")
        print("C =", self.c, "Q")
    def ans(self):
        self.q1 = (self.a-self.c)/self.b
        self.q2 = (self.a-self.c)/self.b
        self.q = self.q1 + self.q2
        self.p = (self.a + (2 * self.b)) / 3
        return self.q1, self.q2, self.q, self.p
```

```python
cournot = Cournot(400, 10, 5)
cournot.formula()
cournot.ans()
```

    P = 400 - 10 Q
    C = 5 Q

    (39.5, 39.5, 79.0, 140.0)

```python
class Test:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def first(self):
        self.c = self.a + self.b
        self.d = self.a * self.b
    def second(self):
        val = []
        for i in range(10):
            self.first()
            val.append((self.c, self.d))
            self.a = self.c
            self.b = self.d
        return val
```

```python
tes = Test(1, 1)
tes.second()
```

    [(2, 1),
     (3, 2),
     (5, 6),
     (11, 30),
     (41, 330),
     (371, 13530),
     (13901, 5019630),
     (5033531, 69777876630),
     (69782910161, 351229105131280530),
     (351229174914190691, 24509789089304573335878465330)]

```python
l = ['me', 'he', 'she']
for i, name in enumerate(l):
    print(i, name)
```

    0 me
    1 he
    2 she

```python
class Newton:
    def __init__(self, a, tol):
        self.a = a
        self.tol = tol
    def f(self, x):
        return x**2 - x + 1
    def new_slope(self, y):
        import sympy
        z = sympy.Symbol('z')
        der = sympy.diff(self.f(z), z)
        new_slope = der.subs(z, y)
        return new_slope
    def new_x(self, old_x):
        new_slope = self.new_slope(old_x)
        new_x = old_x - (self.f(old_x) / float(new_slope))
        return new_x
    def newton_method_zero(self):
        x = self.a
        loops = []
        while self.f(x) >= self.tol:
            loops.append(x)
            new_x = self.new_x(x)
            x = new_x
        return x, loops
    def newton_method_min(self):
        x = self.a
        loops = []
        while self.new_slope(x) > self.tol:
            loops.append(x)
            new_x = self.new_x(x)
            x = new_x
        return x, loops
```

```python
n = Newton(5, 1e-5)
n.newton_method_min()
```

    (-8.167752356861968,
     [5, 2.6666666666666665, 1.41025641025641, 0.5431563741422893])

```python

```