---
layout: page
title: Teorema Central do Limite
nav_order: 7
---

[](https://colab.research.google.com/github/icd-ufmg/icd-ufmg.github.io/blob/master/_lessons/07-tcl.ipynb)

# Teorema Central do Limite
{: .no_toc .mb-2 }

O teorema base para os nossos testes de hipóteses
{: .fs-6 .fw-300 }

{: .no_toc .text-delta }
Resultados Esperados

1. Revisar conceitos de Probabilidade ligados a distribuição normal
1. Revisar o teorema central do limite
1. Entendimento do teorema central do limite
1. Simular médias de qualquer distribuição
1. Mostrar como a distribuição de média segue uma normal

---
**Sumário**
1. TOC
{:toc}
---

```python
# -*- coding: utf8

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
```

```python
plt.style.use('seaborn-colorblind')

plt.rcParams['figure.figsize']  = (16, 10)
plt.rcParams['axes.labelsize']  = 20
plt.rcParams['axes.titlesize']  = 20
plt.rcParams['legend.fontsize'] = 20
plt.rcParams['xtick.labelsize'] = 20
plt.rcParams['ytick.labelsize'] = 20
plt.rcParams['lines.linewidth'] = 4
```

```python
plt.ion()
```

```python
def despine(ax=None):
    if ax is None:
        ax = plt.gca()
    # Hide the right and top spines
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)

    # Only show ticks on the left and bottom spines
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')
```

## Introdução

Uma razão pela qual a distribuição normal é tão útil é o teorema central do limite, que diz (em essência) que uma variável aleatória definida como a média (ou soma) de um grande número de variáveis aleatórias independentes e identicamente distribuídas é aproximadamente distribuída normalmente. Em outras palavras, a distribuição amostral de médias segue uma normal.

Em detalhes, se $X_1, ..., X_n$ são variáveis aleatórias. Em particular, todas as VAs foram amostradas de uma mesma população com média $\mu$ (finita), desvio padrão $\sigma$ (finito). Além do mais, a geração de cada VA é independente da outra, sendo toas identicamente distribuídas. Quando $n$ é grande, então

$$\frac{1}{n}(X_1 + \cdots + X_n)$$

é aproximadamente distribuído por uma Normal com média $\mu$ e desvio padrão $\sigma/\sqrt{n}$. De forma equivalente (mas muitas vezes mais útil),

$$Z = \frac{(X_1 + \cdots + X_n) - \mu }{\sigma / \sqrt{n}}$$

é aproximadamente uma normal com média 0 e desvio padrão 1.

$$Z \sim Normal(0, 1).$$

### Como transformar VAs?

Lemebre-se da aula passada que sabemos estimar:

$$\bar{x} \approx \mu$$

e

$$s^2 \approx \sigma$$

Além do mais, sabemos que a variância do estimador da média é:

$$Var(\hat{\mu}) = \frac{\sigma^2}{n}$$

Assim:

\begin{align}
\bar{X} \sim Normal(\mu, \frac{\sigma^2}{n}) \\
\bar{X}- \mu \sim Normal(0, \frac{\sigma^2}{n}) \\
\frac{\bar{X}- \mu}{\sigma / \sqrt{n}} \sim Normal(0, 1) \\
\end{align}

## Exemplo das Moedas

Considere o caso de uma moeda sendo jogada para cima. Agora, escolhe um número `n` (tamanho da amostra), e gere amostras da mesma. Ou seja, jogue uma moeda para cima `n` vezes. Por fim, some quantas vezes a moeda cai em `cara` (ou `coroa`). Isto é uma soma para uma amostra de tamanho `n`.

O processo de geração destes dados é bem capturado por uma distribuição Binomial. Variáveis aleatórias binomiais, que possuem dois parâmetros $n$ e $p$. A distribuição binomial é útil para contar o número de sucessos $n$ dada uma probabilidade $p$. Por exemplo, quantas vezes ($n$) uma moeda ($p$) gera um o valor cara. Formalmente, uma variável aleatória Binomial($n, p$) é simplesmente a soma de $n$ variáveis aleatórias independentes de Bernoulli($p$), cada uma delas igual a $1$ com probabilidade $p$ e $0$ com probabilidade $1 - p$.

Ao gerar um valor de uma Binomial estamos falando "Jogue uma moeda para cima n vezes e conte quantas caras!". No caso abaixo, jogue uma moeda para cima 5 vezes e conte quantas caras!

```python
num_caras = np.random.binomial(5, 0.5)
num_caras
```

    2

Vamos repetir o processo várias! Jogue uma moeda para cima 5 vezes, pare, respire, depois jogue mais 5. Por aí vai. Note que temos a contagem para cada experimento de tamanho 5.

```python
np.random.binomial(5, 0.5, size=10)
```

    array([3, 3, 2, 1, 2, 3, 2, 2, 4, 4])

Agora, vamos ver o gráfico de tal experimento!

```python
num_caras_a_cada_5 = np.random.binomial(5, 0.5, size=10000)
plt.hist(num_caras_a_cada_5, bins=[-0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5], edgecolor='k')
plt.xlabel('Jogadas de moedas')
plt.ylabel('Número de caras')
plt.title('1000 jogadas de moedas em grupos de 5')
despine()
```

Mesma coisa com 10 moedas

```python
num_caras_a_cada_5 = np.random.binomial(10, 0.5, size=1000000)
ticks = np.linspace(-0.5, 10.5, num=12)
print(ticks)
plt.hist(num_caras_a_cada_5, bins=ticks, edgecolor='k')
plt.xlabel('Jogadas de moedas')
plt.ylabel('Número de caras')
plt.title('1000 jogadas de moedas em grupos de 5')
despine()
```

Lembrando da sua aula de probabilidade, uma população que segue uma variável Binomial($n, p$) tem média $\mu = np$ e desvio padrão $\sigma = \sqrt{np(1 - p)}$. Se plotarmos ambos, você pode facilmente ver a semelhança. Obseve o plot abaixo da PDF com os parâmetros que listamos (média, desvio padrão).

```python
import scipy.stats as ss

mean = 10 * 0.5
std = np.sqrt(10 * 0.5 *(1 - 0.5))
x = np.linspace(1, 11, 1000)
y = ss.distributions.norm.pdf(loc=mean, scale=std, x=x)
plt.xlim(0, 10)
plt.plot(x, y)
plt.xlabel('Número caras - x')
plt.ylabel('P(X = x)')
despine()
```

```python
mean = 10 * 0.5
std = np.sqrt(10 * 0.5 *(1 - 0.5))
x = np.linspace(1, 11, 1000)
y = ss.distributions.norm.pdf(loc=mean, scale=std, x=x)
plt.xlim(0, 10)
plt.plot(x, y, label='Aproximação Normal')

num_caras_a_cada_5 = np.random.binomial(10, 0.5, size=1000000)
ticks = np.linspace(-0.5, 10.5, num=12)
plt.hist(num_caras_a_cada_5, bins=ticks, edgecolor='k', label='Dados',
         density=True)
plt.plot(x, y)
plt.xlabel('Jogadas de moedas')
plt.ylabel('Número de caras')
plt.title('1000 jogadas de moedas em grupos de 5')
plt.legend()
despine()
```

## Exemplo com Dados Sintéticos de Matrículas

Para exemplificar com dados, considere o exemplo abaixo onde geramos uma distribuição sintética de 25 mil alunos da UFMG. A distribuição captura o número de matéria que um aluno se matrícula no ano. Note que diferente da moeda, que gera apenas cara ou cora, cada aluno pode se matricular entre [min, max] matérias. No exemplo, vamos suport que todo aluno precisa se matricular em pelo menos uma matéria `min=1` e pode ser matricular em no máximo 10 matérias `max=10`. Agora, vamos suport que cada número de matéria tem probabilidade $p_i$. Ou seja, a chance se matricular em uma matéria é $p_1$ e por aí vai.

Dados deste tipo são modelados por distribuições multinomiais. Generalizando a Binomial, uma Multinomial conta a quantidade de sucessos (matrículas) em cada $p_i$. A mesma é definida por $n > 0$, número de amostras ou matrículas, e $p_1, \ldots, p_k$ probabilidade de se matrícular em $i$ matérias. A pmf de uma multinomial é dada por:

$$P(X = x) = \frac{n!}{x_1!\cdots x_k!} p_1^{x_1} \cdots p_k^{x_k}$$

Inicialmente observe os valores de $p_i$.

```python
num_materias = np.arange(10) + 1
prob_materias = np.array([6, 7, 16, 25, 25, 25, 10, 12, 2, 11])
prob_materias = prob_materias / prob_materias.sum()
plt.bar(num_materias, prob_materias, edgecolor='k')
plt.xlabel('Número de Matérias no Semestre')
plt.ylabel('Fração de Alunos')
plt.title('Distribuição do número de matérias no semestre')
despine()
```

Agora vamos responder a pergunta: **Quantas matérias, em média, um aluno se matrícula?!**. Note que a nossa pergunta aqui é **em média!!**. Então, vamos considerar que temos 25 mil discente na ufmg. Para cada um destes alunos, vamos amostrar de $p_i$ o número de matérias que tal aluno está matrículado no atual semestre.

```python
amostras = 25000
mats = np.arange(10) + 1
print(mats)

dados = []
for i in range(25000):
    n_mat = np.random.choice(mats, p=prob_materias)
    dados.append(n_mat)
dados = np.array(dados)
dados
```

    [ 1  2  3  4  5  6  7  8  9 10]

    array([3, 3, 4, ..., 6, 5, 7])

Agora vamos responder nossa pergunta. **Quantas matérias, em média, um aluno se matrícula?!**. Para tirar uma média precisamos de uma amostra. Vamos definir amostras de tamanho 100. Então, vamos amostrar 100 alunos, **com repetição**, dos nossos 25 mil alunos.

```python
n_amostra = 100
soma = 0
for i in range(n_amostra):
    aluno = np.random.randint(0, len(dados))
    num_mat = dados[aluno]
    soma += num_mat
media = soma / n_amostra
print(media)
```

    5.52

Vamos repetir o processo algumas vezes. Tipo, 10000 vezes.

```python
n_amostra = 100
medias = []
for _ in range(10000):
    soma = 0
    for i in range(n_amostra):
        aluno = np.random.randint(0, len(dados))
        num_mat = dados[aluno]
        soma += num_mat
    media = soma / n_amostra
    medias.append(media)
medias = np.array(medias)
```

Agora vamos ver os resultados!

```python
plt.hist(medias, bins=20, edgecolor='k')
plt.ylabel('P(X = x)')
plt.xlabel('Média das matérias - x')
plt.title('CLT na Prática')
despine()
```

Agora, vamos comparar com a nossa Normal, para isto podemos usar a média das médias e o desvio padrão das médias.

```python
mean = np.mean(medias)
# ddof=1 faz dividir por n-1
std = np.std(medias, ddof=1)

# pegue 1000 números entre o minimo e o max
x = np.linspace(np.min(medias), np.max(medias), 1000)
y = ss.distributions.norm.pdf(loc=mean, scale=std, x=x)

plt.plot(x, y, label='Aproximação Normal')

plt.hist(medias, bins=20, edgecolor='k', density=True)
plt.ylabel('P(X = x)')
plt.xlabel('Média das matérias - x')
plt.title('CLT na Prática')
despine()
```

## Com Dados

```python
df = pd.read_csv('https://media.githubusercontent.com/media/icd-ufmg/material/master/aulas/03-Tabelas-e-Tipos-de-Dados/nba_salaries.csv')
df.head()
```

      PLAYER
      POSITION
      TEAM
      SALARY

      0
      Paul Millsap
      PF
      Atlanta Hawks
      18.671659

      1
      Al Horford
      C
      Atlanta Hawks
      12.000000

      2
      Tiago Splitter
      C
      Atlanta Hawks
      9.756250

      3
      Jeff Teague
      PG
      Atlanta Hawks
      8.000000

      4
      Kyle Korver
      SG
      Atlanta Hawks
      5.746479

```python
df['SALARY'].sort_values().plot.hist(bins=20, edgecolor='k')
plt.xlabel('Salário na NBA')
plt.ylabel('Número de Linhas')
despine()
```

```python
N = 10000
data = df['SALARY']
medias = []
for i in range(N):
    mean = np.random.choice(data, 100).mean()
    medias.append(mean)
```

```python
mean = np.mean(medias)
# ddof=1 faz dividir por n-1
std = np.std(medias, ddof=1)

# pegue 1000 números entre o minimo e o max
x = np.linspace(np.min(medias), np.max(medias), 1000)
y = ss.distributions.norm.pdf(loc=mean, scale=std, x=x)

plt.plot(x, y, label='Aproximação Normal')

plt.hist(medias, bins=20, edgecolor='k', density=True)
plt.ylabel('P(X = x)')
plt.xlabel('Salário da NBA - x')
plt.title('CLT na Prática')
despine()
```

## Condições para o TCL

Existem algumas condições para garantir que o TCL  seja válido.

1. Dados independentes e identicamente distribuídos.
1. Variância finita.
1. Pelo menos umas 30 amostras

Observe do wikipedia que uma distribuição Pareto(1) tem variância infinita. Quebramos nossa condição. Olhe que o plot abaixo não parece em nada com uma Normal.

https://en.wikipedia.org/wiki/Pareto_distribution

```python
data = []
for _ in range(10000):
    m = np.random.pareto(1, size=100).mean()
    data.append(m)
```

```python
plt.hist(data, bins=100, edgecolor='k')
despine()
```

Podemos quebrar também com amostras muito pequenas, tipo na Beta(3, 2, size=2) abaixo.

Observe como é muito perto de uma Normal mas tem um certo viés para a direita.

```python
data = []
for _ in range(10000):
    m = np.random.beta(3, 2, size=2).mean()
    data.append(m)
plt.hist(data, edgecolor='k')
despine()
```

```python
mean = np.mean(data)
# ddof=1 faz dividir por n-1
std = np.std(data, ddof=1)

# pegue 1000 números entre o minimo e o max
x = np.linspace(np.min(data), np.max(data), 1000)
y = ss.distributions.norm.pdf(loc=mean, scale=std, x=x)

plt.plot(x, y, label='Aproximação Normal')

plt.hist(data, bins=20, edgecolor='k', density=True)
plt.ylabel('P(X = x)')
plt.title('CLT na Prática')
despine()
```


# Kamodo Project Page

Kamodo is a new CCMC tool for access, interpolation, and visualization of space weather models and data in python. Kamodo allows model developers to represent simulation results as mathematical functions which may be manipulated directly by end users. Kamodo handles unit conversion transparently and supports interactive science discovery through jupyter notebooks with minimal coding and is accessible through python and fortran.

# Usage

Suppose we have a vector field defined by a function of positions in the x-y plane:

```python
from kamodo import kamodofy
import numpy as np

x = np.linspace(-np.pi, np.pi, 25)
y = np.linspace(-np.pi, np.pi, 30)
xx, yy = np.meshgrid(x,y)
points = np.array(list(zip(xx.ravel(), yy.ravel())))

@kamodofy(units = 'km/s')
def fvec(rvec = points):
    ux = np.sin(rvec[:,0])
    uy = np.cos(rvec[:,1])
    return np.vstack((ux,uy)).T
```

The @kamodofy decorator lets us register this field with units to enable unit-conversion downstream:

```python
from kamodo import Kamodo

kamodo = Kamodo(fvec = fvec)
kamodo
```

\begin{equation}\vec{f}{\left(\vec{r} \right)} [km/s] = \lambda{\left(\vec{r} \right)}\end{equation}

```python
kamodo.to_latex()
```

    '\\begin{equation}\\vec{f}{\\left(\\vec{r} \\right)} [km/s] = \\lambda{\\left(\\vec{r} \\right)}\\end{equation}'

We can now evaluate our function using dot notation:

```python
kamodo.fvec(np.array([[-1,1]]))
```

    array([[-0.84147098,  0.54030231]])

We can perform unit conversion:

```python
kamodo['gvec[m/s]'] = 'fvec'
kamodo
```

\begin{equation}\vec{f}{\left(\vec{r} \right)} [km/s] = \lambda{\left(\vec{r} \right)}\end{equation}\begin{equation}\vec{g}{\left(\vec{r} \right)} [m/s] = 1000 \vec{f}{\left(\vec{r} \right)}\end{equation}

```python
kamodo.gvec(np.array([[-1,1]]))
```

    array([[-841.47098481,  540.30230587]])

We can even generate plots.

```python
import plotly.io as pio

fig = kamodo.plot('fvec')
pio.write_image(fig, 'images/fig2d-usage.svg')
```

## Getting started

### Where to download

If you have a nasa.developer.gov account, you may access the kamodo repository with [git](https://git-scm.com/):

    git pull https://developer.nasa.gov/CCMC/Kameleon2.0

!!! note
    Kamodo is currently only available to users with NASA credentials. We are in the process of making Kamodo open to the public as an open-source project.

#### Download (mini)conda

We strongly recommend using the conda environment system to avoid library conflicts with your host machine's python.

Download and install miniconda from [here](https://conda.io/miniconda.html).

#### Create Kamodo environment

Create a new environment for kamodo

    conda create -n kamodo python=2.7 jupyter pandas numpy

#### Activate new environment

From a bash shell:

    source activate kamodo

Requirements

* numpy
* scipy
* sympy
* pandas
* plotly==3.3
* pytest
* psutil

#### Loading a notebook

Start the notebook server in the ```Prototypes``` subdirectory

    jupyter notebook Prototypes


### Preaamble

```python
import sys
sys.path.append('../src/')
from preamble import *
```

    'Loaded typical imports v 0.0.1'

```python
import IFTA as bidirect
```

```python
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
import matplotlib.font_manager as fm
fontprops = fm.FontProperties(size=12)
from matplotlib.patches import Wedge
from matplotlib import patches
```

```python
# Required for plotting Figure from paper, if not install skip plot_does_paper function
# install pacakge [matplotlib-scalebar] with conda or pip
from matplotlib_scalebar.scalebar import ScaleBar
from matplotlib_scalebar.scalebar import SI_LENGTH_RECIPROCAL
```

# Designing a ring in the reciprocal k-space

We are going to established different rules to generate a QR. We have to find the suitable rule to generate different structure. We will use the effective refractive index to understand the rules.

```python
# For the design we have not taken in account the dispersion of the materials. We use fixed values.
n_front = 1.0
n_back = 1.4 # We assume a fixed SiO2 1.4, lower limit.
n_active = 3.6
# The PC is made between SiO2
```

## Selection of the wavelength to use

We limit the ring to two fundamental wavelengths,
the band-gap + Urbach Tail of the semiconductor and the full absorption of the structure (100 nm GaAs layer).
$l_1 = 900 nm$, $l_2 = 440 nm$. With this values we define the $k_1,k_2$ as the $R$ and $r$ for the ring. Therefore:

\begin{equation}
k_1 = 1/l_1,\\
k_2 = 1/l_2.
\end{equation}

The spatial frequencies will be scaled using the effective refractive index of the layer.
Therefore:
\begin{equation}
k_{1,m} = n_{m} k_{1,0},\\
k_{2,m} = n_{m} k_{2,0}.
\end{equation}

```python
l1 = 0.9 # Bandgap + Urbach Tail
l2 = 0.440 # Single pass with absorption > 95 %
k1 = 1/l1
k2 = 1/l2
k_i = np.array([k1,k2]) * n_front# k in the inc. medium
k_t = np.array([k1,k2]) * n_back# k in the tran. medium
k_c = np.array([k1,k2]) * n_active# using the active
```

```python
# As we are going to work on transmission we use the k_t with the SiO2
k_q = k_t
```

## Lattice Parameter Rules

We have to select the lattice parameter. We can define different rules to established it.
First of all, we want to see the progression from a conventional PC to a QR-PC structure. Therefore, somehow we have to increase the number o diffracted orders between, $k_1$ and $k_2$. Each diffracted order is spaced $k_a$ being, $k_a = 1/a$. Increasing the lattice parameter we increase the number of orders inside the ring, as we are joining more orders together.

**Rule $\Delta m = 0$:**

We want the first diffracted order between $k_2$ and $k_1$. Therefore
\begin{equation}
\label{eq:krule0}
k_a= (k_2 + k_1) / 2.
\end{equation}

**Rule $\Delta m \geq 0$:**

We want to increase the number of orders $\Delta m$, so we establish $k_a$ by dividing linearly the number of orders ($R-r$).

\begin{equation}
\label{eq:krule1}
k_a= (k_2 - k_1) / \Delta m.
\end{equation}

(We use $k_a = 1/a$ for simplicity/numeric but it is equivalent to $k_a = 2\pi / a$ if the constant $2 \pi$ is removed/added in all the steps.)

####  Functions

```python
def qrule(k_int, k_ext, delta_m=1):
    """ Function that generates the rule for creating the Q space
    k_int (float): interior radious in microns^-1
    k_ext (float): exterior radious in microns^-1
    delta_m (int)
    """
    if delta_m == 0:
        ka = (k_int +  k_ext) / 2
    else:
        ka = (k_ext - k_int) / delta_m
    return ka
```

```python
def apply_LUT(I, LUT1, LUT2):
    """ Dummy function to apply two LUTs"""
    I[LUT1] = 0.
    I[LUT2] = 0.
    # no need for return write directly

def create_DOEs(Q, M=2, steps=200, seed=23):
    """ Function to create the structure with objective Q using the function from IFTA
    Q(np.array NxN): Matrix containing the target Q
    M (int): multiplier for the zero-padding frame. The final targe matrix rank is [2*N+1 + M*N].
    seed (int): Seed used in the numpy random to generate the phase.
    """
    F_WA, Q_WA, error_WA = bidirect.WA_A(Q, M=M, steps=steps, seed=seed)
    Q_WA = abs(Q_WA) / abs(Q_WA).max()
    DOE = dict(F=F_WA,Q=Q_WA,error=error_WA)
    return DOE
```

```python
#from pyphotonics.utils import images as simages
def generate_qr(a, k_int, k_ext,  M=10, steps=200, seed=23): #23 is just my cake day
    """ Function to generate the QR for a lattice parameter a, a k_int and k_ext.
    a (float): lattice in microns.
    k_int (float): interior radious in microns^-1
    k_ext (float): exterior radious in microns^-1
    M (int): Zero-Padding frame used for the structure.
    steps (int): steps to do the iteration
    seed (int): Initial seed for the random generator of numpy.
    """
    r1 = k_int #* n_doe #n_doe / l1
    r2 = k_ext #* n_doe #n_doe / l2
    N = int(np.round(r2 * a, 0)) + 1 # + 1 # Longest radius/samples x microns -1
    sN = 2 * N + 1
    R1 = r1 * a
    R2 = r2 * a
    conditions = dict(a=a, R1=R1,R2=R2,M=M)
    # We create the Q(qx,qy)
    x, y = np.meshgrid(np.arange(-N, N + 1), np.arange(-N, N + 1))
    Q = np.ones((2 * N + 1, 2 * N + 1))
    LUT1 = x ** 2 + y ** 2 <= R1 ** 2
    Q[LUT1] = 0.
    LUT2 = x ** 2 + y ** 2 > R2 ** 2
    Q[LUT2] = 0.
    Q = Q/Q.max()
    #print(i+1, Q.sum())
    # Let' create the DOE
    L = create_DOEs(Q, M=M, steps=steps, seed=seed)
    print("Sizes", Q.shape, L['F'].shape)
    return L, Q, conditions
```

## Generating the L (real space) from the Q (reciprocal space)

```python
k_int = k_q[0] # The  interior radius circle
k_ext = k_q[1]
```

```python
delta_m = np.array([0, 1, 2, 3, 4], dtype='float64')
kas = np.zeros_like(delta_m)

for i, m_i in enumerate(delta_m):
    ka = qrule(k_int, k_ext, delta_m=m_i)
    kas[i] = ka
a_s = 1. / kas ## We do not rescale twice the k
print("Lattice parameter (microns)")
print (a_s)
```

    Lattice parameter (microns)
    [0.4222 0.6149 1.2298 1.8447 2.4596]

```python
Ls = []
CONDs = []
Qs = []
seed = 2
for i, a in enumerate(a_s):
    L, Q, conditions = generate_qr(a, k_int, k_ext, M=10)
    Ls.append(L)
    CONDs.append(conditions)
    Qs.append(Q)
    ## PLOTTTING
    # Plotting uncomment for seeing each iteration
    F_WA = L['F']
    Q_WA = np.fft.fft2(F_WA)
    Q_WA[0,0] = 0 # We remove the 0th order for plotting
    Q_WA = np.abs(np.fft.fftshift(Q_WA)) # Re-center
    Q_WA = Q_WA/Q_WA.max() # We normalize the Q
    N = Q.shape[0]/2
    sN = Q.shape[0]
    # We have to zoom into the Zero-padding for plotting Q
    MN = (Q_WA.shape[0] - 1 )//2
    sNM = (2*MN + 1) // a
    # Window to check
    win2 = [(Q_WA.shape[0] - Q.shape[0])//2,
            (Q_WA.shape[0] - Q.shape[0])//2 + Q.shape[0]]
    settings = {"cmap":"magma", "vmin":0, "vmax":1,
                "origin":"lower",}
    fg, ax = plt.subplots(1,4)
    ax[0].set_title('Target Q')
    ax[0].imshow(Q, **settings)
    ax[1].set_title('Structure L')
    ax[1].imshow(L['F'], **settings)
    ax[2].set_title('PSD(L) \nwith padding')
    ax[2].imshow(Q_WA, **settings)
    ax[3].set_title('PSD(L) \ninterest region')
    ax[3].imshow(Q_WA[win2[0]:win2[1], win2[0]:win2[1]], **settings)
```

```python
def plot_does_paper(Is, DOEs, a_s, title1, title2, scales_r, scales_k,  cmap1='magma',
                    cmap2='binary', ncols=5,
                    ltop_as = "", ltop_bs= "", **kwargs,
                     ):
    """It returns the figures for Fig.1 with Q and L for each Delta m"""
    fontprops = fm.FontProperties(size=12)
    plt.rcParams['font.size']=9
    plt.rcParams['svg.fonttype']='none'
    figsize = kwargs.get('figsize', (7.48, 2.7))

    fg, ax = plt.subplots(nrows=2, ncols=ncols, figsize=figsize)
    ii = range(ncols)
    for i, I, DOE, a in zip(ii, Is, DOEs, a_s):
        F_WA = DOE['F']
        I_WA = DOE['Q']
        I_WA = np.fft.fft2(F_WA)
        I_WA[0,0] = 0
        I_WA = np.abs(np.fft.fftshift(I_WA))
        I_WA = I_WA/I_WA.max()
        N = I.shape[0]/2
        sN = I.shape[0]
        # We have to zoom into the Zero-padding for plotting Q
        MN = (I_WA.shape[0] - 1 )//2
        sNM = (2*MN + 1) // a
        win2 = [(I_WA.shape[0] - I.shape[0])//2,
                (I_WA.shape[0] - I.shape[0])//2 + I.shape[0]]
        xlim = [-a / 2., a / 2.   ]
        xftlim = np.array([-sN // 2 , sN // 2 +1] * 2) /a
        xftlim2 = np.array([-sNM // 2, sNM // 2+1] * 2) /a
        ax1 = ax[0,i]
        ax2 = ax[1,i]

        ax1.text(1., 1.03, title1[i], fontsize=10, horizontalalignment='right',
                  verticalalignment='bottom', transform=ax1.transAxes)

        ax1.text(-.05, 1.03, ltop_as[i], fontsize=11, horizontalalignment='left',
                  verticalalignment='bottom', transform=ax1.transAxes)

        ax2.text(1., 1.02, title1[i], fontsize=10, horizontalalignment='right',
                  verticalalignment='bottom', transform=ax2.transAxes)

        ax2.text(-.05, 1.02, ltop_bs[i], fontsize=11, horizontalalignment='left',
                  verticalalignment='bottom', transform=ax2.transAxes)

        im1 = ax1.imshow(
            abs(I_WA[win2[0]:win2[1], win2[0]:win2[1]]), #interpolation='nearest',
                extent=xftlim,
                cmap=cmap1,
                vmin=0,vmax=1)
        im2 = ax2.imshow(F_WA.real / np.pi, #interpolation='nearest',
                         extent=xlim * 2,
                         origin='lower',
                         cmap=cmap2)

        label_formatter = lambda x,y: ""
        scalebar1 = ScaleBar(1,units='1/um',dimension=SI_LENGTH_RECIPROCAL,
                             height_fraction=0.05,
                             fixed_value=scales_k[i],
                             box_alpha=0.,
                             box_color='k',
                             #frameon=True,
                             color='w',
                            label_formatter=label_formatter)
        ax1.add_artist(scalebar1)
        [axi.set_xticks([]) for axi in[ax1,ax2]]
        [axi.set_xticklabels([]) for axi in[ax1,ax2]]
        [axi.set_yticks([]) for axi in[ax1,ax2]]
        [axi.set_yticklabels([]) for axi in[ax1,ax2]]

        ax2.annotate('', xy=(0, -0.08), xycoords='axes fraction', xytext=(1, -0.08),
                     arrowprops=dict(arrowstyle="<->", color='k'))
        ax2.text(0.5, -0.19, title2[i], fontsize=10, horizontalalignment='center',
                  verticalalignment='center', transform=ax2.transAxes,)
    #fg.tight_layout()
    sbot = kwargs.get("sbot",0.05)
    stop = kwargs.get("stop",0.9)
    fg.subplots_adjust(left=0.01,bottom=sbot, top=stop,right=0.99)
    cb = fg.colorbar(im1, ax=ax[0,:].ravel().tolist(), pad=0.01, aspect = 30, label="|FT|$^2$")
    cb = fg.colorbar(im2, ax=ax[1,:].ravel().tolist(), pad=0.01, aspect = 30, label="Phi/$\pi$")

    #algaas = patches.Patch(facecolor='black', edgecolor='black', label='AlGaAs')
    #vacuum = patches.Patch(facecolor='white', edgecolor='black', label='SiO$_2$')
    #ax[1,-1].legend(loc=2,bbox_to_anchor=(1,1.1),frameon=False,handles=[algaas, vacuum])
    return fg
```

```python
scale_rs = [0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 4]
label_aleft = 'abcde'
label_bleft = 'fghij'
titles1 = []
titles2 = []
ltopsa = []
ltopsb = []
scales = [0.1, 0.25, 0.5, 0.75, 1.]
scales = [0.4, 0.4, 0.4, 0.4, .4]
for i, a in enumerate(a_s):
    title1 = "$\Delta m={0}$".format(int(delta_m[i]),0)
    title2 = f"a = {a:.3f} $\mu$m"
    ltop_a = '({})'.format(label_aleft[i])
    ltop_b = '({})'.format(label_bleft[i])
    ltopsa.append(ltop_a)
    ltopsb.append(ltop_b)
    titles1.append(title1)
    titles2.append(title2)
    print(a)
fg2  = plot_does_paper(Qs, Ls, a_s, titles1,titles2,
                        ltop_as=ltopsa,
                        ltop_bs=ltopsb, scales_r=scales, scales_k=[2]*5)
fg2.savefig('../figures/Fig_K_Growth.pdf', dpi=600, bbox_inches='tight')
```

### Saving the Structures

```python
## Used for saving the structures as images
# If not installed use pip or conda
import skimage.io
def saver_image(saveas, DOE):
    A = DOE['F']/DOE['F'].max()
    A = np.array( A * 255,dtype=np.uint8)
    im = skimage.io.imsave(saveas+".png", A)
```

```python
for ii, L in enumerate(Ls):
    saver_image("../figures/ls/" +  "qr_{0}_s{1}".format(ii,0), L)
```

# END

The structures are saved as .png bitmaps so can be loaded on the EM solver.

# Appendix: Example design of Q with two delta m

```python
def plot_bragg_harmonics(ks, kal=0.4, kar=0.4):
    fig, axes = plt.subplots(1,1,figsize=(3.3,3.3))
    axes = [axes]
    colors = ['C0', 'C0', 'C3']
    linestyles=['-','-.', '--']
    axes[0].plot([0],[0], '+', color='k')
    #labels = ['k$_0$', 'k$_{\mathrm{SiO}_2}$', 'k$_\mathrm{GaAs}$']
    i = 1
    color_c =['0.9', 'w']

    axes[0].plot([0,0],[0,0], '-', color='k',label='$k_1$')
    axes[0].plot([0,0],[0,0], '--', color='k',label='$k_2$')

    circle1 = plt.Circle((0,0), ks[1], fill=True, edgecolor='k', linestyle='--', facecolor='0.85', label='$k_2$')
    axes[0].add_artist(circle1)

    circle1 = plt.Circle((0,0), ks[0], fill=True, facecolor='white', edgecolor='k',linestyle='-', label='$k_1$')
    axes[0].add_artist(circle1)

    ring = Wedge((0,0),ks[1],360,360, width=ks[1]-ks[0])
    axes[0].add_artist(ring)

    xc = np.arange(-100,100,1) * kal
    Xc, Yc = np.meshgrid(xc,xc)
    axes[0].plot(Xc[Xc<=0],Yc[Xc<=0],'x', color='0.6', label='$k_{G,\Delta m=0}$')
    lutA = Xc**2 + Yc**2 >= ks[0]**2
    lutB = Xc**2 + Yc**2 <  ks[1]**2
    lut = (lutA  * lutB) * (Xc <=0)
    axes[0].plot(Xc[lut],Yc[lut],'x', color='C3')

    xc = np.arange(-100,100,1) * kar
    Xc, Yc = np.meshgrid(xc,xc)
    axes[0].plot(Xc[Xc>=0],Yc[Xc>=0],'.', color='0.6', label='$k_{G,\Delta m=2}$')
    lutA = Xc**2 + Yc**2 >= ks[0]**2
    lutB = Xc**2 + Yc**2 <  ks[1]**2
    lut = (lutA  * lutB) * (Xc >=0)
    axes[0].plot(Xc[lut],Yc[lut],'.', color='C3')

    axes[0].axis('scaled')
    axes[0].set_xlabel(r'$k_x/\pi$($\mu$m$^{-1}$)')
    axes[0].set_ylabel(r'$k_y/\pi$($\mu$m$^{-1}$)')
    axes[0].legend(loc=2, bbox_to_anchor=(1,1),  frameon=False, fontsize=12)
#

    axes[0].set_ylim(-1,1)

    axes[0].set_xticks([-10.0, -5,   0,5, 10])
    axes[0].set_yticks([-10.0, -5,   0,5, 10])
    axes[0].set_ylim(-10,10)
    axes[0].set_xlim(-10,10)

    return fig, axes

ks = k_t[:2]

fig, axes = plot_bragg_harmonics(k_t*2, kal=2.36969*2, kar=1.62)
#axes[0].axhline(0, color='k')
axes[0].axvline(0, color='k', linestyle='dotted', linewidth=1.2)
fig.savefig('../figures/target_design.svg', bbox_inches='tight')
#fig.savefig('../reports/Fig_rings_kb.pdf', dpi=600, bbox_inches='tight')
```

```python

```

```python

```

notebooks/01-K-Space Desing 100 nm GaAs.ipynb


## Tracking Error Minimization
---

### Passive Management Vs. Active Management

+ So far we have reviewed how to manage our portfolio in terms of the balance between the expected return and the risk (the variance or the expected shortfall). This style of portfolio management is called active management. Active management also involves discretionary selection of assets.

+ Passive management of a portfolio, on the other hand, is a investment strategy in which an investor tries to mimic a benchmark index. Passive management funds that mimic indices are called \alert{index funds}. As the benchmark portfolio, index funds use stock indices, bond indices, currencies, commodities, or even hedge funds.

+ The goal in passive management is to minimize a discrepancy between a portfolio and the benchmark index.

```python
import numpy as np
import scipy.stats as st
import cvxpy as cvx
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
```

Here we generate artificial stock return data and save them in the CSV file `asset_return_data.csv`.

`random.seed` sets the seed for pseudo-random numbers. This assures the reproducibility of computational outcomes with pseudo-random numbers.

We use `multivariate_normal` in `scipy.stats` to generate random vectors from the multivariate normal distribution with the mean vector `Mu` and the covariance matrix `Sigma`. See more details on `multivariate_normal` at https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.multivariate_normal.html

```python
Mu = np.array([1.0, 3.0, 1.5, 6.0, 4.5])
Stdev = np.array([5.0, 10.0, 7.5, 15.0, 11.0])
CorrMatrix = np.array([[1.00, 0.25, 0.18, 0.10, 0.25],
                       [0.25, 1.00, 0.36, 0.20, 0.20],
                       [0.18, 0.36, 1.00, 0.25, 0.36],
                       [0.10, 0.20, 0.25, 1.00, 0.45],
                       [0.25, 0.20, 0.36, 0.45, 1.00]])
Sigma = np.diag(Stdev) @ CorrMatrix @ np.diag(Stdev)
np.random.seed(9999)
T = 120
End_of_Month = pd.date_range('1/1/2010', periods=T, freq='M')
Asset_Names = ['Asset 1', 'Aseet 2', 'Asset3', 'Asset 4', 'Asset 5']
Asset_Return = pd.DataFrame(st.multivariate_normal.rvs(mean=Mu, cov=Sigma, size=T),
                            index=End_of_Month, columns=Asset_Names)
Asset_Return.to_csv('asset_return_data.csv')
display(Asset_Return)
```

      Asset 1
      Aseet 2
      Asset3
      Asset 4
      Asset 5

      2010-01-31
      -2.313057
      -4.503855
      0.530932
      1.392579
      -1.345408

      2010-02-28
      -2.474033
      -2.361441
      -19.814875
      12.819493
      -10.031085

      2010-03-31
      -5.384739
      2.588934
      10.350946
      10.356395
      -1.664494

      2010-04-30
      -5.992190
      -0.496439
      -4.770446
      -5.838914
      -21.171828

      2010-05-31
      -4.701301
      -13.822786
      6.776691
      -4.201704
      21.390680

      ...
      ...
      ...
      ...
      ...
      ...

      2019-08-31
      3.627017
      25.813189
      5.174548
      6.392566
      10.367343

      2019-09-30
      8.219459
      2.193804
      -1.083170
      32.676895
      16.633275

      2019-10-31
      7.407514
      13.978706
      -5.043529
      15.059008
      2.255886

      2019-11-30
      2.853102
      -2.226434
      5.037606
      6.213427
      21.054736

      2019-12-31
      3.925230
      -0.556322
      6.234687
      -3.554233
      9.539070

120 rows × 5 columns

Then we read the generate data from `asset_return_data.csv` again.

```python
R = pd.read_csv('asset_return_data.csv', index_col=0)
R = R.asfreq(pd.infer_freq(R.index))
T, N = R.shape
np.random.seed(8888)
BenchmarkIndex = R @ np.tile(1.0/N, N) + st.norm.rvs(loc=0.0, scale=3.0, size=T)
```

### Tracking Error

Let $y_t$ denote the return on the benchmark index at time $t$, $r_{nt}$ denote the return on asset $n$ $(n=1,\dots,N)$ at time $t$ $(t=1,\dots,T)$, and $w_n$ denote the allocation weight for asset $n$. Then a discrepancy between a portfolio and the benchmark index at time $t$ is given by
\begin{equation}
\begin{split}
 e_t &= y_t - \sum_{n=1}^N w_n r_{nt}
 = y_t - \begin{bmatrix} r_{1t} & \cdots & r_{Nt}\end{bmatrix}
 \begin{bmatrix} w_1\\ \vdots \\ w_N \end{bmatrix} \\
 &= y_t - r_t^{\intercal}w.
\end{split}
\end{equation}
This discrepancy is called a tracking error.

Define

\begin{equation*}
 y = \begin{bmatrix} y_1 \\ \vdots \\ y_T \end{bmatrix},\quad
 R = \begin{bmatrix} r_1^{\intercal} \\ \vdots \\ r_T^{\intercal} \end{bmatrix},\quad
 e = \begin{bmatrix} e_1 \\ \vdots \\ e_T \end{bmatrix}.
\end{equation*}

The tracking error minimization problem is defined as

\begin{equation*}
\begin{split}
 \min_{w, e} & \quad \frac1{T}e^{\intercal}e, \\
 \text{subject to} & \quad  e = y-Rw,\quad w^{\intercal}\iota = 1,\quad w\geqq 0.
\end{split}
\end{equation*}

```python
MovingWindow = 96
BackTesting = T - MovingWindow
V_Tracking = np.zeros(BackTesting)
Weight = cvx.Variable(N)
Error = cvx.Variable(MovingWindow)
TrackingError = cvx.sum_squares(Error) / MovingWindow
Asset_all = R
Index_all = BenchmarkIndex
for Month in range(0, BackTesting):
    Asset = Asset_all.values[Month:(Month + MovingWindow), :]
    Index = Index_all.values[Month:(Month + MovingWindow)]
    Min_TrackingError = cvx.Problem(cvx.Minimize(TrackingError),
                                    [Index - Asset @ Weight == Error,
                                     cvx.sum(Weight) == 1.0,
                                     Weight >= 0.0])
    Min_TrackingError.solve(solver=cvx.ECOS)
    V_Tracking[Month] = R.values[Month + MovingWindow, :] @ Weight.value
```

```python
fig1 = plt.figure(num=1, facecolor='w')
plt.plot(list(range(1, BackTesting + 1)), BenchmarkIndex[MovingWindow:], 'b-', label='Benchmark')
plt.plot(list(range(1, BackTesting + 1)), V_Tracking, 'r--', label='Portfolio')
plt.legend(loc='best', frameon=False)
plt.xlabel('Year')
plt.ylabel('Return (%)')
plt.xticks(list(range(12, BackTesting + 1, 12)),
           pd.date_range(R.index[MovingWindow], periods=BackTesting//12, freq='AS').year)
plt.show()
```


Computational Seismology

Finite Element Method - 1D Elastic Wave Equation

---

This notebook is part of the supplementary material
to [Computational Seismology: A Practical Introduction](https://global.oup.com/academic/product/computational-seismology-9780198717416?cc=de&lang=en&#),
Oxford University Press, 2016.

##### Authors:
* David Vargas ([@dvargas](https://github.com/davofis))
* Heiner Igel ([@heinerigel](https://github.com/heinerigel))

## Basic Equations
This notebook presents a finite element code for the 1D elastic wave equation. Additionally, a solution using finite difference scheme is given for comparison.

The problem of solving the wave equation

\begin{equation}
\rho(x) \partial_t^2 u(x,t) = \partial_x (\mu(x) \partial_x u(x,t)) + f(x,t)
\end{equation}

using the finite element method is done after a series of steps performed on the above equation.

1) We first obtain a weak form of the wave equation by integrating over the entire physical domain $D$ and at the same time multiplying by some basis $\varphi_{i}$.

2) Integration by parts and implementation of the stress-free boundary condition is performed.

3) We approximate our unknown displacement field $u(x, t)$ by a sum over space-dependent basis functions $\varphi_i$ weighted by time-dependent coefficients $u_i(t)$.

\begin{equation}
u(x,t) \ \approx \ \overline{u}(x,t) \ = \ \sum_{i=1}^{n} u_i(t) \ \varphi_i(x)
\end{equation}

4) Utilize the same basis functions used to expand $u(x, t)$ as test functions in the weak form, this is the Galerkin principle.

5) We can turn the continuous weak form into a system of linear equations by considering the approximated displacement field.

\begin{equation}
\mathbf{M}^T\partial_t^2 \mathbf{u} + \mathbf{K}^T\mathbf{u} = \mathbf{f}
\end{equation}

6) For the second time-derivative, we use a standard finite-difference approximation. Finally, we arrive at the explicit time extrapolation scheme.

\begin{equation}
\mathbf{u}(t + dt) = dt^2 (\mathbf{M}^T)^{-1}[\mathbf{f} - \mathbf{K}^T\mathbf{u}] + 2\mathbf{u} - \mathbf{u}(t-dt).
\end{equation}

where $\mathbf{M}$ is known as the mass matrix, and $\mathbf{K}$ the stiffness matrix.

7) As interpolating functions, we choose interpolants such that $\varphi_{i}(x_{i}) = 1$ and zero elsewhere. Then, we transform the space coordinate into a local system. According to $\xi = x − x_{i}$ and $h_{i} = x_{i+1} − x_{i}$, we have:

\begin{equation}
 \varphi_{i}(\xi) =
  \begin{cases}
    \frac{\xi}{h_{i-1}} + 1  & \quad \text{if} \quad -h_{i-1} \le \xi \le 0\\
    1 + \frac{\xi}{h_{i}}    & \quad \text{if} \quad 0 \le \xi \le h_{i}\\
    0  & \quad elsewhere\\
  \end{cases}
\end{equation}

with the corresponding derivatives

\begin{equation}
 \partial_{\xi}\varphi_{i}(\xi) =
  \begin{cases}
    \frac{1}{h_{i-1}}  & \quad \text{if} \quad -h_{i-1} \le \xi \le 0\\
   -\frac{1}{h_{i}}    & \quad \text{if} \quad 0 \le \xi \le h_{i}\\
    0  & \quad elsewhere\\
  \end{cases}
\end{equation}

The figure on the left-hand side illustrates the shape of $\varphi_{i}(\xi)$ and $\partial_{\xi}\varphi_{i}(\xi)$ with varying $h$.

Code implementation starts with the initialization of a particular setup of our problem. Then, we define the source that introduces perturbations following by initialization of the mass and stiffness matrices. Finally, time extrapolation is done.

```python
# Import all necessary libraries, this is a configuration step for the exercise.
# Please run it before the simulation code!
import numpy as np
import matplotlib
# Show Plot in The Notebook
matplotlib.use("nbagg")
import matplotlib.pyplot as plt
import matplotlib.animation as animation

```

### 1. Initialization of setup

```python
# Initialization of setup
# ---------------------------------------------------------------
# Basic parameters
nt    = 2000    # Number of time steps
vs    = 3000    # Wave velocity [m/s]
ro0   = 2500    # Density [kg/m^3]
nx    = 1000    # Number of grid points
isx   = 500     # Source location [m]
xmax  = 10000.  # Maximum length
eps   = 0.5     # Stability limit
iplot = 20      # Snapshot frequency

dx = xmax/(nx-1)           # calculate space increment
x  = np.arange(0, nx)*dx   # initialize space coordinates
x  = np.transpose(x)

h = np.diff(x)  # Element sizes [m]

# parameters
ro = x*0 + ro0
mu = x*0 + ro*vs**2

# time step from stabiity criterion
dt = 0.5*eps*dx/np.max(np.sqrt(mu/ro))
# initialize time axis
t   = np.arange(1, nt+1)*dt

# ---------------------------------------------------------------
# Initialize fields
# ---------------------------------------------------------------
u    = np.zeros(nx)
uold = np.zeros(nx)
unew = np.zeros(nx)

p    = np.zeros(nx)
pold = np.zeros(nx)
pnew = np.zeros(nx)
```

### 2. Source time function

In 1D the propagating signal is an integral of the source time function. As we look for a Gaussian waveform, we initialize the source time function $f(t)$ using the first derivative of a Gaussian function.

\begin{equation}
f(t) = -\dfrac{2}{\sigma^2}(t - t_0)e^{-\dfrac{(t - t_0)^2}{\sigma^2}}
\end{equation}

#### Exercise 1
Initialize a source time function called 'src'.  Use $\sigma = 20 dt$ as Gaussian width, and time shift $t_0 = 3\sigma$. Then, visualize the source in a given plot.

```python
#################################################################
# INITIALIZE THE SOURCE TIME FUCTION HERE!
#################################################################

# Source vector
f = np.zeros(nx); f[isx:isx+1] = f[isx:isx+1] + 1.

#################################################################
# PLOT THE SOURCE TIME FUNCTION HERE!
#################################################################
```

#### Solutions:

```python
# Initialization of the source time function
# ---------------------------------------------------------------
pt  = 20*dt     # Gaussian width
t0  = 3*pt      # Time shift
src = -2/pt**2 * (t-t0) * np.exp(-1/pt**2 * (t-t0)**2)

# Source vector
# ---------------------------------------------------------------
f = np.zeros(nx); f[isx:isx+1] = f[isx:isx+1] + 1.

# ---------------------------------------------------------------
# Plot source time fuction
# ---------------------------------------------------------------
plt.plot(t, src, color='b', lw=2, label='Source time function')
plt.ylabel('Amplitude', size=16)
plt.xlabel('time', size=16)
plt.legend()
plt.grid(True)
plt.show()
```

    <IPython.core.display.Javascript object>

### 3. The Mass Matrix
Having implemented the desired source, now we initialize the mass and stiffness matrices. In general, the mass matrix is given

\begin{equation}
M_{ij} = \int_{D} \rho \varphi_i \varphi_j dx = \int_{D_{\xi}} \rho \varphi_i \varphi_j d\xi
\end{equation}

next, the defined basis are introduced and some algebraic treatment is done to arrive at the explicit form of the mass matrix

#### Exercise 2
Implement the mass matrix

\begin{equation}
M_{ij} = \frac{\rho h}{6}
 \begin{pmatrix}
   \ddots  &    &    &    & 0\\
   1 & 4 &  1 &    &  \\
     & 1 &  4 &  1 &  \\
     &   &  1 &  4 & 1\\
   0 &   &    &    &  \ddots
 \end{pmatrix}
\end{equation}

Compute the inverse mass matrix and display your result to visually inspect how it looks like

```python
#################################################################
# IMPLEMENT THE MASS MATRIX HERE!
#################################################################

#################################################################
# COMPUTE THE INVERSE MASS MATRIX HERE!
#################################################################

#################################################################
# DISPLAY THE INVERSE MASS MATRIX HERE!
#################################################################
```

#### Solutions:

```python
# ---------------------------------------------------------------
# Mass matrix M_ij (Eq 6.56)
# ---------------------------------------------------------------
M = np.zeros((nx,nx), dtype=float)
for i in range(1, nx-1):
    for j in range (1, nx-1):
        if j==i:
            M[i,j] = (ro[i-1]*h[i-1] + ro[i]*h[i])/3
        elif j==i+1:
            M[i,j] = ro[i]*h[i]/6
        elif j==i-1:
            M[i,j] = ro[i-1]*h[i-1]/6
        else:
            M[i,j] = 0

# Corner elements
M[0,0] = ro[0]*h[0]/3
M[nx-1,nx-1] = ro[nx-1]*h[nx-2]/3
# Invert M
Minv = np.linalg.inv(M)

# ---------------------------------------------------------------
# Display inverse mass matrix inv(M)
# ---------------------------------------------------------------
plt.imshow(Minv)
plt.title('Mass Matrix $\mathbf{M}$')
plt.axis("off")
plt.tight_layout()
plt.show()
```

    <IPython.core.display.Javascript object>

### 4. The Stiffness matrix
On the other hand, the general form of the stiffness matrix is

\begin{equation}
K_{ij} = \int_{D} \mu \partial_x\varphi_i \partial_x\varphi_j dx = \int_{D_{\xi}} \mu \partial_\xi\varphi_i \partial_\xi\varphi_j d\xi
\end{equation}

at this point, the defined basis are introduced. Again, with the help of some algebraic treatment, we arrive at the explicit form of the stiffness matrix

#### Exercise 3
Implement the stiffness matrix

\begin{equation}
K_{ij} = \frac{\mu}{h}
 \begin{pmatrix}
   \ddots  &    &    &    & 0\\
  -1 & 2 & -1 &    &  \\
     &-1 &  2 & -1 &  \\
     &   & -1 &  2 & -1\\
   0 &   &    &    &  \ddots
 \end{pmatrix}
\end{equation}

Display the stiffness matrix to visually inspect how it looks like

```python
#################################################################
# IMPLEMENT THE STIFFNESS MATRIX HERE!
#################################################################

#################################################################
# DISPLAY THE STIFFNESS MATRIX HERE!
#################################################################
```

#### Solutions

```python
# ---------------------------------------------------------------
# Stiffness matrix Kij (Eq 6.60)
# ---------------------------------------------------------------
K = np.zeros((nx,nx), dtype=float)
for i in range(1, nx-1):
    for j in range(1, nx-1):
        if i==j:
            K[i,j] = mu[i-1]/h[i-1] + mu[i]/h[i]
        elif i==j+1:
            K[i,j] = -mu[i-1]/h[i-1]
        elif i+1==j:
            K[i,j] = -mu[i]/h[i]
        else:
            K[i,j] = 0

K[0,0] = mu[0]/h[0]
K[nx-1,nx-1] = mu[nx-1]/h[nx-2]

# ---------------------------------------------------------------
# Display stiffness matrix K
# ---------------------------------------------------------------
plt.imshow(K)
plt.title('Stiffness Matrix $\mathbf{K}$')
plt.axis("off")

plt.tight_layout()
plt.show()
```

    <IPython.core.display.Javascript object>

### 5. Finite differences matrices
We implement a finite difference scheme in order to compare with the finite elements solution.

#### Exercise 4
Implement the finite differences matrices $M$ and $D$. Where $M$ is a diagonal mass matrix containing the inverse densities, and differentiation matrix

\begin{equation}
D_{ij} = \frac{\mu}{dt^2}
 \begin{pmatrix}
   -2 & 1  &    &    &  \\
    1 & -2 & 1  &    &  \\
      &  & \ddots  &  & \\
      &   & 1 &  -2 & 1\\
      &   &    &  1 & -2
 \end{pmatrix}
\end{equation}

Display both matrices to visually inspect how they look like

```python
#################################################################
# INITIALIZE FINITE DIFFERENCES HERE!
#################################################################

#################################################################
# DISPLAY THE DIFFERENCES MATRICES HERE!
#################################################################

```

#### Solutions:

```python
# Initialize finite differences matrices (Eq 6.61)
# ---------------------------------------------------------------
Mf = np.zeros((nx,nx), dtype=float)
D  = np.zeros((nx,nx), dtype=float)
dx = h[1]

for i in range(nx):
    Mf[i,i] = 1./ro[i]
    if i>0:
        if i<nx-1:
            D[i+1,i] =1
            D[i-1,i] =1
            D[i,i] = -2

D = ro0 * vs**2 * D/dx**2

# ---------------------------------------------------------------
# Display differences matrices
# ---------------------------------------------------------------
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.imshow(D)
ax1.set_title('Stiffness Differential Matrix $\mathbf{D}$')
ax1.axis("off")

ax2.imshow(Mf)
ax2.set_title('Stiffness Differential Matrix $\mathbf{M_f}$')
ax2.axis("off")

plt.tight_layout()
plt.show()
```

    <IPython.core.display.Javascript object>

### 6. Finite element solution

Finally we implement the finite element solution using the computed mass $M$ and stiffness $K$ matrices together with a finite differences extrapolation scheme

\begin{equation}
\mathbf{u}(t + dt) = dt^2 (\mathbf{M}^T)^{-1}[\mathbf{f} - \mathbf{K}^T\mathbf{u}] + 2\mathbf{u} - \mathbf{u}(t-dt).
\end{equation}

```python
# Initialize animated plot
# ---------------------------------------------------------------
plt.figure(figsize=(12,4))

line1 = plt.plot(x, u, 'k', lw=1.5, label='FEM')
line2 = plt.plot(x, p, 'r', lw=1.5, label='FDM')
plt.title('Finite elements 1D Animation', fontsize=16)
plt.ylabel('Amplitude', fontsize=12)
plt.xlabel('x (m)', fontsize=12)

plt.ion()   # set interective mode
plt.show()

# ---------------------------------------------------------------
# Time extrapolation
# ---------------------------------------------------------------
for it in range(nt):
    # --------------------------------------
    # Finite Element Method
    unew = (dt**2) * Minv @ (f*src[it] - K @ u) + 2*u - uold
    uold, u = u, unew

    # --------------------------------------
    # Finite Difference Method
    pnew = (dt**2) * Mf @ (D @ p + f/dx*src[it]) + 2*p - pold
    pold, p = p, pnew

    # --------------------------------------
    # Animation plot. Display both solutions
    if not it % iplot:
        for l in line1:
            l.remove()
            del l
        for l in line2:
            l.remove()
            del l
        line1 = plt.plot(x, u, 'k', lw=1.5, label='FEM')
        line2 = plt.plot(x, p, 'r', lw=1.5, label='FDM')
        plt.legend()
        plt.gcf().canvas.draw()
```

    <IPython.core.display.Javascript object>

```python

```

notebooks/Computational Seismology/The Finite-Element Method/fe_elastic_1d_solution.ipynb


# The Black-Scholes-Merton model

The stock price $\DeclareMathOperator*{\argmin}{argmin}
\DeclareMathOperator*{\argmax}{argmax}
\DeclareMathOperator*{\plim}{plim}
\newcommand{\ffrac}{\displaystyle \frac}
\newcommand{\d}[1]{\displaystyle{#1}}
\newcommand{\space}{\text{ }}
\newcommand{\bspace}{\;\;\;\;}
\newcommand{\bbspace}{\;\;\;\;\;\;\;\;}
\newcommand{\QQQ}{\boxed{?\:}}
\newcommand{\void}{\left.\right.}
\newcommand{\Tran}[1]{{#1}^{\mathrm{T}}}
\newcommand{\CB}[1]{\left\{ #1 \right\}}
\newcommand{\SB}[1]{\left[ #1 \right]}
\newcommand{\P}[1]{\left( #1 \right)}
\newcommand{\abs}[1]{\left| #1 \right|}
\newcommand{\norm}[1]{\left\| #1 \right\|}
\newcommand{\given}[1]{\left. #1 \right|}
\newcommand{\using}[1]{\stackrel{\mathrm{#1}}{=}}
\newcommand{\asim}{\overset{\text{a}}{\sim}}
\newcommand{\RR}{\mathbb{R}}
\newcommand{\EE}{\mathbb{E}}
\newcommand{\II}{\mathbb{I}}
\newcommand{\NN}{\mathbb{N}}
\newcommand{\ZZ}{\mathbb{Z}}
\newcommand{\QQ}{\mathbb{Q}}
\newcommand{\PP}{\mathbb{P}}
\newcommand{\AcA}{\mathcal{A}}
\newcommand{\FcF}{\mathcal{F}}
\newcommand{\AsA}{\mathscr{A}}
\newcommand{\FsF}{\mathscr{F}}
\newcommand{\dd}{\mathrm{d}}
\newcommand{\I}[1]{\mathrm{I}\left( #1 \right)}
\newcommand{\N}[1]{\mathcal{N}\left( #1 \right)}
\newcommand{\Exp}[1]{\mathrm{E}\left[ #1 \right]}
\newcommand{\Var}[1]{\mathrm{Var}\left[ #1 \right]}
\newcommand{\Avar}[1]{\mathrm{Avar}\left[ #1 \right]}
\newcommand{\Cov}[1]{\mathrm{Cov}\left( #1 \right)}
\newcommand{\Corr}[1]{\mathrm{Corr}\left( #1 \right)}
\newcommand{\ExpH}{\mathrm{E}}
\newcommand{\VarH}{\mathrm{Var}}
\newcommand{\AVarH}{\mathrm{Avar}}
\newcommand{\CovH}{\mathrm{Cov}}
\newcommand{\CorrH}{\mathrm{Corr}}
\newcommand{\ow}{\text{otherwise}}
\newcommand{\FSD}{\text{FSD}}
\newcommand{\SSD}{\text{SSD}}S_t$ satisfies the ***Black-Scholes-Merton model***, $\dd S_t = \mu S_t \dd t + \sigma S_t \dd W_t$. And from the preceding result, we have

$$S_t = S_0 \cdot \exp\CB{\P{\mu - \ffrac{\sigma^2}{2}}t +\sigma W_t},\bspace \Exp{S_t} = S_0 e^{\mu t}$$

Here, $\mu$ represents the **expected return** of the stock, $\sigma$ measures the riskness of the stock, and we call it the ***volatility***. Normally, the higher the *risk*, the higher the *expected return*.

## Estimating Volatility from Historical Data

Usually the time interval is fixed. So for $i=1,2,\dots,n$, define $\Delta_t = t_i - t_{i-1}$. Then let

$$X_i = \log\P{\ffrac{S_{t_{\void_i}}}{S_{t_{\void_{i-1}}}}} \Longrightarrow \bar X = \ffrac{1}{n} \sum_{i=1}^n X_i,\bspace s = \sqrt{\ffrac{1}{\mathbf{n-1}} \sum\nolimits_{i=1}^n \P{X_i - \bar X}^2}$$

From the expression of $S_t$, we find the distribution of $X_i$, and the *unbiased estimate* of $\sigma$, ($ \Exp{s^2} = \sigma^2 \Delta_t$)

$$X_i = \log\P{\ffrac{S_{t_{\void_i}}}{S_{t_{\void_{i-1}}}}} \sim \N{\P{\mu - \ffrac{\sigma^2}{2}}\Delta_t,\sigma^2 \Delta_t},\bspace \hat\sigma = \ffrac{s}{\sqrt{\Delta_t}}$$

$Assumptions$

1. NO transaction costs or taxes.
2. All securities are *perfectly divisible*.
3. *Short selling* of stocks is permitted.
4. The assets pay NO dividends.
5. NO riskless arbitrage opportunies.
6. Trading takes place *continuously*.
7. The stock price $S_t$ satisfies the Black-Scholes-Merton model, where the parameters $\mu$, $\sigma$, and risk-free interest rate $r$ are *constant*.

## Derivation of the Black-Scholes-Merton Differential Equation
### Method 1: Dynamic Replication (No arbitrage)

The model is assumed to be $\dd S_t = \mu S_t \dd t + \sigma S_t \dd W_t$ and let $\pi_t$ denote the amount of money (not shares) held in the stock at time $t$. Borned to be an *adapted process* to $\FcF_t$.

Let $V_t^\pi \equiv V_t$ denote the ***wealth process*** corresponding to the portfolio strategy $\pi$. Besides, this portfolio strategy is required to be **self-financing**, meaning that the amount held in the bank at time $t$ is equal to $V_t - \pi_t$. If we let $B_t = e^{rt}$ denote the bond price at time $t$, using no-arbitrage,

$$\dd V_t = \ffrac{\pi_t}{S_t} \dd S_t + \ffrac{V_t - \pi_t}{B_t}\dd B_t$$

With this we can determine the value of the investor's wealth by combine the two differential equation together:

$$\dd V_t = \SB{rV_t + \P{\mu-r} \pi_t}\dd t + \sigma\pi_t \dd W_t$$

Using the portfolio strategy $\pi$ to replicate the European call:

$$\begin{align}
V_T &= c\P{T,S_T} = \max\P{S_T-K,0}\Longrightarrow V_t = c\P{t,S_t}, \bspace \forall \; t\leq T\\[0.8em]
\dd c\P{t,S_t} &\using{\text{Itô}} \P{\ffrac{\partial c}{\partial t} + \mu S_t \ffrac{\partial c}{\partial S_t} + \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2}}\dd t + \sigma S_t\ffrac{\partial c}{\partial S_t}\;\dd W_t\\
\dd V_t &= \SB{rV_t + \P{\mu-r} \pi_t}\dd t + \sigma\pi_t \dd W_t
\end{align}$$

Compare the $\dd W_t$ term of the last two equations, we have the **number of shares needs** to be **delta** of the option, and this is known as the ***dynamic replication***.

$$\ffrac{\pi_t}{S_t} = \ffrac{\partial c}{\partial S_t}$$

And then we compare the $\dd t$ term, and this leads to the ***Black-Scholes-Merton equation***.

$$ \ffrac{\partial c}{\partial t} + \mu S_t \ffrac{\partial c}{\partial S_t} + \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2} = rc + \P{\mu-r} \pi_t $$

with terminal condition $c\P{T,S_T} = \max\P{S_T -K,0}$. Then plugin the equity $\ffrac{\pi_t}{S_t} = \ffrac{\partial c}{\partial S_t}$ to this, we have the equivalent equation (with the same terminal condition):

$$\ffrac{\partial c}{\partial t} + r S_t \ffrac{\partial c}{\partial S_t} + \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2} - rc = 0$$

$Remark$

>$\mu$ is not in the final equation.

### Method 2: Riskless Hedging Principle

Portfolio: short selling of $1$ unit of a *European call option* and long holding of $\Delta$ units of the asset. The portfolio value at time $t$ is now

$$\Pi\P{t,S_t} = -c\P{t,S_t}+\Delta S_t$$

$Remark$

>Here the $\Delta$ is the stock portion, the same as the dynamic replication, $\Delta = \ffrac{\pi_t}{S_t} = \ffrac{\partial c}{\partial S_t}$.

The differential financial gain over is $\dd\Pi\P{t,S_t} = -\dd c + \Delta \dd S_t$, or more specifically using Itô,

$$\begin{align}
\dd\Pi\P{t,S_t}&=-\P{\ffrac{\partial c}{\partial t} + \mu S_t \ffrac{\partial c}{\partial S_t} + \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2}}\dd t - \sigma S_t\ffrac{\partial c}{\partial S_t}\;\dd W_t + \Delta \P{\mu S_t\dd t + \sigma S_t \dd W_t}\\
&= \P{ - \ffrac{\partial c}{\partial t} + \mu S_t\P{\Delta - \ffrac{\partial c}{\partial S_t}} - \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2}}\dd t + \sigma S_t\P{\Delta - \ffrac{\partial c}{\partial S_t}}\dd W_t
\end{align}$$

Then from the **riskless hedging principle**, we have $\dd\Pi\P{t,S_t} = r\cdot\Pi\P{t,S_t}\dd t$ and thus

$$\P{ - \ffrac{\partial c}{\partial t} + \mu S_t\P{\Delta - \ffrac{\partial c}{\partial S_t}} - \ffrac{1}{2}\sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2}}\dd t + \sigma S_t\P{\Delta - \ffrac{\partial c}{\partial S_t}}\dd W_t = r\cdot\Pi\P{t,S_t}\dd t = r\cdot\P{-c+\Delta S_t}\dd t$$

Match the $\dd W_t$ terms on both sides, we have $\Delta = \ffrac{\partial c}{\partial S_t}$. Match the $\dd t$ terms on both sides, again, we have the **Black-Scholes-Merton equation**, by replacing $\Delta$ with $\ffrac{\partial c}{\partial S_t}$:

$$\ffrac{\partial c}{\partial t} + \ffrac{1}{2} \sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2} + rS_t \ffrac{\partial c}{\partial S_t} - rc = 0$$

### Method 3: Risk-Neutral Valuation

Following the **Gisanov's Thoerem**, to find the measure $\QQ$ under which the discounted stock price $\CB{ \tilde S_t}_{t\geq 0}$ is a martingale, our conclusion is

- $\given{\ffrac{\dd\QQ}{\dd\PP}}_{\FcF_t} = \exp\CB{-\theta W_t - \ffrac{1}{2}\theta^2 t}$, where $\theta = \ffrac{\mu - r}{\sigma}$
- Under $\QQ$ measure, $\dd S_t = rS_t\dd t + \sigma S_t \dd \widetilde W_t$, $\dd \tilde S_t = \sigma \tilde S_t \dd \widetilde W_t$

And then we obtain the risk-neutral valuation formula:

$$\begin{align}
c\P{t,S_t} &= \ExpH_t^\QQ\SB{e^{-r\P{T-t}}c\P{T,S_T}}\\
&= \ExpH_t^\QQ\SB{e^{-r\P{T-t}} %\max
\P{S_T - K}^+}\\
&= \ExpH^\QQ\SB{e^{-r\P{T-t}} %\max
\P{S_T - K}^+ \mid S_t}
\end{align}$$

With this we apply the **Feynman-Kac theorem** and obtain (meaning these methods are actually equivalent!)

$$\ffrac{\partial c}{\partial t} + \ffrac{1}{2} \sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2} + rS_t \ffrac{\partial c}{\partial S_t} - rc = 0$$

## Black-Scholes Formula

For *european call* option, we solve the equation derived from each method and obtain:

$$c\P{t,S_t} = S_t\Phi^{-1}\P{d_1} - Ke^{-r\P{T-t}} \Phi^{-1}\P{d_2}\equiv S_t\N{d_1} - Ke^{-r\P{T-t}} \N{d_2}\\
\begin{align}
\N{x} &\equiv \Phi^{-1}\P{x} = P\CB{X\leq x} = \ffrac{1}{\sqrt{2\pi}} \int_{-\infty}^x \exp\CB{-\ffrac{u^2}{2}} \; \dd u,\bspace X\sim \N{0,1}\\
d_1 &= \ffrac{1}{\sigma\sqrt{T-t}} \P{\log\ffrac{S_t}{K} + \P{r\,\mathbf+\,\ffrac{1}{2}\sigma^2}\P{T-t}}\\
d_2 &= \ffrac{1}{\sigma\sqrt{T-t}} \P{\log\ffrac{S_t}{K} + \P{r\,\mathbf-\,\ffrac{1}{2}\sigma^2}\P{T-t}} = d_1 - \sigma \sqrt{T-t}
\end{align}$$

$Proof$

>We can solve the **PDE**, and to see the essays in ref folder. Or, use the probabilistic approach. In method 3, European call option price can expressed as expectation under risk neutral measure $\QQ$
>
>$$\begin{align}
c\P{t,S_t} &= \ExpH^\QQ\SB{e^{-r\P{T-t}} %\max
\P{S_T-K}^+\mid S_t}\\
&= \ExpH^\QQ_t\SB{e^{-r\P{T-t}} %\max
\P{S_T-K}^+}\\
&=e^{-r\P{T-t}}\P{\ExpH^\QQ_t\SB{S_T \cdot \mathbf{1}_{S_{\void_T}>K}}-K\cdot\ExpH^\QQ_t\SB{ \mathbf{1}_{S_{ \void_T} >K}}}
\end{align}$$
>
>Note that $S_T = S_t \exp\CB{\P{r- \ffrac{1}{2}\sigma^2}\P{T-t} + \sigma\P{\widetilde W_T -\widetilde W_t}}$, then

>$$S_T\mid \FcF_t \sim S_t \exp\CB{\P{r- \ffrac{1}{2}\sigma^2}\P{T-t} + \sigma\sqrt{T-t}\cdot Z},\bspace Z\sim \N{0,1}$$
>
>$$\begin{align}
\ExpH^\QQ_t\SB{\mathbf{1}_{S_{\void_T}>K}} &= \QQ\P{S_t \exp\CB{\P{r- \ffrac{1}{2}\sigma^2}\P{T-t} + \sigma\sqrt{T-t}\cdot Z} >K}\\
&= \QQ\P{Z>\ffrac{1}{\sigma\sqrt{T-t}} \P{\log\ffrac{K}{S_t} - \P{r-\ffrac{1}{2}\sigma^2}\P{T-t}}}\\
&= \QQ\P{Z>-d_2} = \QQ\P{Z<d_2} = \N{d_2}
\end{align}$$
>
>$$\begin{align}
\ExpH^\QQ_t\SB{S_T \cdot \mathbf{1}_{S_{\void_T}>K}} &= \ExpH^\QQ_t\SB{S_T \cdot \mathbf{1}_{Z>-d_{\void_2}}}\\
&= S_t\int_{-d_{\void_2}}^{+\infty} \exp\CB{\P{r-\ffrac{1}{2}\sigma^2}\P{T-t} + \sigma \sqrt{T-t}\cdot z} \ffrac{1}{\sqrt{2\pi}} \exp\CB{-\ffrac{z^2}{2}}\;\dd z\\
&= S_t e^{r\P{T-t}}\int_{-d_{\void_2}}^{+\infty}\ffrac{1}{\sqrt{2\pi}}\exp\CB{-\ffrac{ \P{z - \sigma\sqrt{T-t}}^2 }{2}}\;\dd z\\
&\bspace\text{let } u = z-\sigma\sqrt{T-t},\text{ and the lower bond or the integral:}\\
&\bspace-d_2 \to -\P{d_2 - \sigma\sqrt{T-t}} = -d_1,\text{ so that}\\
&= S_t e^{r\P{T-t}}\int_{-d_{\void_1}}^{+\infty}\ffrac{1}{\sqrt{2\pi}}\exp\CB{-\ffrac{u^2 }{2}}\;\dd u\\
&= S_t e^{r\P{T-t}} \N{d_1}
\end{align}$$
>
>So finally the formula: $c\P{t,S_t} = S_t\N{d_1} - Ke^{-r\P{T-t}} \N{d_2}$

$Remark$

>- the Delta Hedging, $\Delta = \ffrac{\partial c}{\partial S_t} = \N{d_1}$.
>- the probability that the European call option will be exercised in a risk-neutral world. $\ExpH^\QQ_t\SB{\mathbf{1}_{S_{\void_T}>K}} = \N{d_2}$
>- drift $\mu$ is not contained in the formula, meaning that the price of a European option does not depend on $\mu$, where $\mu$ is hard to estimate in pratice.
>- From $\dd S_t = \mu S_t + \sigma S_t \dd W_t$ to $\dd S_t = r S_t + \sigma S_t \dd \widetilde W_t$, we need to assume $\widetilde W_t = W_t + \ffrac{\mu-r}{\sigma}t$. We call $\ffrac{\mu-r}{\sigma}$, denoted as $\theta$, the ***sharpe ratio***, or ***market price of risk***, ***risk premium*** or ***relative risk***.

***
For a european put option, the **Black-Scholes-Merton equation** is:

$$\ffrac{\partial p}{\partial t} + \ffrac{1}{2} \sigma^2 S_t^2 \ffrac{\partial^2 p}{\partial S_t^2} + rS_t \ffrac{\partial p}{\partial S_t} - rp = 0$$

with **terminal condition** $p\P{T,S_T} = \max\P{K-S_T,0}$. Similarly, we drive the formula for $p$:

$$p\P{t,S_t} = Ke^{-r\P{T-t}} \N{-d_2} - S_t\N{-d_1}$$

We can also derive this by the put-call parity:

$$\begin{align}
p\P{t,S_t} &= c\P{t,S_t} + Ke^{-r\P{T-t}} - S_t \\
&= S_t\N{d_1} - Ke^{-r\P{T-t}} \N{d_2} + Ke^{-r\P{T-t}} - S_t\\
&= -S_t\P{1-\N{d_1}} + Ke^{-r\P{T-t}}\P{1-\N{d_2}}\\
&= Ke^{-r\P{T-t}} \N{-d_2} - S_t\N{-d_1}
\end{align}$$

## Volatility

In BS model, the input data consists of the parameters $S_t$, $r$, $T$, $t$, and $\sigma$. To apply the model, we need to estimate the parameter $\sigma$. We could use ***historic volatility***, or ***implied volatility***.

### [Historic Volatility](#Estimating-Volatility-from-Historical-Data)
The standard approach is to use historical data for a period of the *same length as the time to maturity*. Like we we are evaluating a European call with $6$ months left to maturity, we'll retrive the data of the last $6$ months.

### Implied Volatility
Implied volatility is the value of $σ$ that matches the theoretical BS price of the option with the observed market price of the option. We first let the benchmark option price be $\tilde n$ and write the BS pricing formula for European call options by $c\P{t,S_t, T,r,\sigma, K}$. Then the implied volatility can be found by solving

$$\tilde c = c\P{t,S_t, T,r,\sigma, K}$$

Newton method is a practical way to find the root. And there's only one solution due to the fact that $\ffrac{\partial c}{\partial \sigma} > 0$. In later Chapter, we'll see that there's a smile pattern for **implied volatility** on the **strike price** $S_t$.

## Variations on Black-Scholes-Merton
### Dividends Paid Continuously
Assume the stock pays a dividend at a continuous rate $q$. In the infinitesimal time interval $[t,t+\dd t)$, the holder of the stock receives a dividend payment of $qS_t\dd t$. Thus, the cumulative dividend to time $t$ ends up with $\d{\int_0^t qS_u\;\dd u}$

#### Method 1: Combination of dynamic replication and risk-neutral valuation
We first write the total value of holding $1$ share of stock,

$$G_t := S_t + \int_0^t qS_u\;\dd u$$

Therefore, the wealth process of investing in this stock and the bond is

$$\begin{align}
\dd V_t &= \ffrac{\pi_t}{S_t}\dd G_t + \ffrac{V_t - \pi_t}{B_t}\dd B_t\neq \ffrac{\pi_t}{S_t}\dd S_t + \ffrac{V_t - \pi_t}{B_t}\dd B_t\\
&= \P{r V_t + \P{\mu+q-r}\pi_t}\dd t + \sigma \pi_t \dd W_t
\end{align}$$

To get the **discounted wealth process** to be a martingale, that is $\dd V_t = rV_t \dd t + \sigma\pi_t \dd \widetilde W_t$, we now need to have $\widetilde W_t = W_t + \ffrac{\mu+q-r}{\sigma}t$. This makes the stock dynamics

$$\dd S_t = \P{r-q} S_t \dd t + \sigma S_t \dd \widetilde W_t = \mu S_t \dd t + \sigma S_t \dd \widetilde W_t$$

Then the price of the European call option with continuous dividends paid is $\ExpH_t^\QQ \SB{e^{-r\P{T-t}}\P{S_T - K}^+}$. By **Feynman-Kac theorem**, the pricing PDE becomes

$$\ffrac{\partial c}{\partial t} + \ffrac{1}{2} \sigma^2 S_t^2 \ffrac{\partial^2 c}{\partial S_t^2} + \P{r-q}S_t \ffrac{\partial c}{\partial S_t} - rc = 0$$

Solve the equation we have

$$c_{\text{dividend}}\P{t,S_t} = S_t e^{-q\P{T-t}}\N{d_1} -Ke^{-r\P{T-t}}\N{d_2}$$

where

$$\begin{align}
d_1 &= \ffrac{1}{\sigma\sqrt{T-t}} \P{\log\P{\ffrac{S_t}{K}} + \P{r-q\,\mathbf+\,\ffrac{\sigma^2}{2}} \P{T-t}}\\
d_2 &= \ffrac{1}{\sigma\sqrt{T-t}} \P{\log\P{\ffrac{S_t}{K}} + \P{r-q\,\mathbf-\,\ffrac{\sigma^2}{2}} \P{T-t}}\\
&= d_1 - \sigma\sqrt{T-t}
\end{align}$$

#### Method 2: Risk-nuetral valuation

First recall that $\dd S_t = \mu S_t \dd t + \sigma S_t \dd W_t$ and $S_t = S_0 \cdot \exp\CB{\P{\mu - \ffrac{\sigma^2}{2}}t +\sigma W_t}$. Now given extra dividends, $\CB{S_t}_{t\geq 0}$ doesn't represent the true value of the stock at time $t$. In other words, if we buy the stock at time $0$ for $S_0$, $S_t$ is not *tradalble* at time $t$, since the value of holding is the sum of $S_t-S_0$ and the cumulative dividends.

However this situation can be solved using the strategy that immediately reinvest the dividend in the stock. The infinitesimal payout $qS_t \dd t$ will buy $q\dd t$ units of stock. Thus, at time $t$, we now hold

$$Y_t = e^{qt} S_t = S_0 \exp\CB{\P{\mu - \ffrac{\sigma^2}{2} + q}t + \sigma W_t}$$

To value, we need to find the measure $\QQ$ under which the discounted stock price $\CB{\tilde Y_t}_{t\geq }$ is a martingale. First we write

$$\dd Y_t = \P{\mu + q}Y_t\dd t + \sigma Y_t \dd W_t \Rightarrow \dd \tilde Y_t = \P{\mu +q -r}\tilde Y_t\dd t + \sigma \tilde Y_t \dd W_t \equiv \sigma \tilde Y_t \dd \widetilde W_t $$

Since under $\QQ$ measure $\dd Y_t = rY_t\dd t + \sigma Y_t \dd\widetilde W_t$, we define $\QQ$

$$\given{\ffrac{\dd \QQ}{\dd \PP}}_{\FcF_t} = \exp\CB{-\theta W_t - \ffrac{1}{2}\theta^2 t}, \bspace \theta = \ffrac{\mu + q - r}{\sigma}$$

Under this, we have

$$\begin{align}
c\P{t,S_t} &= \ExpH^\QQ\SB{e^{-r\P{T-t}}c\P{T,S_T}\mid \FcF_t}\\
&= \ExpH^\QQ_t \SB{e^{-r\P{T-t}}\P{S_T-K}^+}\\
&= \ExpH^\QQ_t \SB{e^{-r\P{T-t}-qT}\P{Y_T-Ke^{qT}}^+}
\end{align}$$

$Remark$

>Let $S_t$ denote the stock price with dividend and $S_t^0$ represent the stock price without dividend. Then
>
>$$\begin{align}
\dd S_t &= \P{r-q} S_t \dd t + \sigma S_t \dd \widetilde W_t\\
\dd S_t^0 &= r S_t^0 \dd t + \sigma S_t^0 \dd \widetilde W_t\\
\end{align}$$
>
>Note that $\dd\P{S_t^0 e^{q\P{T-t}}} = S_t^0 e^{q\P{T-t}} \P{\P{r-q}\dd t + \sigma\dd\widetilde W_t }$. So if we let $S_t = S_t^0 e^{q\P{T-t}}$, such that $S_T^0 = S_T$, the option price with dividend would be
>
>$$\begin{align}
c_{\text{dividend}} &= \ExpH^\QQ_t\SB{e^{-r\P{T-t}} \P{S_T-K}^+ }\\
&= \ExpH^\QQ_t\SB{e^{-r\P{T-t}} \P{S_T^0-K}^+ }\\
&= c\P{t,S_t^0} = c\P{t,S_t e^{-q\P{T-t}}}
\end{align}$$
>
>Thus, $c_{\text{dividend}} = c\P{t,S_t e^{-q\P{T-t}}}$. The *price of call option with dividend* is **equal** to the *price of call option without dividend, but with the stock price $S_t$ replaced by $S_t e^{-q\P{T-t}}$*.

### Currency Options in the B-S-M Model

Consider a currency option that give its owner the right to buy $1$ unit of a foreign currency for $K$ units of the domestic currency at maturity $T$. Thus the payoff would be, as evaluated in the domestic currency, is euqal to $\P{R_T - K}^+$, where $R_T$ is the exchange rate at time $T$, $i.e.$, the domestic value of one unit of foreign currency at time $T$.

We now assume the **exchange rate process** $\CB{R_t}_{t\geq 0}$ satisfies the **Black-Scholes model**,

$$\dd R_t = R_t \SB{\mu_R \dd t + \sigma_R \dd W_t} = \mu_R R_t \dd t + \sigma_R R_t \dd W_t$$

Let the foreign risk-free rate be $r_f$, then the price of the European call currency option is the same as in the case of a dividend-paying underlying, but with $q$ replaced by $r_f$, and with the *dividend* replaced by *interest*. Thus, the domestic value of the one unit of the foreign account is

$$\dd R_t^{\,f} := \dd\P{R_t^{\,f} e^{r_{\void_f} \cdot t}} = \P{\mu_R + r_f}R_t^{\,f}\dd t + \sigma_R R_t^{\,f} \dd W_t $$

To replicate, we consider the wealth dynamic (in domestic currency) of a portfolio with $\pi_t$ in the foreign account and the rest in the domestic account, that is

$$\dd V_t = \ffrac{\pi_t}{R_t^{\,f}} \dd R_t^{\,f} + \ffrac{V_t - \pi_t}{B_t} \dd B_t= \P{r_d V_t + \pi_t \P{\mu_R + r_f - r_d}}\dd t + \pi_t \sigma_R \dd W_t$$

where $r_d$ is the domestic risk-free rate. And this dynamics is the the same as the case of a dividend-paying with $q$ replaced by $r_f$. Thus we have the solution of the option price, by applying the risk-neutral method then,

$$\begin{align}
c\P{t,R_t} &= R_t \exp\CB{-r_f\P{T-t}}\N{d_1} - K\exp\CB{-r_d\P{T-t}}\N{d_2}\\
d_1 &= \ffrac{1}{\sigma_R\sqrt{T-t}} \P{\log\P{\ffrac{R_t}{K}} + \P{r_d - r_f + \ffrac{\sigma^2}{2}}\P{T-t}}\\
d_2 &= \ffrac{1}{\sigma_R\sqrt{T-t}} \P{\log\P{\ffrac{R_t}{K}} + \P{r_d - r_f - \ffrac{\sigma^2}{2}}\P{T-t}}
\end{align}$$

FinMath/Models and Pricing of Financial Derivatives/Chap_04_The_Black-Scholes-Merton_model.ipynb


```python
from sympy import *
```

```python
s=10
```

```python
x = Symbol("x")
```

```python
y = x
#y = (2*x-1)
```

```python
legpols = []
```

```python
for k in range(0,s+1):
    legpol = 0
    for j in range(0, k//2+1):
        legpol += (-1)**j * factorial(2*k - 2*j) / factorial(k-j) / factorial(k-2*j) / factorial(j) / 2**k * y**(k-2*j)
    legpols.append(expand(legpol))
```

```python
legpols
```

    [1,
     x,
     3*x**2/2 - 1/2,
     5*x**3/2 - 3*x/2,
     35*x**4/8 - 15*x**2/4 + 3/8,
     63*x**5/8 - 35*x**3/4 + 15*x/8,
     231*x**6/16 - 315*x**4/16 + 105*x**2/16 - 5/16,
     429*x**7/16 - 693*x**5/16 + 315*x**3/16 - 35*x/16,
     6435*x**8/128 - 3003*x**6/32 + 3465*x**4/64 - 315*x**2/32 + 35/128,
     12155*x**9/128 - 6435*x**7/32 + 9009*x**5/64 - 1155*x**3/32 + 315*x/128,
     46189*x**10/256 - 109395*x**8/256 + 45045*x**6/128 - 15015*x**4/128 + 3465*x**2/256 - 63/256]

```python

```