Propuesta para un Framework Basado En Software Libre

para facilitar el Proceso de Enseñanza-Aprendizaje

en Materias de Ciencias Exactas en Carreras de Grado

```python
from IPython.display import Javascript, display
from ipywidgets.widgets import Layout
from ipywidgets import widgets

def run_all(ev):
    display(Javascript('IPython.notebook.execute_cells_below()'))

button = widgets.Button(description="Ejecutar Todas las Celdas", layout=Layout(width='99%', height="50px"))
button.on_click(run_all)
```

### Ejecutar todas las celdas

```python
display(button)
```

    Button(description='Ejecutar Todas las Celdas', layout=Layout(height='50px', width='99%'), style=ButtonStyle()…

## ¿Quién Soy? - Ezequiel Leonardo Castaño

- Estudiante de ISI en UTN Facultad Regional Rosario
- Programo en Python por más de 5 años como hobby

**Me interesa**
- Inteligencia Artificial
- Data Visualization
- Simulación y modelado
- Aplicación de informática en Educación

## Agenda

- ¿Por qué?
- ¿Dónde?
- ¿Quién?
- ¿Qué?
- ¿Cómo?
- ¿Para quién?
- ¿Junto a qué?
- ¿Antes de qué?

## ¿Por qué y Dónde? - Software Privativo vs Software Libre

```python
from IPython.display import IFrame
```

# Jupyter Education Map

```python
IFrame('https://elc.github.io/jupyter-map', width="100%", height=600)
```

## ¿Quién? - Universidades que ya lo implementan

- 85 Cursos ya lo implementan
- 64 Tienen el material disponible de manera pública
- Algunas de las universidades:
    - University of Notre Dame
    - University of Amsterdam
    - National Institutes of Health (NIH)
    - Universitat de Barcelona
    - Stanford University
    - California Institute of Technology

## ¿Qué? - Pasos para implementar la propuesta

1. **Material de estudio**
2. Experimentación en clase
3. Trabajos prácticos
4. Tareas y asignaciones

## Primer Paso - Material de estudio

- Dinámico
- Editable
- Entendible
- Documentado

## ¿Cómo? Tecnologías

### Demostración

### Corrección de Errores en Vivo

$$ \int_1^\infty \!\frac{1}{x^2}\, dx=\left[\frac{1}{x}\right]_1^\infty=1 $$

### Graficar Funciones y ver como varían - Función Cuadrática

```python
import matplotlib.pyplot as plt
from ipywidgets import interact
import numpy as np
from matplotlib import animation, rc
from matplotlib import style

# Jupyter Specifics
import matplotlib as mpl
from IPython.display import HTML
from ipywidgets.widgets import interact, IntSlider, FloatSlider, Layout
```

```python
plt.style.use('bmh')

%matplotlib inline

mpl.rcParams['figure.figsize'] = (16.0, 6.0)
rc('animation', html='html5')
```

```python
def f1(a, b, c):
    mpl.rcParams['figure.figsize'] = (16.0, 6.0)
    x = np.linspace(-5,5,100)
    y = a*x**2+b*x+c
    plt.title(f"Expresion: $ax^2 + bx + c$ \n $a = {a}, b = {b}, c = {c}$")
    plt.ylim(-1,20)
    plt.xlim(-5,5)
    plt.grid(color='grey', linewidth=0.5)
    plt.plot(x, y)
```

```python
interact(f1, a=FloatSlider(min=-5, max=6, step=0.25, value=1, layout=Layout(width='99%')), b=FloatSlider(min=-5, max=6, step=1, value=0, layout=Layout(width='99%')), c=FloatSlider(min=-5, max=6, step=1, value=1, layout=Layout(width='99%')),);
```

    interactive(children=(FloatSlider(value=1.0, description='a', layout=Layout(width='99%'), max=6.0, min=-5.0, s…

### Graficar Funciones y ver como varían - Función Cuadrática Canónica

```python
def f2(a, b, c):
    mpl.rcParams['figure.figsize'] = (16.0, 6.0)
    x = np.linspace(-5,5,1000)
    y = (a*x+b)**2+c
    plt.title("Expresion: $(ax+b)^2 + c$ \n a = {}, b = {}, c = {}".format(a,b,c))
    plt.ylim(-1,20)
    plt.xlim(-5,5)
    plt.grid(color='grey', linewidth=0.5)
    plt.plot(x, y)
```

```python
interact(f2, a=FloatSlider(min=-5, max=6, step=0.25, value=1, layout=Layout(width='99%')), b=FloatSlider(min=-5, max=6, step=1, value=0, layout=Layout(width='99%')), c=FloatSlider(min=-5, max=6, step=1, value=1, layout=Layout(width='99%')),);
```

    interactive(children=(FloatSlider(value=1.0, description='a', layout=Layout(width='99%'), max=6.0, min=-5.0, s…

### Integración Numérica y Graficación

```python
from matplotlib.patches import Polygon
import scipy.integrate as integrate

def func(x):
    return (x - 3) * (x - 5) * (x - 7) + 85

def f3(a, b):
    mpl.rcParams['figure.figsize'] = (16.0, 6.0)
    x = np.linspace(0, 10)
    y = func(x)

    fig, ax = plt.subplots()
    plt.plot(x, y, linewidth=2)
    plt.ylim(ymin=0)

    # Make the shaded region
    ix = np.linspace(a, b)
    iy = func(ix)
    verts = [(a, 0)] + list(zip(ix, iy)) + [(b, 0)]
    poly = Polygon(verts, facecolor='0.8', edgecolor='0.5')
    ax.add_patch(poly)

    inte = int(integrate.quad(func, a, b)[0])

    plt.text(0.5 * (a + b), 30, r"$\int_a^b f(x)\mathrm{d}x" + f" = {inte}$",
             horizontalalignment='center', fontsize=20)

    ax.set_xticks((a, b))
    ax.set_xticklabels(('$a$', '$b$'))

    plt.title(f"Función: $f(x) = (x - 3)(x - 5)(x - 7) + 85$ \n $a = {a}, b= {b}$")
    plt.show()
```

```python
interact(f3, a=FloatSlider(min=-5, max=10, step=0.25, value=2, layout=Layout(width='99%')), b=FloatSlider(min=-5, max=10, step=0.25, value=9, layout=Layout(width='99%')));
```

    interactive(children=(FloatSlider(value=2.0, description='a', layout=Layout(width='99%'), max=10.0, min=-5.0, …

### Polinomio de Taylor

```python
import sympy as sy
import numpy as np
from sympy.functions import sin,cos
import matplotlib.pyplot as plt

# Factorial function
def factorial(n):
    if n <= 0:
        return 1
    else:
        return n*factorial(n-1)

# Taylor approximation at x0 of the function 'function'
def taylor(function,x0,n):
    i = 0
    p = 0
    while i <= n:
        p = p + (function.diff(x, i).subs(x, x0)) / (factorial(i)) * (x - x0) ** i
        i += 1
    return p
```

```python
x = sy.Symbol('x')
f = sin(x) * x**2

def animate(j):
    if j % 2 == 0:
        return []

    x_lims = [-5,5]
    x1 = np.linspace(x_lims[0],x_lims[1],800)

    plt.xlim(x_lims)
    plt.ylim([-5,5])

    if j == 1:
        plt.plot(x1, np.sin(x1) * x1**2, label='$sin(x) * x^2$')
        return []
    y1 = []
    func = taylor(f,0,j)
    print(j, 'Polinomio de Taylor para n='+str(j), func)
    for k in x1:
        y1.append(func.subs(x,k))
    plt.plot(x1,y1,label='Orden '+str(j))

    plt.xlim(x_lims)
    plt.ylim([-5,5])
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True)
    plt.title('Aproximación por serie de Taylor')
    return []

# Plot results
def plot():
    mpl.rcParams['figure.figsize'] = (12.0, 6.0)
    fig, ax = plt.subplots();
    anim = animation.FuncAnimation(fig, animate, frames=10, interval=500, blit=True);
    return anim
```

```python
anim = plot()
HTML(anim.to_html5_video())
```

### Polinomio de Taylor interactivo

```python
x = sy.Symbol('x')
f = sin(x)

def f4(order):
    mpl.rcParams['figure.figsize'] = (16.0, 6.0)
    x_lims = [-10, 10]
    x1 = np.linspace(x_lims[0],x_lims[1],800)
    plt.plot(x1, np.sin(x1), label='sin of x')
    y1 = []
    func = taylor(f,0,order)
    for k in x1:
        y1.append(func.subs(x,k))
    plt.plot(x1,y1,label='order '+str(order))
    plt.xlim(x_lims)
    plt.ylim([-5,5])
    plt.legend()
    plt.grid(True)
    plt.title('Taylor series approximation')
    plt.show()
```

```python
interact(f4, order=IntSlider(min=1, max=15, step=2, value=1, layout=Layout(width='99%')),);
```

    interactive(children=(IntSlider(value=1, description='order', layout=Layout(width='99%'), max=15, min=1, step=…

### Cónicas

```python
import sympy as sy
from sympy import plot_implicit, Eq
x = sy.Symbol('x')
y = sy.Symbol('y')

def plot_conic(a, b, h, k):
    if a == 0 or b == 0:
        return []
    mpl.rcParams['figure.figsize'] = (10.0, 10.0)
    plot_implicit(Eq((x + h)**2 / a + (y + k)**2 / b, 1), (x, -np.pi, np.pi), (y, -np.pi, np.pi), title="Ecuación: $\\frac{(x+h)^2}{a} + \\frac{(y+k)^2}{b} = 1$")
```

```python
interact(plot_conic, a=FloatSlider(min=-5, max=5, step=1, value=2, layout=Layout(width='99%')),
                     b=FloatSlider(min=-5, max=5, step=1, value=2, layout=Layout(width='99%')),
                     h=FloatSlider(min=-5, max=5, step=1, value=0, layout=Layout(width='99%')),
                     k=FloatSlider(min=-5, max=5, step=1, value=0, layout=Layout(width='99%')));
```

    interactive(children=(FloatSlider(value=2.0, description='a', layout=Layout(width='99%'), max=5.0, min=-5.0, s…

### Ventajas

- Visualización Integrada
- Visualización de los datos
- Problemas de mayor dificultad
- Material interactivo
- Habilidades de computación
- Acompañamiento
- Filosofía Open Source y Open Science
- Colaboración
- Instalación sencilla
- Versatilidad

### Desventajas

- Carga Cognitiva
- Testing
- Orden de Ejecución
- Variables globales
- Borrado accidental
- Errores intimidantes

## ¿Para quién? - Docentes objetivos

- Docentes de ciencias exactas
- Docentes de materias del ciclo básico
- Docentes que quieran implementar la tecnología en clase

### Desafíos

- Esfuerzo inicial para el aprendizaje
- Migración del material existente
- Resistencia a la tecnología

## ¿Junto a qué? - Tecnologías complementarias

### Tradicionales

### Basadas en Video

## ¿Antes de qué? - Próximos Pasos

- Realizar Pruebas Piloto
- Desarrollar metodologías para medir resultados
- Generar documentación

Muchas gracias

Ezequiel Leonardo Castaño

Blog Personal: elc.github.io


# Optimización media-varianza

La **teoría de portafolios** es uno de los avances más importantes en las finanzas modernas e inversiones.
- Apareció por primera vez en un [artículo corto](https://www.math.ust.hk/~maykwok/courses/ma362/07F/markowitz_JF.pdf) llamado "Portfolio Selection" en la edición de Marzo de 1952 de "the Journal of Finance".
- Escrito por un desconocido estudiante de la Universidad de Chicago, llamado Harry Markowitz.
- Escrito corto (sólo 14 páginas), poco texto, fácil de entender, muchas gráficas y unas cuantas referencias.
- No se le prestó mucha atención hasta los 60s.

Finalmente, este trabajo se convirtió en una de las más grandes ideas en finanzas, y le dió a Markowitz el Premio Nobel casi 40 años después.
- Markowitz estaba incidentalmente interesado en los mercados de acciones e inversiones.
- Estaba más bien interesado en entender cómo las personas tomaban sus mejores decisiones cuando se enfrentaban con "trade-offs".
- Principio de conservación de la miseria. O, dirían los instructores de gimnasio: "no pain, no gain".
- Si queremos más de algo, tenemos que perder en algún otro lado.
- El estudio de este fenómeno era el que le atraía a Markowitz.

De manera que nadie se hace rico poniendo todo su dinero en la cuenta de ahorros. La única manera de esperar altos rendimientos es si se toma bastante riesgo. Sin embargo, riesgo significa también la posibilidad de perder, tanto como ganar.

Pero, ¿qué tanto riesgo es necesario?, y ¿hay alguna manera de minimizar el riesgo mientras se maximizan las ganancias?
- Markowitz básicamente cambió la manera en que los inversionistas pensamos acerca de esas preguntas.
- Alteró completamente la práctica de la administración de inversiones.
- Incluso el título de su artículo era innovador. Portafolio: una colección de activos en lugar de tener activos individuales.
- En ese tiempo, un portafolio se refería a una carpeta de piel.
- En el resto de este módulo, nos ocuparemos de la parte analítica de la teoría de portafolios, la cual puede ser resumida en dos frases:
 - No pain, no gain.
 - No ponga todo el blanquillo en una sola bolsa.

**Objetivos:**
- ¿Qué es la línea de asignación de capital?
- ¿Qué es el radio de Sharpe?
- ¿Cómo deberíamos asignar nuestro capital entre un activo riesgoso y un activo libre de riesgo?

*Referencia:*
- Notas del curso "Portfolio Selection and Risk Management", Rice University, disponible en Coursera.
___

## 1. Línea de asignación de capital

### 1.1. Motivación

El proceso de construcción de un portafolio tiene entonces los siguientes dos pasos:
1. Escoger un portafolio de activos riesgosos.
2. Decidir qué tanto de tu riqueza invertirás en el portafolio y qué tanto invertirás en activos libres de riesgo.

Al paso 2 lo llamamos **decisión de asignación de activos**.

Preguntas importantes:
1. ¿Qué es el portafolio óptimo de activos riesgosos?
 - ¿Cuál es el mejor portafolio de activos riesgosos?
 - Es un portafolio eficiente en media-varianza.
2. ¿Qué es la distribución óptima de activos?
 - ¿Cómo deberíamos distribuir nuestra riqueza entre el portafolo riesgoso óptimo y el activo libre de riesgo?
 - Concepto de **línea de asignación de capital**.
 - Concepto de **radio de Sharpe**.

Dos suposiciones importantes:
- Funciones de utilidad media-varianza.
- Inversionista averso al riesgo.

La idea sorprendente que saldrá de este análisis, es que cualquiera que sea la actitud del inversionista de cara al riesgo, el mejor portafolio de activos riesgosos es idéntico para todos los inversionistas.

Lo que nos importará a cada uno de nosotros en particular, es simplemente la desición óptima de asignación de activos.
___

### 1.2. Línea de asignación de capital

Sean:
- $r_s$ el rendimiento del activo riesgoso,
- $r_f$ el rendimiento libre de riesgo, y
- $w$ la fracción invertida en el activo riesgoso.

 Realizar deducción de la línea de asignación de capital en el tablero.

**Tres doritos después...**

#### Línea de asignación de capital (LAC):
$E[r_p]$ se relaciona con $\sigma_p$ de manera afín. Es decir, mediante la ecuación de una recta:

$$E[r_p]=r_f+\frac{E[r_s-r_f]}{\sigma_s}\sigma_p.$$

- La pendiente de la LAC es el radio de Sharpe $\frac{E[r_s-r_f]}{\sigma_s}=\frac{E[r_s]-r_f}{\sigma_s}$,
- el cual nos dice qué tanto rendimiento obtenemos por unidad de riesgo asumido en la tenencia del activo (portafolio) riesgoso.

Ahora, la pregunta es, ¿dónde sobre esta línea queremos estar?
___

### 1.3. Resolviendo para la asignación óptima de capital

Recapitulando de la clase pasada, tenemos las curvas de indiferencia: **queremos estar en la curva de indiferencia más alta posible, que sea tangente a la LAC**.

 Ver en el tablero.

Analíticamente, el problema es

$$\max_{w} \quad E[U(r_p)]\equiv\max_{w} \quad E[r_p]-\frac{1}{2}\gamma\sigma_p^2,$$

donde los puntos $(\sigma_p,E[r_p])$ se restringen a estar en la LAC, esto es $E[r_p]=r_f+\frac{E[r_s-r_f]}{\sigma_s}\sigma_p$ y $\sigma_p=w\sigma_s$. Entonces el problema anterior se puede escribir de la siguiente manera:

$$\max_{w} \quad r_f+wE[r_s-r_f]-\frac{1}{2}\gamma w^2\sigma_s^2.$$

 Encontrar la $w$ que maximiza la anterior expresión en el tablero.

**Tres doritos después...**

La solución es entonces:

$$w^\ast=\frac{E[r_s]-r_f}{\gamma\sigma_s^2}.$$

De manera intuitiva:
- $w^\ast\propto E[r_s-r_f]$: a más exceso de rendimiento que se obtenga del activo riesgoso, más querremos invertir en él.
- $w^\ast\propto \frac{1}{\gamma}$: mientras más averso al riesgo seas, menos querrás invertir en el activo riesgoso.
- $w^\ast\propto \frac{1}{\sigma_s^2}$: mientras más riesgoso sea el activo, menos querrás invertir en él.
___

## 2. Ejemplo de asignación óptima de capital: acciones y billetes de EU

Pongamos algunos números con algunos datos, para ilustrar la derivación que acabamos de hacer.

En este caso, consideraremos:
- **Portafolio riesgoso**: mercado de acciones de EU (representados en algún índice de mercado como el S&P500).
- **Activo libre de riesgo**: billetes del departamento de tesorería de EU (T-bills).

Tenemos los siguientes datos:

$$E[r_{US}]=11.9\%,\quad \sigma_{US}=19.15\%, \quad r_f=1\%.$$

Recordamos que podemos escribir la expresión de la LAC como:

\begin{align}
E[r_p]&=r_f+\left[\frac{E[r_{US}-r_f]}{\sigma_{US}}\right]\sigma_p\\
      &=0.01+\text{S.R.}\sigma_p,
\end{align}

donde $\text{S.R}=\frac{0.119-0.01}{0.1915}\approx0.569$ es el radio de Sharpe (¿qué es lo que es esto?).

Grafiquemos la LAC con estos datos reales:

```python
# Importamos librerías que vamos a utilizar
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
```

```python
# Datos
Ers = 0.119
ss = 0.1915
rf = 0.01
# Radio de Sharpe para este activo
RS = (Ers - rf) / ss
# Vector de volatilidades del portafolio (sugerido: 0% a 50%)
sp = np.linspace(0, 0.5)
# LAC
Erp = RS * sp + rf
```

```python
# Gráfica
plt.figure(figsize=(6, 4))
plt.plot(sp, Erp, lw=1, label='LAC')
plt.plot(0, rf, 'ob', ms=5, label='Libre de riesgo')
plt.plot(ss, Ers, 'or', ms=5, label='Portafolio/activo riesgoso')
plt.legend(loc='best')
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
```

Bueno, y ¿en qué punto de esta línea querríamos estar?
- Pues ya vimos que depende de tus preferencias.
- En particular, de tu actitud de cara al riesgo, medido por tu coeficiente de aversión al riesgo.

Solución al problema de asignación óptima de capital:

$$\max_{w} \quad E[U(r_p)]$$

$$w^\ast=\frac{E[r_s-r_f]}{\gamma\sigma_s^2}$$

Dado que ya tenemos datos, podemos intentar para varios coeficientes de aversión al riesgo:

```python
# importar pandas
import pandas as pd
```

```python
# Crear un DataFrame con los pesos, rendimiento
# esperado y volatilidad del portafolio óptimo
# entre los activos riesgoso y libre de riesgo
# cuyo índice sean los coeficientes de aversión
# al riesgo del 1 al 10 (enteros)
gamma = np.arange(1, 11)
dist_cap = pd.DataFrame({'$\gamma$': gamma,
                         r'$w^{\ast}$': (Ers - rf) / (gamma * ss**2)
                        })
dist_cap
```

      $\gamma$
      $w^{\ast}$

      0
      1
      2.972275

      1
      2
      1.486137

      2
      3
      0.990758

      3
      4
      0.743069

      4
      5
      0.594455

      5
      6
      0.495379

      6
      7
      0.424611

      7
      8
      0.371534

      8
      9
      0.330253

      9
      10
      0.297227

```python
g = 4.5
w_ac = (Ers - rf) / (g * ss**2)
w_ac
```

    0.6605054836346889

¿Cómo se interpreta $w^\ast>1$?
- Cuando $0<w^\ast<1$, entonces $0<1-w^\ast<1$. Lo cual implica posiciones largas en el mercado de activos y en el activo libre de riesgo.
- Por el contrario, cuando $w^\ast>1$, tenemos $1-w^\ast<0$. Lo anterior implica una posición corta en el activo libre de riesgo (suponiendo que se puede) y una posición larga (de más del 100%) en el mercado de activos: apalancamiento.

# Anuncios parroquiales.

## 1. Quiz la siguiente clase.

## 2. Pueden consultar sus calificaciones en el siguiente [enlace](https://docs.google.com/spreadsheets/d/1BwI1Mm7B3xxJ-jQIQEDQ_WdRHyehZrQBpHGd0hY9fU4/edit?usp=sharing)

Created with Jupyter by Esteban Jiménez Rodríguez.


```
#Enable LaTeX printing
from IPython.core.display import *

global MathJax
MathJax = True
def MDPL(string): display(Math(string)) if MathJax else display(Latex(string))

import IPython.core.display
IPython.core.display.HTML("""

div.input {
width: 105ex; /* about 80 chars + buffer */
}

div.text_cell {
width: 105ex /* instead of 100%, */
}

div.text_cell_render {
/*font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;*/
font-family: "Charis SIL", serif; /* Make non-code text serif. */
line-height: 145%; /* added for some line spacing of text. */
width: 105ex; /* instead of 'inherit' for shorter lines */
}

/* Set the size of the headers */
div.text_cell_render h1 {
font-size: 18pt;
}

div.text_cell_render h2 {
font-size: 14pt;
}

.CodeMirror {
 font-family: Consolas, monospace;
 }

""")
```

div.input {
width: 105ex; /* about 80 chars + buffer */
}

div.text_cell {
width: 105ex /* instead of 100%, */
}

div.text_cell_render {
/*font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;*/
font-family: "Charis SIL", serif; /* Make non-code text serif. */
line-height: 145%; /* added for some line spacing of text. */
width: 105ex; /* instead of 'inherit' for shorter lines */
}

/* Set the size of the headers */
div.text_cell_render h1 {
font-size: 18pt;
}

div.text_cell_render h2 {
font-size: 14pt;
}

.CodeMirror {
 font-family: Consolas, monospace;
 }

```
#(Extended) Euclid Algorithm
#Code Block
#Derived from Algorithm by Robert W. Sebesta
def Euclid(a,b,ans=0,pt=0,retA=1):
    """Finds the greatest common divisor of a and b and x,y
    such that a*x+b*y=gcd(a,b)
    Three flags vary the output (Any number may be true):
    ans: If true will print the equation a*x+b*y=gcd(a,b)
    pt: If true will show the table method for finding the solution
    retA: If true will return the list [a,x,b,y,gcd(a,b)]
    """
    Temp=_Euc([1,0,a],[0,1,b],[[['A1','A2','A3'],['B1','B2','B3']],[[1,0,a],[0,1,b]]])
    T=Temp[0]
    if ans:
        print '\n({0})({1})+({2})({3})={4}'.format(a,T[0],b,T[1],T[2])
    if pt:
        for x in Temp[1]: print '{0}\t{1}\t{2}\t{3}\t{4}\t{5}'.format(x[0][0],x[0][1],x[0][2],x[1][0],x[1][1],x[1][2])
        print
    if retA:
        return [a,T[0],b,T[1],T[2]]
def _Euc(a,b,s):
    """Main recursive loop for Extended Euclidean Algorithm"""
    if b[2]==0: return [b[0],b[1],a[2]],s
    if b[2]==1: return b,s
    else:
        Q=a[2]/b[2]
        t=[a[n]-Q*b[n] for n in range(3)]
        a=b
        b=t
        return _Euc(a,b,s+[[a,b]])
def MI(a,b):
    """Returns the multiplicative inverse of a mod b"""
    temp = Euclid(a,b,ans=0,pt=0,retA=1)[1]
    if temp<0:temp+=b
    return temp
```

```
#Chinese Remainder Theorum
#Code Block
pprint=MDPL
def CRT(A,ptable=0):
    """A is a list of lists such that [[a1,a2],[b1,b2],...] is equivalent to
    x = a1 mod a2, x = b1 mod b2,... where a,b,... are integers
    The flag ptable if true will output the work to find the solution"""
    M=reduce(lambda x,y:x*y,[i[1] for i in A])
    if ptable: pprint( 'M={0}'.format(M))
    count=1;Ans=[];outS='';sumS=0
    for i in A:
        Ans=Ans+[[count,i[0],i[1],M/i[1],Euclid(M/i[1],i[1],retA=1)[1]%i[1]]]
        #print 'a({0})={1} m({0})={2} M/M({0})={3} c({0})={4}'.format(Ans[count-1][0],Ans[count-1][1],Ans[count-1][2],Ans[count-1][3],Ans[count-1][4])
        if ptable: pprint('a_{{{0}}}={1},\ m_{{{0}}}={2},\ \\frac{{M}}{{M_{{{0}}}}}={3},\ c_{{{0}}}={4}'.format(Ans[count-1][0],Ans[count-1][1],Ans[count-1][2],Ans[count-1][3],Ans[count-1][4]))
        outS=outS+'({0}*{1}*{2})+'.format(Ans[count-1][3],Ans[count-1][4],Ans[count-1][1])
        sumS=sumS+(Ans[count-1][3]*Ans[count-1][4]*Ans[count-1][1])
        count=count+1
    if ptable:
        pprint( outS[:-1] + ' = {0}'.format(sumS) )
        pprint( '= {0}\ mod\ {1}'.format(sumS%M,M) )
    return sumS%M
#ChRem([[5,7],[7,11],[3,13]]);print
#CRT([[5,8],[3,5]],ptable=1);print
#ChRem([[7,11],[9,7],[2,3],[1,2]])
```

```
#Basic Encryption/Decryption Algorithms
#Code Block
from binascii import *        #Hex conversion
from collections import deque #Queue
from collections import defaultdict as dd #Dynamic Dictionary
from string import Template   #Output formatting
from heapq import heappush, heappop #Minheap
import re

def type2(a):
    try:
        b=type(a)
    except:
        b='NULL'
    return b

def remDups(seq):
    seen = set()
    seen_add = seen.add
    return reduce(lambda x,y:x+y,[ x for x in seq if x not in seen and not seen_add(x)])

class Cipher:
    def __init__(self):
        pass
    def enc(self,s):
        print "No Encryption Algorithm Added"
    def dec(self,s):
        print "No Decryption Algorithm Added"

class MAS(Cipher):
    """Monoalphabetic (Affine) Substitution Cipher"""
    def __init__(self,a,b):
        self.a=a
        self.b=b
        self.an=Euclid(a,95,retA=1)[1]
    def enc(self,s):
        return reduce(lambda x,y:x+y,[chr(((ord(p)-32)*self.a+self.b)%95+32) for p in s])#if (ord(p)>=97 and ord(p)<=122) else p for p in s])
    def dec(self,s):
        return reduce(lambda x,y:x+y,[chr((((ord(p)-32)-self.b)*self.an)%95+32) for p in s])#if (ord(p)>=97 and ord(p)<=122) else p for p in s])

class PF(Cipher):
    """Playfair Cipher"""
    def __init__(self,s):
        Temp=remDups(s.replace('q','')+'abcdefghijklmnoprstuvwxyz')
        Temp2=[]
        for i in range(5):
            Temp2=Temp2+[Temp[i*5:(i+1)*5]]
        self.Table=Temp2
    def printTable(self):
        for i in self.Table:
            print '{0}'.format(str(i))

```

```
#Primality Tests
##Code Block
from random import randint as ri
ppt=MDPL
class PTest:
    def __init__(self):
        pass
    def TryDiv(self,a):
        return [(i,a/i) for i in range(2,int(a**.5)) if a%i==0]
    def Fermat(self,a):
        pass
    def Carmic(self,a):
        pass
def MilRab(n,rep=5,showwork=0):
    if n<=3: return 1
    if n%2==0:
        if showwork: ppt("{0}\ is\ even".format(n))
        return 0
    n1=q=(n-1)
    k=0
    while q%2==0:
        q=(q)/2
        k+=1
    c=pow(2,q,n)
    d,s=q,k #Other explanations use this notation
    if showwork: ppt("{2}-1 = 2^{0} {1}".format(k,q,n)) #Show n-1 factored
    if q==1: return 1
    def _MRHelp(a):
        if pow(a,d,n)==1: return 0
        for i in range(s): #Witness Loop
            temp2=pow(a,2**i*d,n)
            if temp2==n-1: return 0
        return 1
    for temp in range(rep):
        a=ri(2,n-2)
        if _MRHelp(a): return 0
    return 1 #Since nothing says otherwise, return probably prime
```

```
#Factorization
#Code Block
class Factorizer:
    def __init__(self):
        pass
    def TryDiv(self,a):
        return [(i,a/i) for i in range(2,int(a**.5)) if a%i==0]
    def Fermat(self,a):
        ret=[]
        for i in range(1,100):
            temp=(a+i**2)**.5
            if temp==int(temp):
                temp=int(temp)
                ret+=[[temp+i,temp-i]]
        return ret
    def Pollard(self,a):
        pass
    def QuadSieve(self,a):
        pass
```

```
#Pohlig Hellman Discrete Log
#Code Block
#Source: http://www-math.ucdenver.edu/~wcherowi/courses/m5410/phexam.html
from sympy import factorint as fc
ppt=MDPL #LaTeX-style print function
def DLog(g,p,y,ShowWork=0):
    """Prompt for a generator g, prime p, and y=g^x value.
    Essentially finds x
    Compute x using the Pohlig Hellman algorithm."""
    pf=fc(p-1) #Factor p-1
    if ShowWork:
        ppt("Pollig-Hellman\ Discrete\ Log\ Example")
        ppt("Given\ p={0},g={1},\ and\ y={2}\ such\ that\ {1}^x\equiv {2}\mod {0} \equiv {3}\mod {0},\ find\ x:".format(p,g,y,y%p))
        ppt("$p-1={0}-1={1}$".format(p,"".join([str(i[0])+"^"+str(i[1])+" * " for i in pf.iteritems()]))[:-3])
        ppt("Now\ we\ need\ to\ find\ a\ congruency\ for\ each\ factor:")
    x=[]
    for i in pf.iteritems(): #For each factor
        q=i[0] #Factor
        r=i[1] #Power
        if ShowWork:
            print
            ppt("Find\ x_{{{0}}} \mod {1}\ [x_{{{0}}}={2}]:".format(q,q**r,"".join(["c_{{{0}}}*({{{1}}})+".format(ii,q**ii) for ii in range(r)])[:-1]))
        yy=y #Initially set base to y for each factor
        NewCLast=dict((pow(g,ii*(p-1)/q,p),ii%q) for ii in range(1,q+1)) #Dictionary of possible values for c_? to hit
        if ShowWork: ppt("y^{{ k \\frac{{p-1}}{{q^{{c_0}}}} }}\ must\ be\ one\ of\ these:\ {0}".format("".join(["{2}^{{{0}*\\frac{{p-1}}{{{3}}}}} \equiv {1},\ ".format(NewCLast[ii],ii,g,q) for ii in NewCLast.keys()])[:-3] ) )
        xTemp=0 #Using cumulative sum for finding each c_?
        for k in range(r): #For each c_?
            for j in range(1,q+1): #Try values until temp is in the NewCLast dictionary
                j=j%q #Since it would normally start with 0, making 0 last by wrapping with mod
                trypow=j*(p-1)/(q**(k+1)) #Generate power to raise yy by
                temp=pow(yy,trypow,p)
                if temp in NewCLast.keys():
                    j=NewCLast[temp] #Find the power of yy that makes temp
                    xTemp+=(q**k)*j #Add the cumulating sum for x
                    if ShowWork: ppt("$ y^{{ k \\frac{{p-1}}{{q^{{{9}}}}} }} \equiv {0}^{{ {7} \\frac{{{1}}}{{{2}}} }} \equiv {0}^{{{3}}} \equiv {4} \mod {6},\ so\ c_{{{8}}} = {7} $".format(yy,p-1,q**(k+1),trypow,temp,temp-p,p,j,k,k+1))
                    #cLast=[j]
                    if k<r-1: #Skip division if unneccessary
                        if ShowWork: ppt("We found\ c_{{{1}}},\ so\ divide\ {0}\ by\ {3}^{{{2}*c_{{{1}}} }}\ ({3}^{{ -{5}*{4} }} \equiv {6})".format(yy,k,q**k,g,j,q**k, (MI(g,p)**(q**j))%p ) )
                        yy=( yy*MI(g,p)**( q**(k)*(j) ) )%p
                    break
        if ShowWork: ppt("So,\ x_{{{0}}}={1} \mod {2}".format(q,xTemp,q**r))
        x+=[[xTemp,q**r]] #Add equivalence to list for CRT
    if ShowWork:
        print
        ppt("Now\ use\ the\ Chinese\ Remainder\ Theorem:")
    return CRT(x,ptable=ShowWork)

```

```
#P3
#Code Block
from sympy import factorint as fc
from sympy import randprime as rp
from random import randint as ri
from binascii import hexlify
from binascii import unhexlify
def isProbablyPrime(a,b=5):
    """Uses Miller-Rabin to return if a is prime using b repititions"""
    return MilRab(a,rep=b,showwork=0)
def getProbablePrime(a,b,speedup=0):
    """Return a prime of bit length a using b repititions of Miller-Rabin"""
    r=4
    if speedup: r=rp(2**(a-1),2**(a))
    else:
        while not(isProbablyPrime(r,b)):
            r=ri(2**(a-2),2**(a-1))*2-1#int(reduce(lambda x,y:x+y,[str(ri(0,1)) for i in range(a)]),2)
    return r
def Gcd(a,b):
    """Returns gcd of integers a and b"""
    return Euclid(a,b,ans=0,pt=0,retA=1)[4]
def MultiplicativeInverse(a,b):
    """Returns the inverse of a mod b"""
    temp = Euclid(a,b,ans=0,pt=0,retA=1)[1]
    if temp<0:temp+=b
    return temp
def GenerateRSAKeyPair():
    """generate large (probable) primes p and q,
    and appropriate e and d and print them to the screen.
    It would be helpful to allow interaction with a file
    (put the public key information into one file and
    private key information into another file for input
    into later functions)."""
    p=getProbablePrime(1024,5,1)
    q=getProbablePrime(1024,5,1)
    n=p*q
    e=ri(3,2**10+3)
    phiN=(p-1)*(q-1)
    while Gcd(e,phiN)!=1: e=ri(3,2**10+3)
    d=MultiplicativeInverse(e,phiN)
    return (e,n),(d,n)

def RSAEncrypt(p,e):
    e,n=e
    m=int(hexlify(p),16)
    m=hex(pow(m,e,n))[2:-1]
    if len(m)%2==1: m='0'+m
    m=unhexlify(m)
    return m
def RSADecrypt(c,d):
    return RSAEncrypt(c,d)
def PohligHellmanDiscreteLogarithm(g,p,y):
    """Prompt for a generator g, prime p, and y=g^x value.
    Essentially finds x
    Compute x using the Pohlig Hellman algorithm."""
    return DLog(g,p,y)

```

```
TEST=MAS(59,34)
pt="""Gen. 1:27 So God created man in his own image, in the image of God he created him; male and female he created them."""
ct=TEST.enc(pt)
print ct
print TEST.dec(ct)#.replace("[","")
d=Euclid(17,12195156688,retA=1)[1]
n=12195377863
ct=RSAEncrypt("""Hey Alice, isn't RSA cool!?  --Bill""",[17,12195377863])
pt=RSADecrypt(ct,[d,12195377863])
print "\nRSA:"
print ct
print pt
ct=[4048208982,7973981835,2232860160,1228506572,2392521799,2353518709,4207703255,3703820767,250037531,6326907769,3298628742]
for i in ct:
    print pow(i,d,n)
```

    W4l%BwPS]B#HBWHXB|;4eR4XB1elBblB'bvBHElBb1eK4mBblBR'4Bb1eK4BHoBWHXB'4B|;4eR4XB'b1,B1eU4BelXBo41eU4B'4B|;4eR4XBR'41%
    Gen. 1:27 So God created man in his own image, in the image of God he created him; male and female he created them.

    RSA:
    �4�
    V���
    7249815785
    5681832279
    5686118676
    5937131590
    59816655
    5677382953
    5682582307
    5865256094
    6509821119
    1059145107
    6256070068

```
from math import log
def texttoblocks(text_,blocklen_) :
	Map[Sum[#[[i]] 95^(i-1), {i, Length[#]}] & ,Partition[Join[texttonums[text],Table[0,
	{j,Mod[-Length[texttonums[text]],blocklen]}]],blocklen]]
def blocklength(M): return int(log(95,M))
def RSAencode(string_,e_,M_) :
	return pow(texttoblocks(string_,blocklength(M_)),e_,M_)
```


# PHY321: Introduction to Classical Mechanics

**[Morten Hjorth-Jensen](http://mhjgit.github.io/info/doc/web/)**, Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University, USA and Department of Physics, University of Oslo, Norway

 **[Scott Pratt](https://pa.msu.edu/profile/pratts/)**, Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University, USA

 **[Carl Schmidt](https://pa.msu.edu/profile/schmidt/)**, Department of Physics and Astronomy, Michigan State University, USA

Date: **Jan 6, 2020**

Copyright 1999-2020, [Morten Hjorth-Jensen](http://mhjgit.github.io/info/doc/web/). Released under CC Attribution-NonCommercial 4.0 license

## Introduction

Classical mechanics is a topic which has been taught intensively over
several centuries. It is, with its many variants and ways of
presenting the educational material, normally the first **real** physics
course many of us meet and it lays the foundation for further physics
studies. Many of the equations and ways of reasoning about the
underlying laws of motion and pertinent forces, shape our approaches and understanding
of the scientific method and discourse, as well as the way we develop our insights
and deeper understanding about physical systems.

There is a wealth of
well-tested (from both a physics point of view and a pedagogical
standpoint) exercises and problems which can be solved
analytically. However, many of these problems represent idealized and
less realistic situations.  The large majority of these problems are
solved by paper and pencil and are traditionally aimed
at what we normally refer to as continuous models from which we may find an analytical solution.  As a consequence,
when teaching mechanics, it implies that we can seldomly venture beyond an idealized case
in order to develop our understandings and insights about the
underlying forces and laws of motion.

## Numerical Elements
On the other hand, numerical algorithms call for approximate discrete
models and much of the development of methods for continuous models
are nowadays being replaced by methods for discrete models in science and
industry, simply because **much larger classes of problems can be addressed** with discrete models, often  by simpler and more
generic methodologies.

As we will see below, when properly scaling the equations at hand,
discrete models open up for more advanced abstractions and the possibility to
study  real life systems, with the added bonus that we can explore and
deepen our basic understanding of various physical systems

Analytical solutions are as important as before. In addition, such
solutions provide us with invaluable benchmarks and tests for our
discrete models. Such benchmarks, as we will see below, allow us
to discuss possible sources of errors and their behaviors.  And
finally, since most of our models are based on various algorithms from
numerical mathematics, we have a unique oppotunity to gain a deeper
understanding of the mathematical approaches we are using.

With computing and data science as important elements in essentially
all aspects of a modern society, we could  then try to define Computing as
**solving scientific problems using all possible tools, including
symbolic computing, computers and numerical algorithms, and analytical
paper and pencil solutions**.
Computing provides us with the tools to develope our own understanding of the scientific method by enhancing algorithmic thinking.

## Computations and the Scientific Method

The way we will teach this course reflects
this definition of computing. The course contains both classical paper
and pencil exercises as well as  computational projects and exercises. The
hope is that this will allow you to explore the physics of systems
governed by the degrees of freedom of classical mechanics at a deeper
level, and that these insights about the scientific method will help
you to develop a better understanding of how the underlying forces and
equations of motion and how they impact a given system. Furthermore, by introducing various numerical methods
via computational projects and exercises, we aim at developing your competences and skills about these topics.

These competences will enable you to

* understand how algorithms are used to solve mathematical problems,

* derive, verify, and implement algorithms,

* understand what can go wrong with algorithms,

* use these algorithms to construct reproducible scientific outcomes and to engage in science in ethical ways, and

* think algorithmically for the purposes of gaining deeper insights about scientific problems.

All these elements are central for maturing and gaining a better understanding of the modern scientific process *per se*.

The power of the scientific method lies in identifying a given problem
as a special case of an abstract class of problems, identifying
general solution methods for this class of problems, and applying a
general method to the specific problem (applying means, in the case of
computing, calculations by pen and paper, symbolic computing, or
numerical computing by ready-made and/or self-written software). This
generic view on problems and methods is particularly important for
understanding how to apply available, generic software to solve a
particular problem.

*However, verification of algorithms and understanding their limitations requires much of the classical knowledge about continuous models.*

## A well-known examples to illustrate many of the above concepts

Before we venture into a reminder on Python and mechanics relevant applications, let us briefly outline some of the
abovementioned topics using an example many of you may have seen before in for example CMSE201.
A simple algorithm for integration is the Trapezoidal rule.
Integration of a function $f(x)$ by the Trapezoidal Rule is given by following algorithm for an interval $x \in [a,b]$

$$
\int_a^b(f(x) dx = \frac{1}{2}\left [f(a)+2f(a+h)+\dots+2f(b-h)+f(b)\right] +O(h^2),
$$

where $h$ is the so-called stepsize defined by the number of integration points $N$ as $h=(b-a)/(n)$.
Python offers an  extremely versatile programming  environment, allowing for
the inclusion of analytical studies in a numerical program. Here we show an
example code with the **trapezoidal rule**. We use also **SymPy** to evaluate the exact value of the integral and compute the absolute error
with respect to the numerically evaluated one of the integral
$\int_0^1 dx x^2 = 1/3$.
The following code for  the trapezoidal rule allows you  to plot the relative error by comparing with the exact result. By increasing to $10^8$ points one arrives at a region where numerical errors start to accumulate.

```python
%matplotlib inline

from math import log10
import numpy as np
from sympy import Symbol, integrate
import matplotlib.pyplot as plt
# function for the trapezoidal rule
def Trapez(a,b,f,n):
   h = (b-a)/float(n)
   s = 0
   x = a
   for i in range(1,n,1):
       x = x+h
       s = s+ f(x)
   s = 0.5*(f(a)+f(b)) +s
   return h*s
#  function to compute pi
def function(x):
    return x*x
# define integration limits
a = 0.0;  b = 1.0;
# find result from sympy
# define x as a symbol to be used by sympy
x = Symbol('x')
exact = integrate(function(x), (x, a, b))
# set up the arrays for plotting the relative error
n = np.zeros(9); y = np.zeros(9);
# find the relative error as function of integration points
for i in range(1, 8, 1):
    npts = 10**i
    result = Trapez(a,b,function,npts)
    RelativeError = abs((exact-result)/exact)
    n[i] = log10(npts); y[i] = log10(RelativeError);
plt.plot(n,y, 'ro')
plt.xlabel('n')
plt.ylabel('Relative error')
plt.show()
```

## Analyzing the above example
This example shows the potential of combining numerical algorithms with symbolic calculations, allowing us to

* Validate and verify  their  algorithms.

* Including concepts like unit testing, one has the possibility to test and test several or all parts of the code.

* Validation and verification are then included *naturally* and one can develop a better attitude to what is meant with an ethically sound scientific approach.

* The above example allows the student to also test the mathematical error of the algorithm for the trapezoidal rule by changing the number of integration points. The students get **trained from day one to think error analysis**.

* With a Jupyter notebook you can keep exploring similar examples and turn them in as your own notebooks.

In this process we can easily bake in
1. How to structure a code in terms of functions

2. How to make a module

3. How to read input data flexibly from the command line

4. How to create graphical/web user interfaces

5. How to write unit tests (test functions or doctests)

6. How to refactor code in terms of classes (instead of functions only)

7. How to conduct and automate large-scale numerical experiments

8. How to write scientific reports in various formats (LaTeX, HTML)

The conventions and techniques outlined here will save you a lot of time when you incrementally extend software over time from simpler to more complicated problems. In particular, you will benefit from many good habits:
1. New code is added in a modular fashion to a library (modules)

2. Programs are run through convenient user interfaces

3. It takes one quick command to let all your code undergo heavy testing

4. Tedious manual work with running programs is automated,

5. Your scientific investigations are reproducible, scientific reports with top quality typesetting are produced both for paper and electronic devices.

## Teaching team, grading and other practicalities

     Lectures     	                      	                   	    Location

   Monday 3:00-3:50pm    	    Wednesday 3:00-3:50pm    	    Friday 3:00-3:50pm    	    Room 1420 BPS

                     Instructor                    	      Email     	         Office        	 Office phone/cellphone

   [Morten Hjorth-Jensen](https://github.com/mhjensen)    	    hjensen@msu.edu    	    Office: NSCL/FRIB 2131    	    5179087290/5172491375

                 Office Hours

   Monday/Wednesday 4-5:00pm, Room 2131 NSCL/FRIB    	    or immediately after class

Homework Grader 	      Email

   Kasun Senanayaka    	    senanaya@msu.edu

Learning Assistant	      Email

   Dylan R. Smith        	    smithdy6@msu.edu

## Grading and dates

                        Activity                        	 Percentage of total score

   Homeworks, 10 in total and due Wednesdays the week after    	    20%

   First Midterm Project, due Friday February 28               	    25%

   Second  Midterm Project, due Friday April 10                	    25%

   Final Exam, April 29, 5:45-7:45pm                           	    30%

   Extra Credit Assignment (Due Friday April 24)               	    10%

Grading scale

   4.0(90%)         	    3.5(80%)    	    3.0(70%)    	    2.5(60%)    	    2.0(50%)    	    1.5(40%)    	    1.0(30%)

## Possible textbooks and lecture notes

**Recommended textbook**:
* [John R. Taylor, Classical Mechanics (Univ. Sci. Books 2005)](https://www.uscibooks.com/taylor2.htm), see also [the GitHub link of the course](https://github.com/mhjensen/Physics321/tree/master/doc/Literature)

**Additional textbooks**:
* [Anders Malthe-Sørenssen, Elementary Mechanics using Python (Springer 2015)](https://www.springer.com/gp/book/9783319195957) and [the GitHub link of the course](https://github.com/mhjensen/Physics321/tree/master/doc/Literature)

* [Alessandro Bettini, A Course in Classical Physics 1, Mechanics (Springer 2017)](https://www.springer.com/gp/book/9783319292564) and the [GitHub link of the course](https://github.com/mhjensen/Physics321/tree/master/doc/Literature).

The books from Springer can be downloaded for free (pdf or ebook format) from any MSU IP address.

**Lecture notes**:
Posted lecture notes are in the doc/pub folder here or at <https://mhjensen.github.io/Physics321/doc/web/course.html> for easier viewing. They are not meant to be a replacement for textbook. These notes are updated on a weekly basis and a **git pull** should thus always give you the latest update.

## Teaching schedule with links to material (This will be updated asap)

Weekly mails (Wednesdays or Thursdays) with updates, plans for lectures etc will sent to everybody. We use also Piazza as a discussion forum. Please use this sign-up link <https://piazza.com/msu/spring2020/phy321>. The class link is <https://piazza.com/msu/spring2020/phy321/home>
### Week 2, January 6-10, 2020

1. Monday: Introduction to the course and start discussion of vectors, space, time and motion, Taylor chapter 1.2 and lecture notes (https://mhjensen.github.io/Physics321/doc/pub/Introduction/html/Introduction.html)

2. Wednesday: More on time,space, vectors and motion, Taylor chapters 1.2 and 1.3 and lecture notes (https://mhjensen.github.io/Physics321/doc/pub/Introduction/html/Introduction.html), first homework available

3. Friday: Forces and Newton's laws of motion. Taylor chapter 1.4 and lecture notes (https://mhjensen.github.io/Physics321/doc/pub/Introduction/html/Introduction.html). Introduction to Git and GitHub and getting started with numerical exercises. Installing software (anaconda) and first homework due January 15.

### Week 3, January 13-17, 2020

1. Monday:

2. Wednesday:

3. Friday:  2nd homework, due January 22

### Week 4, January 20-24, 2020

1. Monday: MLK day, no lectures

2. Wednesday:

3. Friday:  3rd homework, due January 29

### Week 5, January 27-31, 2020

1. Monday:

2. Wednesday:

3. Friday:  4th homework, due February 5

### Week 6, February 3-7, 2020

1. Monday:

2. Wednesday:

3. Friday:  5th homework, due February 12

### Week 7, February 10-14, 2020

1. Monday:

2. Wednesday:

3. Friday:  6th homework, due February 19

### Week 8, February 17-21, 2020

1. Monday:

2. Wednesday:

3. Friday:  **First midterm project, due February 28, 2020**

### Week 9, February 24-28, 2020

1. Monday:

2. Wednesday:

3. Friday:

### Week 10, March 2-6, 2020, Spring break

1. Monday:  No lectures

2. Wednesday: No lectures

3. Friday:  No lectures

### Week 11, March  9-13, 2020

1. Monday:

2. Wednesday:

3. Friday:  7th homework, due March 18

### Week 12, March 16-20, 2020

1. Monday:

2. Wednesday:

3. Friday:  8th homework, due March 25

### Week 13, March 23-27, 2020

1. Monday:

2. Wednesday:

3. Friday:  9th homework, due April 1

### Week 14, March 30-April 3, 2020

1. Monday:

2. Wednesday:

3. Friday:  **Second midterm project, due  April 10, 2020**

### Week 15, April  13-17, 2020

1. Monday:

2. Wednesday:

3. Friday:  10th homework and extra assignments, due April 24

### Week 16, April 20-24, 2020

1. Monday:

2. Wednesday:

3. Friday:  Summary and discussions of finals exams

### Week 17, April 27- May 1, 2020, Finals week

1. Final Exam:  April 29, 5:45pm - 7:45pm in 1420 Biomedical & Physical Sciences

## Space, Time, Motion and Reminder on vectors and other mathematical quantities

## Newton's Laws of Motion


```python
import axelrod as axl
import numpy as np
import pandas as pd
import scipy as sp
from sympy import *
import random
from itertools import groupby
import difflib

from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.render import make_snapshot
from snapshot_selenium import snapshot
import matplotlib.pyplot as plt

from matplotlib.colors import LinearSegmentedColormap

from IPython.display import display, HTML
```

```python
#axl.ZDExtortion(phi = 0.1, s = 0.5)
```

```python
#me = axl.Human(name='me')
#players = [axl.TitForTat(), me]
#match = axl.Match(players, turns = 3)
```

```python
_Image_PATH_ = './images/'
_Figure_PATH_ = './figures/'
```

```python
armyrose = ['#798234', '#a3ad62', '#d0d3a2', '#fdfbe4', '#f0c6c3', '#df91a3', '#d46780']
fall = ['#3d5941', '#778868', '#b5b991', '#f6edbd', '#edbb8a', '#de8a5a', '#ca562c']
geyser = ['#008080', '#70a494', '#b4c8a8', '#f6edbd', '#edbb8a', '#de8a5a', '#ca562c']
temps = ['#009392', '#39b185', '#9ccb86', '#e9e29c', '#eeb479', '#e88471', '#cf597e']
tealrose = ['#009392', '#72aaa1', '#b1c7b3', '#f1eac8', '#e5b9ad', '#d98994', '#d0587e']
tropic = ['#009B9E', '#42B7B9', '#A7D3D4', '#F1F1F1', '#E4C1D9', '#D691C1', '#C75DAB']
earth = ['#A16928', '#bd925a', '#d6bd8d', '#edeac2', '#b5c8b8', '#79a7ac', '#2887a1']
```

```python
# Define symbols
R, S, T, P = symbols('R, S, T, P', real = True) # payoffs
chi, phi = symbols('chi, phi', real = True) # parameters for the probabilities
Q_1 = 1 - (R - P)*phi*(chi - 1)
Q_2 = 1 - phi*((T - P)*chi + (P - S))
Q_3 = phi*((P - S)*chi + (T - P))
Q_4 = phi*(P - P)

phi_upper_con = 1/(chi*(T - P) + (P - S)) # the range of phi is 0 < phi < phi_upper
phi_upper_abn = 1/(chi*(P - S) + (T - P)) # the range of phi is 0 < phi < phi_upper

payoff_con_subs = [(R, 3), (S, 0), (T, 5), (P, 1)] # payments of the conventional IPD

human_score_dict = {('C', 'C'): R, ('C', 'D'): S, ('D', 'C'): T, ('D', 'D'): P}
ZD_score_dict = {('C', 'C'): R, ('C', 'D'): T, ('D', 'C'): S, ('D', 'D'): P}
```

```python
strategies = ['Cooperator', 'Defector', 'Random', 'Tit For Tat', 'Win-Stay Lose-Shift', 'Human']
descriptions = ['A player who only ever cooperates. ' + r'$p = [1, 1, 1, 1]$', 'A player who only ever defects. ' + r'$p = [0, 0, 0, 0]$',
                'A player who randomly chooses between cooperating and defecting. ' + r'$p = [0.5, 0.5, 0.5, 0.5]$',
                'A player who starts by cooperating and then repeats the opponent’s previous move. '+ r'$p = [1, 0, 1, 0]$',
                'A player who repeats the previous move if the resulting payoff has met its aspiration level and changes otherwise. ' + r'$p = [1, 0, 0, 1]$',
                'A player does whatever you want.']
p_0_dict = {'Cooperator': 1, 'Defector': 0, 'Random': 0.5, 'Tit For Tat': 1, 'Win-Stay Lose-Shift': 1, 'Human': np.nan}
p_dict = {'Cooperator': [1, 1, 1, 1], 'Defector': [0, 0, 0, 0], 'Random': [0.5, 0.5, 0.5, 0.5],
          'Tit For Tat': [1, 0, 1, 0], 'Win-Stay Lose-Shift': [1, 0, 0, 1], 'Human': [np.nan, np.nan, np.nan, np.nan]}

cmaps = [armyrose, earth, fall, tealrose, tropic, temps]
fig, axes = plt.subplots(2, 3, figsize = (3*5, 2*2), sharey = False)
fig.subplots_adjust(hspace = -0.25, wspace = 0.1)
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
for i, title in enumerate(strategies):
    ix = np.unravel_index(i, axes.shape)
    ax = axes[ix]
    cm = LinearSegmentedColormap.from_list('tealrose', cmaps[i], N = 100)
    ax.imshow(gradient, aspect = 5, cmap = cm)
    ax.axis("off")
    ax.set_title(title, fontsize = 15)
fig.suptitle('iPrisoner Game: ZDGo', fontsize = 20, y = 0.95)
fig.savefig(_Image_PATH_ + 'cover.png', dpi = 400)

def ZDGo(printing = True):

    player = input("Enter your player: ")
    while player not in strategies:
        # find closest match
        options = difflib.get_close_matches(player, strategies)
        if len(options) == 1:
            player = input("Do you mean " + options[0] + '? If yes, enter ' + options[0] +  ", otherwise, choose another player: ")
        else:
            player = input("Unjustified player, re-enter your player: ")

    chi_value = input("Enter chi (chi is the extortion factor and is a number no less than 1): ")
    while True:
        try:
            val = float(chi_value)
            if val < 1:
                chi_value = input("Input is less than 1. Re-enter chi: ")
                continue
            break
        except ValueError:
            chi_value = input("Input is not a number. Re-enter chi: ")
    chi_value = float(chi_value)

    n = input("Enter number of rounds n (n is a positive integer): ")
    while True:
        try:
            val = int(n)
            if val < 0:  # if not a positive int print message and ask for input again
                n = input("Input is not a positive integer. Re-enter n: ")
                continue
            break
        except ValueError:
            try:
                val = float(n)
                n = input("Input is a float. Re-enter n: ")
            except ValueError:
                n = input("Input is not a number. Re-enter n: ")
    n = int(n)

    df_information = pd.DataFrame(data = {'your strategy': [player], 'extortion factor': [chi_value], 'number of rounds': [n]})
    df_information.index = ['']
    display(HTML(df_information.to_html()))

    chi_subs = [(chi, chi_value)]
    payoff_subs, phi_upper = payoff_con_subs, phi_upper_con
    phi_value = 1/2*phi_upper.subs(payoff_subs).subs(chi_subs)
    phi_subs = [(phi, phi_value)]
    q_1 = Q_1.subs(payoff_subs).subs(chi_subs + phi_subs)
    q_2 = Q_2.subs(payoff_subs).subs(chi_subs + phi_subs)
    q_3 = Q_3.subs(payoff_subs).subs(chi_subs + phi_subs)
    q_4 = Q_4.subs(payoff_subs).subs(chi_subs + phi_subs)

    #  count the frequencies of different outcomes
    def freqs(outcomes):
        freq_cc = outcomes.count(('C', 'C'))
        freq_cd = outcomes.count(('C', 'D'))
        freq_dc = outcomes.count(('D', 'C'))
        freq_dd = outcomes.count(('D', 'D'))
        return freq_cc, freq_cd, freq_dc, freq_dd

    # calculate the average scores of human player and ZD extorter
    def scores(outcomes):
        outcomes = outcomes.copy()
        outcomes.sort()
        # frequencies of (C, C), (C, D), (D, C) and (D, D)
        frequencies = [(key, len(list(group))) for key, group in groupby(outcomes)]
        human_score = ZD_score = 0
        for item in frequencies:
            outcome = item[0]
            freq = item[1]
            human_score += human_score_dict[outcome].subs(payoff_subs)*freq
            ZD_score += ZD_score_dict[outcome].subs(payoff_subs)*freq
        # take the average
        human_score /= n
        ZD_score /= n
        return float(human_score), float(ZD_score)

    # decide the action of ZD strategy according to the outcome of the last round
    def ZDAction(last_round):
        if last_round == ('C', 'C'):
            return (lambda x: 'C' if x <= q_1 else 'D')(random.uniform(0, 1))
        elif last_round == ('C', 'D'):
            return (lambda x: 'C' if x <= q_3 else 'D')(random.uniform(0, 1))
        elif last_round == ('D', 'C'):
            return (lambda x: 'C' if x <= q_2 else 'D')(random.uniform(0, 1))
        else:
            return (lambda x: 'C' if x <= q_4 else 'D')(random.uniform(0, 1))

    # decide the action of player according to the outcome of the last round
    def Action(last_round, p):
        p_1, p_2, p_3, p_4 = p
        if last_round == ('C', 'C'):
            return (lambda x: 'C' if x <= p_1 else 'D')(random.uniform(0, 1))
        elif last_round == ('C', 'D'):
            return (lambda x: 'C' if x <= p_2 else 'D')(random.uniform(0, 1))
        elif last_round == ('D', 'C'):
            return (lambda x: 'C' if x <= p_3 else 'D')(random.uniform(0, 1))
        else:
            return (lambda x: 'C' if x <= p_4 else 'D')(random.uniform(0, 1))

    def NonHuman(p_0, p):
        i = 0
        outcomes = []
        while i < n:
            if i == 0:
                action = (lambda x: 'C' if x <= p_0 else 'D')(random.uniform(0, 1))
                ZD_action = 'C'
            else:
                action = Action(outcomes[-1], p)
                ZD_action = ZDAction(outcomes[-1])

            outcomes.append((action, ZD_action))
            if printing == True:
                print("round " + str(i + 1) + '| outcome: ' + '(' + action + ', ' + ZD_action + ')')
            i += 1
        return outcomes

    def Human():
        i = 0
        outcomes = []
        while i < n:
            human_action = input("Round " + str(i + 1) + '| enter your action (C or D): ')
            while human_action not in ['C', 'D']:
                human_action = input('Unjustified action, re-enter your action: ')
            if i == 0:
                ZD_action = 'C'
            else:
                ZD_action = ZDAction(outcomes[-1])
            outcomes.append((human_action, ZD_action))
            if printing == True:
                print("round " + str(i + 1) + '| outcome: ' + '(' + human_action + ', ' + ZD_action + ')')
            i += 1
        return outcomes

    if player == 'Human':
        outcomes = Human()
    else:
        outcomes = NonHuman(p_0_dict[player], p_dict[player])

    df_information["player's score"] = [scores(outcomes)[0]]
    df_information["ZD's score"] = [scores(outcomes)[1]]
    freq_cc, freq_cd, freq_dc, freq_dd = freqs(outcomes)
    df_information['(C, C) pair'] = [freq_cc]
    df_information['(C, D) pair'] = [freq_cd]
    df_information['(D, C) pair'] = [freq_dc]
    df_information['(D, D) pair'] = [freq_dd]

    display(HTML(df_information.to_html()))

    # change data type
    columns = df_information.columns.tolist()
    columns = columns[1:]
    for col in columns:
        df_information[col] = df_information[col].astype(float)

    return df_information, outcomes
```

```python
def figure_pie(df_information):

    title = 'ZDGo'
    subtitle = df_information['your strategy'].tolist()[0] + ' against ZD strategy'
    chi_value = df_information['extortion factor'].tolist()[0]
    rounds = int(df_information['number of rounds'].tolist()[0])
    scores = [df_information["player's score"].tolist()[0], df_information["ZD's score"].tolist()[0]]
    freqs = df_information.iloc[0].tolist()[-4:]

    pie = Pie(init_opts = opts.InitOpts(width='600px', height='500px'))
    v = ['(C, C)', '(C, D)', '(D, C)', '(D, D)']
    d = freqs
    pie.add("frequency", [list(z) for z in zip(v, d)],
                radius = ["35%", "60%"],
                center = ["50%", "55%"],
                rosetype = "radius",
                label_opts = opts.LabelOpts(
                            position = "outside",
                            formatter = "{b|{b}}{abg|}\n{hr|}\n {c|{c} } {per|{d}%}  ", # formatter = "{a|{a}}{abg|}\n{hr|}\n {b|{b}: }{c}  {per|{d}%}  ",
                            background_color = "#eee",
                            border_color = "#aaa",
                            border_width = 1,
                            border_radius = 4,
                            rich = {
                                "b": {"color": "#999", "fontSize": 12, "lineHeight": 20, "align": "center"},
                                "abg": {
                                    "backgroundColor": "#e3e3e3",
                                    "width": "100%",
                                    "align": "right",
                                    "height": 20,
                                    "borderRadius": [4, 4, 0, 0],
                                },
                                "hr": {
                                    "borderColor": "#aaa",
                                    "width": "100%",
                                    "borderWidth": 0.5,
                                    "height": 0,
                                },
                                "c": {"fontSize": 12, "lineHeight": 25},
                                "per": {
                                    "fontSize": 12,
                                    "color": "#eee",
                                    "backgroundColor": "#334455",
                                    "height": 10,
                                    "padding": [2, 2],
                                    "borderRadius": 4,
                                },
                                "d": {"fontSize": 4},
                            },
                        ),
                )
    pie.set_global_opts(title_opts = opts.TitleOpts(title = title, subtitle = subtitle + '\n\n' + 'chi: ' + str(chi_value) + '  ' + 'n: ' + str(rounds) + '  ' + 'scores: ' + '(' + str(scores[0]) + ', ' + str(scores[1]) + ')',
                                                    title_textstyle_opts = opts.TextStyleOpts(font_size = 20),
                                                    subtitle_textstyle_opts = opts.TextStyleOpts(font_size = 16)),
                    legend_opts = opts.LegendOpts(is_show = False, type_ = "plain",
                                                  textstyle_opts = opts.TextStyleOpts(font_size = 12),
                                                  pos_top = "30%", pos_left = "1%", orient = "vertical"),
                    tooltip_opts = opts.TooltipOpts(formatter = "{b}: {c} ({d}%)"))

    make_snapshot(snapshot, pie.render(_Figure_PATH_ + 'ZDGo/' + df_information['your strategy'].tolist()[0].replace(" ", "_") + ".html"),
                              _Figure_PATH_ + 'ZDGo/' + df_information['your strategy'].tolist()[0].replace(" ", "_") + ".png")

    return pie

```

```python

```