```python
import numpy as np
import scipy as sp
import sympy as sm
from scipy import linalg
from scipy import optimize
from scipy import interpolate
import matplotlib.pyplot as plt
sm.init_printing(use_unicode=True)
%matplotlib inline
from mpl_toolkits.mplot3d import Axes3D
```

# 1. Human capital accumulation

Consider a worker living in **two periods**, $t \in \{1,2\}$.

In each period she decides whether to **work ($l_t = 1$) or not ($l_t = 0$)**.

She can *not* borrow or save and thus **consumes all of her income** in each period.

If she **works** her **consumption** becomes:

$$c_t = w h_t l_t\,\,\text{if}\,\,l_t=1$$

where $w$ is **the wage rate** and $h_t$ is her **human capital**.

If she does **not work** her consumption becomes:

$$c_t = b\,\,\text{if}\,\,l_t=0$$

where $b$ is the **unemployment benefits**.

Her **utility of consumption** is:

$$ \frac{c_t^{1-\rho}}{1-\rho} $$

Her **disutility of working** is:

$$ \gamma l_t $$

From period 1 to period 2, she **accumulates human capital** according to:

$$ h_2 = h_1 + l_1 +
\begin{cases}
0 & \text{with prob. }0.5 \\
\Delta & \text{with prob. }0.5
\end{cases} \\
$$

where $\Delta$ is a **stochastic experience gain**.

In the **second period** the worker thus solves:

$$
\begin{eqnarray*}
v_{2}(h_{2}) & = &\max_{l_{2}} \frac{c_2^{1-\rho}}{1-\rho} - \gamma l_2
\\ & \text{s.t.} & \\
c_{2}& = & w h_2 l_2 \\
l_{2}& \in &\{0,1\}
\end{eqnarray*}
$$

In the **first period** the worker thus solves:

$$
\begin{eqnarray*}
v_{1}(h_{1}) &=& \max_{l_{1}} \frac{c_1^{1-\rho}}{1-\rho} - \gamma l_1 + \beta\mathbb{E}_{1}\left[v_2(h_2)\right]
\\ & \text{s.t.} & \\
c_1 &=& w h_1 l_1 \\
h_2 &=& h_1 + l_1 + \begin{cases}
0 & \text{with prob. }0.5\\
\Delta & \text{with prob. }0.5
\end{cases}\\
l_{1} &\in& \{0,1\}\\
\end{eqnarray*}
$$

where $\beta$ is the **discount factor** and $\mathbb{E}_{1}\left[v_2(h_2)\right]$ is the **expected value of living in period two**.

The **parameters** of the model are:

```python
rho = 2
beta = 0.96
gamma = 0.1
w = 2
b = 1
Delta = 0.1
```

The **relevant levels of human capital** are:

```python
h_vec = np.linspace(0.1,1.5,100)

# Define Labor as boolean
l = [0,1]
```

### Question 1.1:
*Solve the model in period 2 and illustrate the solution (including labor supply as a function of human capital).*

```python
# i. Define utility in second period
def v2(l, h):

    # a. Define c
    if l == 0:
        c = b
    else:
        c = w*h*l

    # b. Define utility in second period
    utility = c**(1-rho)/(1-rho) - gamma*l

    return utility
```

```python
# ii. Define function to solve for utility and labour for each level of human capital
def solve(period):

    # a. Define period and utility
    if period == 2:
        utility = v2
    elif period == 1:
        utility = v1

    # b. Define empty grids for h, v and l
    h_vec = np.linspace(0.1,1.5,100)
    v_vec = np.empty(100)
    l_vec = np.empty(100)

    # c. Solve for each h2
    for i,h in enumerate(h_vec):

        # d. Individual will only work if utillity when working > utility when not working
        v_vec[i] = max(utility(l[0],h), utility(l[1],h))
        l_vec[i] = utility(l[0],h) < utility(l[1],h)

    # e. Return values
    if period == 2:
        v2_vec = v_vec
        l2_vec = l_vec
        return h_vec, v2_vec, l2_vec

    if period == 1:
        v1_vec = v_vec
        l1_vec = l_vec
        return h_vec, v1_vec, l1_vec
```

```python
# iii. Extract solved values for period 2
h_vec,v2_vec,l2_vec = solve(2)
```

```python
# iiii. Define fucntion that plots utility and labor function for a given period
def plot(period):

    # a. Period
    if period == 1:
        l_vec = l1_vec
        v_vec = v1_vec
    elif period == 2:
        l_vec = l2_vec
        v_vec = v2_vec

    # b. Set approprate style
    plt.style.use("bmh")

    # c. Define axis and plots
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))

    ax1.plot(h_vec,l_vec)
    ax1.set_xlabel("$h_{}$".format(period))
    ax1.set_ylabel('$l_{}$'.format(period))
    ax1.set_title('$h_{}$ as a function of $l_{}$'.format(period, period))

    ax2.plot(h_vec,v_vec)
    ax2.set_xlabel('$h_{}$'.format(period))
    ax2.set_ylabel('$v_{}$'.format(period))
    ax2.set_title('$V_{}(h_{})$'.format(period, period))

    # d. Define cut-off point for which the individual will choose to work (l=1)
    cut_off = h_vec[np.where(l_vec==1)[0][0]]

    # e. Plot vertical line at cutoff point and make adjustments
    plt.axvline(x=cut_off, linestyle="--", ymin=0, ymax=0.85)
    plt.subplots_adjust(bottom=0.15, wspace=.25)

    plt.show()
```

```python
# iiiii. Call plots for period 2
plot(2)
```

```python
# iiiiii. Define function that calculates the condition for l = 1
def cutoff(period):
    if period == 1:
        l_vec = l1_vec
    elif period == 2:
        l_vec = l2_vec

    cutoff = round(h_vec[np.where(l_vec==1)[0][0]], 2)

    print("l = 1 in period {}, if and only if h ≥ {}".format(period, cutoff))
```

```python
# iiiiiii. Condition for l=1, in period 2
cutoff(2)
```

    l = 1 in period 2, if and only if h ≥ 0.57

### Question 1.2:
*Solve the model in period 1 and illustrate the solution (including labor supply as a function of human capital).*

```python
# i. interpolar, to be used for calculation of v2
v2_intpol = interpolate.RegularGridInterpolator((h_vec,), v2_vec, bounds_error=False,fill_value=None)
```

```python
# ii. Define utility for period 1
def v1(l1, h1, intpol = v2_intpol):

    # a. Calculate expected value of 2, given v2 with and without stochastic gain
    exp_v2 = 0.5*(v2_intpol([h1 + l1])[0] + v2_intpol([h1 + l1 + Delta])[0])

    # b. Define c
    if l1 == 0:
        c = b
    else:
        c = w*h1*l1

    # c. Define Utility
    utility = c**(1-rho)/(1-rho) - gamma*l1  + beta*exp_v2

    return utility
```

```python
# iii. Extract solved values for period 1
h_vec,v1_vec,l1_vec = solve(1)
```

```python
# iiii. Call plots for period 1
plot(1)
```

```python
# iiiii. Condition for l=1, in period 2
cutoff(1)
```

    l = 1 in period 1, if and only if h ≥ 0.35

### Question 1.3:
*Will the worker never work if her potential wage income is lower than the unemployment benefits she can get? Explain and illustrate why or why not.*

If the petential wage income for the worker is lower than the employment benefits she receives, b, then the worker will never chose to work, as she could receive the benefit of not working instead. This is especially true due to the disutility associated with working, $-\gamma l_2$. Thus the worker would never work.

# 2. AS-AD model

Consider the following **AS-AD model**. The **goods market equilibrium** is given by

$$ y_{t} = -\alpha r_{t} + v_{t} $$

where $y_{t}$ is the **output gap**, $r_{t}$ is the **ex ante real interest** and $v_{t}$ is a **demand disturbance**.

The central bank's **Taylor rule** is

$$ i_{t} = \pi_{t+1}^{e} + h \pi_{t} + b y_{t}$$

where $i_{t}$ is the **nominal interest rate**, $\pi_{t}$ is the **inflation gap**, and $\pi_{t+1}^{e}$ is the **expected inflation gap**.

The **ex ante real interest rate** is given by

$$ r_{t} = i_{t} - \pi_{t+1}^{e} $$

Together, the above implies that the **AD-curve** is

$$ \pi_{t} = \frac{1}{h\alpha}\left[v_{t} - (1+b\alpha)y_{t}\right]$$

Further, assume that the **short-run supply curve (SRAS)** is given by

$$ \pi_{t} = \pi_{t}^{e} + \gamma y_{t} + s_{t}$$

where $s_t$ is a **supply disturbance**.

**Inflation expectations are adaptive** and given by

$$ \pi_{t}^{e} = \phi\pi_{t-1}^{e} + (1-\phi)\pi_{t-1}$$

Together, this implies that the **SRAS-curve** can also be written as

$$ \pi_{t} = \pi_{t-1} + \gamma y_{t} - \phi\gamma y_{t-1} + s_{t} - \phi s_{t-1} $$

The **parameters** of the model are:

```python
par = {}

par['alpha'] = 5.76
par['h'] = 0.5
par['b'] = 0.5
par['phi'] = 0
par['gamma'] = 0.075

#Define variables
# Define variables and parameters
alpha = sm.symbols("alpha")
b = sm.symbols("b")
gamma = sm.symbols("gamma")
h = sm.symbols("h")
phi = sm.symbols("phi")
pi = sm.symbols("pi_t")
pit = sm.symbols("pi_t-1")
pi_opt = sm.symbols("pi^opt")
s = sm.symbols("s_t")
st = sm.symbols("s_t-1")
v = sm.symbols("v_t")
y = sm.symbols("y_t")
yt = sm.symbols("y_t-1")
y_opt = sm.symbols("y^opt")
```

### Question 2.1:
*Use the ``sympy`` module to solve for the equilibrium values of output, $y_t$, and inflation, $\pi_t$, (where AD = SRAS) given the parameters ($\alpha$, $h$, $b$, $\alpha$, $\gamma$) and $y_{t-1}$ , $\pi_{t-1}$, $v_t$, $s_t$, and $s_{t-1}$.*

```python
# i. Define agregate demand (AD) and aggregate supply (AS)
ad = sm.Function("ad")
sras = sm.Function("sras")

ad_func = 1/(h*alpha)*(v-(1+b*alpha)*y)
sras_func = pit + gamma*y - phi*gamma*yt + s - phi*st

ad = sm.Eq(pi, ad_func)
sras = sm.Eq(pi, sras_func)
```

We can now solve for $\pi^{opt}$ and $y^{opt}$ and the result is as follows

```python
# ii. Solving the with the first order condition
foc = sm.solve([ad, sras], [y, pi])
```

```python
# iii. Y in equilibrium
y_foc = sm.Eq(y_opt, foc[y])
y_foc
```

```python
# iiii. Pi in equilibrium
pi_foc = sm.Eq(pi_opt, foc[pi])
pi_foc
```

# 3. Exchange economy

Consider an **exchange economy** with

1. 3 goods, $(x_1,x_2,x_3)$
2. $N$ consumers indexed by \\( j \in \{1,2,\dots,N\} \\)
3. Preferences are Cobb-Douglas with log-normally distributed coefficients

    $$ \begin{eqnarray*}
    u^{j}(x_{1},x_{2},x_{3}) &=&
    \left(x_{1}^{\beta_{1}^{j}}x_{2}^{\beta_{2}^{j}}x_{3}^{\beta_{3}^{j}}\right)^{\gamma}\\
     &  & \,\,\,\beta_{i}^{j}=\frac{\alpha_{i}^{j}}{\alpha_{1}^{j}+\alpha_{2}^{j}+\alpha_{3}^{j}} \\
     &  & \,\,\,\boldsymbol{\alpha}^{j}=(\alpha_{1}^{j},\alpha_{2}^{j},\alpha_{3}^{j}) \\
     &  & \,\,\,\log(\boldsymbol{\alpha}^j) \sim \mathcal{N}(\mu,\Sigma) \\
    \end{eqnarray*} $$

4. Endowments are exponentially distributed,

$$
\begin{eqnarray*}
\boldsymbol{e}^{j} &=& (e_{1}^{j},e_{2}^{j},e_{3}^{j}) \\
 &  & e_i^j \sim f, f(z;\zeta) =  1/\zeta \exp(-z/\zeta)
\end{eqnarray*}
$$

Let $p_3 = 1$ be the **numeraire**. The implied **demand functions** are:

$$
\begin{eqnarray*}
x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})&=&\beta^{j}_i\frac{I^j}{p_{i}} \\
\end{eqnarray*}
$$

where consumer $j$'s income is

$$I^j = p_1 e_1^j + p_2 e_2^j +p_3 e_3^j$$

The **parameters** and **random preferences and endowments** are given by:

```python
# a. parameters
N = 50000
mu = np.array([3,2,1])
Sigma = np.array([[0.25, 0, 0], [0, 0.25, 0], [0, 0, 0.25]])
gamma = 0.8
zeta = 1

# b. random draws
seed = 1986
np.random.seed(seed)

# preferences
alphas = np.exp(np.random.multivariate_normal(mu, Sigma, size=N))
betas = alphas/np.reshape(np.sum(alphas,axis=1),(N,1))

# endowments
e1 = np.random.exponential(zeta,size=N)
e2 = np.random.exponential(zeta,size=N)
e3 = np.random.exponential(zeta,size=N)
```

### Question 3.1:
*Plot the histograms of the budget shares for each good across agents.*

```python
# i. Creating a list for our betas as b1, b2, b3
b1 = betas[:,0]
""" The above creates a list of the variable beta
"""
b2 = betas[:,1]
b3 = betas[:,2]

# ii. Plotting b1, b2, b3
fig=plt.figure(figsize=(20,10))
""" Initializing the figure and the size of it
"""

for i in [1,2,3]:
    """ Looping over i to create a plot for each beta
    """
    ax = fig.add_subplot(1,3,i)
    ax.hist(globals()['b%s' % i],bins=100);
    ax.set_xlabel('b%s' % i)
    ax.set_ylabel('Consumers')
    ax.set_title('Good '+str(i))
fig.tight_layout()
```

Consider the **excess demand functions:**

$$ z_i(p_1,p_2) = \sum_{j=1}^N x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j}) - e_i^j$$

### Question 3.2:
*Plot the excess demand functions.*

```python
# i. Creating the demand function for each good

def demand_1(p1,p2,e1,e2,e3,betas):
    """ Defining af function to calculate the demand for good 1
    """

    I = e1*p1 + e2*p2 + e3
    """ Defining income as specified in the assignment
    """
    return b1*I/p1

def demand_2(p1,p2,e1,e2,e3,betas):
    I = e1*p1 + e2*p2 + e3
    return b2*I/p2

def demand_3(p1,p2,e1,e2,e3,betas):
    I = e1*p1 + e2*p2 + e3
    return b3*I
```

```python
# ii. Creating excess demand function

def excess_demand_1(p1,p2,e1,e2,e3,betas):
    """ Defining the excess function
    """
    excess_1 = np.sum(demand_1(p1,p2,e1,e2,e3,betas)) - np.sum(e1)
    """ excess = demand - supply
    """
    return excess_1

def excess_demand_2(p1,p2,e1,e2,e3,betas):
    excess_2 = np.sum(demand_2(p1,p2,e1,e2,e3,betas)) - np.sum(e2)
    return excess_2

def excess_demand_3(p1,p2,e1,e2,e3,betas):
    excess_3 = np.sum(demand_3(p1,p2,e1,e2,e3,betas)) - np.sum(e3)
    return excess_3
```

```python
# iii.Plotting the excess demand function

p1_ = np.linspace(0.3,30,300)
p2_ = np.linspace(0.3,30,300)
""" Creating price vectors to be used for excess demand plot
"""

p1_grid,p2_grid = np.meshgrid(p1_,p2_,indexing='ij')
""" Creating a grid for p1 and p2
"""

excess_1_grid = np.empty((300,300))
""" Excess demand grid for plots
"""
excess_2_grid = np.empty((300,300))
excess_3_grid = np.empty((300,300))

for i,p1 in enumerate(p1_):
    for j,p2 in enumerate(p2_):
        """ Looping over both price sets to get excess demands for each good
        """
        excess_1_grid[i,j] = excess_demand_1(p1,p2,e1,e2,e3,betas)
        excess_2_grid[i,j] = excess_demand_2(p1,p2,e1,e2,e3,betas)
        excess_3_grid[i,j] = excess_demand_3(p1,p2,e1,e2,e3,betas)
```

```python
fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(1,2,1, projection='3d')
ax.plot_surface(p1_grid, p2_grid, excess_1_grid)
ax.invert_xaxis()
ax.set_title('Excess demand - good 1')
ax.set_xlabel("p1")
ax.set_ylabel("p2")

fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(1,2,2, projection='3d')
ax.plot_surface(p1_grid, p2_grid, excess_2_grid)
ax.invert_xaxis()
ax.set_title('Excess demand - good 2')
ax.set_xlabel("p1")
ax.set_ylabel("p2")
plt.show()
```

### Quesiton 3.3:
*Find the Walras-equilibrium prices, $(p_1,p_2)$, where both excess demands are (approximately) zero, e.g. by using the following tâtonnement process:*

*1. Guess on $p_1 > 0$, $p_2 > 0$ and choose tolerance $\epsilon > 0$ and adjustment aggressivity parameter, $\kappa > 0$.*

*2. Calculate $z_1(p_1,p_2)$ and $z_2(p_1,p_2)$.*

*3. If $|z_1| < \epsilon$ and $|z_2| < \epsilon$ then stop.*

*4. Else set $p_1 = p_1 + \kappa \frac{z_1}{N}$ and $p_2 = p_2 + \kappa \frac{z_2}{N}$ and return to step 2.*

```python
# i. Defining the Walras equilibrium function

def Walras_equilibrium(betas, p1, p2, e1, e2, e3, kappa=0.5, eps=1e-8, maxiter=50000):
        t = 0
        while True:

                # a. step 1: excess demand
                X1 = excess_demand_1(p1,p2,e1,e2,e3,betas)
                X2 = excess_demand_2(p1,p2,e1,e2,e3,betas)

                # b. step 2: stop
                if np.abs(X1) < eps and np.abs(X2) < eps or t >= maxiter:
                        print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {X1:14.8f}')
                        print(f'{t:3d}: p2 = {p2:12.8f} -> excess demand -> {X2:14.8f}')
                        break

                # c. step 3: update prices
                p1 = p1 + kappa*X1/betas.size
                p2 = p2 + kappa*X2/betas.size

                # d. step 4: return
                if t < 5 or t%2500 == 0:
                        print(f'{t:3d}: p1 = {p1:12.8f} -> excess demand -> {X1:14.8f}')
                        print(f'{t:3d}: p2 = {p2:12.8f} -> excess demand -> {X2:14.8f}')
                elif t == 5:
                        print(" ...")

                t += 1

        return p1, p2
```

```python
# ii. Setting initial values to find equilibrium price

p1 = 1.4
p2 = 1
kappa = 0.1
eps = 1e-8
```

```python
# iii. Using our function from part i. and initial values from part ii. to find equlibrium prices

p1_eq,p2_eq = Walras_equilibrium(betas,p1,p2,e1,e2,e3,kappa=kappa,eps=eps)
print("Equilibrium prices (p1,p2) = (6.49, 2.61)")
```

      0: p1 =   1.41865154 -> excess demand -> 27977.31151752
      0: p2 =   0.99608594 -> excess demand -> -5871.09387963
      1: p1 =   1.43684279 -> excess demand -> 27286.87384056
      1: p2 =   0.99241373 -> excess demand -> -5508.31164171
      2: p1 =   1.45459877 -> excess demand -> 26633.97026830
      2: p2 =   0.98897615 -> excess demand -> -5156.36947140
      3: p1 =   1.47194254 -> excess demand -> 26015.65971241
      3: p2 =   0.98576609 -> excess demand -> -4815.08645945
      4: p1 =   1.48889542 -> excess demand -> 25429.30968444
      4: p2 =   0.98277657 -> excess demand -> -4484.28325466
     ...
    2500: p1 =   5.93849485 -> excess demand ->   550.18919081
    2500: p2 =   2.41083774 -> excess demand ->   205.44544903
    5000: p1 =   6.37746401 -> excess demand ->   104.64732030
    5000: p2 =   2.57468743 -> excess demand ->    39.04621324
    7500: p1 =   6.46580779 -> excess demand ->    22.23130435
    7500: p2 =   2.60764817 -> excess demand ->     8.29385995
    10000: p1 =   6.48477769 -> excess demand ->     4.82462641
    10000: p2 =   2.61472520 -> excess demand ->     1.79987803
    12500: p1 =   6.48890390 -> excess demand ->     1.05180255
    12500: p2 =   2.61626452 -> excess demand ->     0.39238367
    15000: p1 =   6.48980388 -> excess demand ->     0.22952650
    15000: p2 =   2.61660027 -> excess demand ->     0.08562665
    17500: p1 =   6.49000030 -> excess demand ->     0.05009851
    17500: p2 =   2.61667354 -> excess demand ->     0.01868963
    20000: p1 =   6.49004317 -> excess demand ->     0.01093546
    20000: p2 =   2.61668953 -> excess demand ->     0.00407956
    22500: p1 =   6.49005253 -> excess demand ->     0.00238701
    22500: p2 =   2.61669303 -> excess demand ->     0.00089049
    25000: p1 =   6.49005458 -> excess demand ->     0.00052104
    25000: p2 =   2.61669379 -> excess demand ->     0.00019438
    27500: p1 =   6.49005502 -> excess demand ->     0.00011373
    27500: p2 =   2.61669395 -> excess demand ->     0.00004243
    30000: p1 =   6.49005512 -> excess demand ->     0.00002483
    30000: p2 =   2.61669399 -> excess demand ->     0.00000926
    32500: p1 =   6.49005514 -> excess demand ->     0.00000542
    32500: p2 =   2.61669400 -> excess demand ->     0.00000202
    35000: p1 =   6.49005514 -> excess demand ->     0.00000118
    35000: p2 =   2.61669400 -> excess demand ->     0.00000044
    37500: p1 =   6.49005515 -> excess demand ->     0.00000026
    37500: p2 =   2.61669400 -> excess demand ->     0.00000010
    40000: p1 =   6.49005515 -> excess demand ->     0.00000006
    40000: p2 =   2.61669400 -> excess demand ->     0.00000002
    42500: p1 =   6.49005515 -> excess demand ->     0.00000001
    42500: p2 =   2.61669400 -> excess demand ->     0.00000000
    42841: p1 =   6.49005515 -> excess demand ->     0.00000001
    42841: p2 =   2.61669400 -> excess demand ->     0.00000000
    Equilibrium prices (p1,p2) = (6.49, 2.61)

### Question 3.4:
*Plot the distribution of utility in the Walras-equilibrium and calculate its mean and variance.

```python
# i. Defining the utility function as u_func
def u_func(p1,p2,e1,e2,e3,betas,gamma):
        """ Creating the utility function
        """

        I = p1*e1 + p2*e2 + e3
        demand1 = b1*I/p1
        demand2 = b2*I/p2
        demand3 = b3*I
        """ Setting up inputs for the calculating utility
        """

        u = (demand1**b1 + demand2**b2 + demand3**b3)**gamma
        return u

# ii. Create a vector of utilities
u_vec = u_func(p1_eq, p2_eq, e1,e2,e3, betas,gamma)

# iii. Plot the utility

fig=plt.figure(figsize=(15,10))
plt.hist(u_vec,bins=100);
plt.xlabel("Utility")
plt.ylabel('Consumers')
plt.title("Utility distribution")

# iiii. Calculate and print mean and variance of the utility
u_mean = np.mean(u_vec)
u_variance = np.var(u_vec)
""" Using the build-in numpy functions to calculate the mean and the variance
"""
print(f"Mean: {u_mean: .3f} and Variance: {u_variance: .3f}")
```

```python

```

examproject/Group DYE Code - Examproject final.ipynb


<p align="center">

</p>

## Interactive Correlation Coefficient Demonstration

### Michael Pyrcz, Associate Professor, University of Texas at Austin

##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

### The Correlation Coefficient Workflow

Here's a simple, interactive workflow to help visualize the correlation coefficient.

* students have asked me about the impact of the correlation coefficient on a bivariate distribution

* to help them understand, I have coded this simple example

We will keep it quite simple:

* we assume the two features are Gaussian distributed, univariate and bivariate

#### Bivariate Analysis

Understand and quantify the relationship between two variables

* example: relationship between porosity and permeability
* how can we use this relationship?

What would be the impact if we ignore this relationship and simply modeled porosity and permeability independently?

* no relationship beyond constraints at data locations
* independent away from data
* nonphysical results, unrealistic uncertainty models

#### Bivariate Statistics

Pearson’s Product‐Moment Correlation Coefficient
* Provides a measure of the degree of linear relationship.
* We refer to it as the 'correlation coefficient'

Let's review the sample variance of variable $x$. Of course, I'm truncating our notation as $x$ is a set of samples a locations in our modeling space, $x(\bf{u_\alpha}), \, \forall \, \alpha = 0, 1, \dots, n - 1$.

\begin{equation}
\sigma^2_{x}  = \frac{\sum_{i=1}^{n} (x_i - \overline{x})^2}{(n-1)}
\end{equation}

We can expand the the squared term and replace on of them with $y$, another variable in addition to $x$.

\begin{equation}
C_{xy}  = \frac{\sum_{i=1}^{n} (x_i - \overline{x})(y_i - \overline{y})}{(n-1)}
\end{equation}

We now have a measure that represents the manner in which variables $x$ and $y$ co-vary or vary together.  We can standardized the covariance by the product of the standard deviations of $x$ and $y$ to calculate the correlation coefficent.

\begin{equation}
\rho_{xy}  = \frac{\sum_{i=1}^{n} (x_i - \overline{x})(y_i - \overline{y})}{(n-1)\sigma_x \sigma_y}, \, -1.0 \le \rho_{xy} \le 1.0
\end{equation}

In summary we can state that the correlation coefficient is related to the covariance as:

\begin{equation}
\rho_{xy}  = \frac{C_{xy}}{\sigma_x \sigma_y}
\end{equation}

The Person's correlation coefficient is quite sensitive to outliers and depature from linear behavoir (in the bivariate sense).  We have an altenrative known as the Spearman's rank correlations coefficient.

\begin{equation}
\rho_{R_x R_y}  = \frac{\sum_{i=1}^{n} (R_{x_i} - \overline{R_x})(R_{y_i} - \overline{R_y})}{(n-1)\sigma_{R_x} \sigma_{R_y}}, \, -1.0 \le \rho_{xy} \le 1.0
\end{equation}

The rank correlation applies the rank transform to the data prior to calculating the correlation coefficent.  To calculate the rank transform simply replace the data values with the rank $R_x = 1,\dots,n$, where $n$ is the maximum value and $1$ is the minimum value.

\begin{equation}
x_\alpha, \, \forall \alpha = 1,\dots, n, \, | \, x_i \ge x_j \, \forall \, i \gt j
\end{equation}

\begin{equation}
R_{x_i} = i
\end{equation}

The corelation coefficients provide useful metrics to quantify relationships between two variables at a time. We can also consider bivariate scatter plots and matrix scatter plots to visualize multivariate data. In general, current practical subsurface modeling is bivariate, two variables at a time.

#### Objective

In the PGE 383: Stochastic Subsurface Modeling class I want to provide hands-on experience with building subsurface modeling workflows. Python provides an excellent vehicle to accomplish this. I have coded a package called GeostatsPy with GSLIB: Geostatistical Library (Deutsch and Journel, 1998) functionality that provides basic building blocks for building subsurface modeling workflows.

The objective is to remove the hurdles of subsurface modeling workflow construction by providing building blocks and sufficient examples. This is not a coding class per se, but we need the ability to 'script' workflows working with numerical methods.

#### Getting Started

Here's the steps to get setup in Python with the GeostatsPy package:

1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/).
2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal.
3. In the terminal type: pip install geostatspy.
4. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality.

You will need to copy the data file to your working directory.  They are available here:

* Tabular data - sample_data.csv at https://git.io/fh4gm.

There are exampled below with these functions. You can go here to see a list of the available functions, https://git.io/fh4eX, other example workflows and source code.

#### Load the required libraries

The following code loads the required libraries.

```python
import geostatspy.GSLIB as GSLIB                       # GSLIB utilies, visualization and wrapper
import geostatspy.geostats as geostats                 # GSLIB methods convert to Python
```

We will also need some standard packages. These should have been installed with Anaconda 3.

```python
%matplotlib inline
import os                                               # to set current working directory
import sys                                              # supress output to screen for interactive variogram modeling
import io
import numpy as np                                      # arrays and matrix math
import pandas as pd                                     # DataFrames
import matplotlib.pyplot as plt                         # plotting
from matplotlib.pyplot import cm                        # color maps
from matplotlib.patches import Ellipse                  # plot an ellipse
import math                                             # sqrt operator
import random                                           # random simulation locations
from copy import copy                                   # copy a colormap
from scipy.stats import norm
from ipywidgets import interactive                      # widgets and interactivity
from ipywidgets import widgets
from ipywidgets import Layout
from ipywidgets import Label
from ipywidgets import VBox, HBox
from scipy.stats import norm                            # Gaussian distribution
import scipy.stats as st                                # statistical methods
```

If you get a package import error, you may have to first install some of these packages. This can usually be accomplished by opening up a command window on Windows and then typing 'python -m pip install [package-name]'. More assistance is available with the respective package docs.

#### Interactive Correlation Coefficient

Draw random values from a bivariate Gaussian distribution parameterized by:

* **$\overline{X}_1$, $\overline{X}_2$** - mean of features $X_1$ and $X_2$

* **$\sigma_{X_1}$,$\sigma_{X_1}$** - standard deviation of features $X_1$ and $X_2$

* **$\rho_{X,Y}$** - Pearson product-moment correlation coefficient

Now let's set up our dash board.

```python
import warnings; warnings.simplefilter('ignore')

# dashboard: number of simulation locations and variogram parameters
style = {'description_width': 'initial'}
l = widgets.Text(value='                                              Correlation Coefficient, Michael Pyrcz, Associate Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))
ndata = widgets.IntSlider(min = 0, max = 500, value = 50, step = 1, description = r'$n_{samples}$',orientation='horizontal',continuous_update=True,
                          layout=Layout(width='600px', height='40px'))
ndata.style.handle_color = 'gray'

corr = widgets.FloatSlider(min = -1.0, max = 1.0, value = 0, step = 0.1, description = r'$\rho_{x_1,x_2}$',orientation='horizontal',continuous_update=True,
                          layout=Layout(width='600px', height='40px'))
corr.style.handle_color = 'gray'

uipars = widgets.HBox([ndata,corr],)

uik = widgets.VBox([l,uipars],)

def f_make(ndata,corr): # function to take parameters, make sample and plot
    text_trap = io.StringIO()                           # suppress all text function output to dashboard to avoid clutter
    sys.stdout = text_trap
    cmap = cm.inferno
    np.random.seed(seed = 73072)                        # ensure same results for all runs
    mean = np.array([0,0])
    correl = np.array([[1.0,corr],[corr,1.0]],dtype=float)
    sample = np.random.multivariate_normal(mean,correl,size = ndata)
    slope, intercept, r_value, p_value, std_err = st.linregress(sample[:,0],sample[:,1])
    x1 = np.array([-3.0,3.0])
    x2 = x1*slope + intercept

    plt_scatter = plt.subplot2grid((3, 3), (1, 0), rowspan=2, colspan=2)
    plt_x1 = plt.subplot2grid((3, 3), (0, 0), colspan=2,
                               sharex=plt_scatter)
    plt_x2 = plt.subplot2grid((3, 3), (1, 2), rowspan=2,
                               sharey=plt_scatter)

    #plt.plot([0,0],[1.0,1.0],color = 'black')
    plt_scatter.plot(x1,x2,color = 'black',label = r'$X_2 = f(X_1)$')
    plt_scatter.scatter(sample[:,0],sample[:,1],color = 'red',alpha = 0.5,edgecolors='black',label = 'Samples')
    plt_scatter.set_xlabel(r'$x_1$')
    plt_scatter.set_ylabel(r'$x_2$')
    plt_scatter.set_xlim([-3.0,3.0])
    plt_scatter.set_ylim([-3.0,3.0])
    plt_scatter.legend(loc='upper left')

    plt_x1.hist(sample[:,0],density = True,color='red',alpha=0.2,edgecolor='black',bins=np.linspace(-3.0,3.0,10))
    plt_x1.set_ylim([0.0,0.8])
    plt_x1.set_xlabel(r'$x_1$'); plt_x1.set_ylabel(r'Density')
    plt_x1.set_title(r'Bivariate Standard Gaussian Distributed Data with $\rho =$' + str(corr) + '.')

    plt_x2.hist(sample[:,1],orientation='horizontal',density = True,color='red',alpha=0.2,edgecolor='black',bins=np.linspace(-3.0,3.0,10))
    plt_x2.set_xlim([0.0,0.8])
    plt_x2.set_ylabel(r'$x_2$'); plt_x2.set_xlabel(r'Density')
    plt_scatter.set_ylabel(r'$x_2$')

    plt.subplots_adjust(left=0.0, bottom=0.0, right=1.5, top=1.7, wspace=0.3, hspace=0.3)
    plt.show()

# connect the function to make the samples and plot to the widgets
interactive_plot = widgets.interactive_output(f_make, {'ndata':ndata,'corr':corr,})
#interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating
```

### Interactive Correlation Coefficient Demonstration

* select the number of data and correlation coefficient and observe the samples and linear regression of $X_2 = f(X_1)$

#### Michael Pyrcz, Associate Professor, University of Texas at Austin

##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)

### The Inputs

Select the number of samples and the Pearson product-moment correlation coefficient:

* **$n_{samples}$**: number of samples

* **$\rho_{x_1,x_2}$**: the Pearson product-moment correlation

```python
display(uik, interactive_plot)                            # display the interactive plot
```

    VBox(children=(Text(value='                                              Correlation Coefficient, Michael Pyrc…

    Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': '<Figure size 432x288 with 3 Axes>', 'i…

#### Comments

This was an interactive demonstration of the correlation coefficient. Providing students an opportunity to play with data analytics, geostatistics and machine learning for experiential learning.

#### The Author:

### Michael Pyrcz, Associate Professor, University of Texas at Austin
*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*

With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development.

For more about Michael check out these links:

#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

#### Want to Work Together?

I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.

* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you!

* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!

* I can be reached at mpyrcz@austin.utexas.edu.

I'm always happy to discuss,

*Michael*

Michael Pyrcz, Ph.D., P.Eng. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, The Jackson School of Geosciences, The University of Texas at Austin

#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)

```python

```


# K-Means Clustering

# 1) Use the "Breast Cancer Wisconsin (Diagnostic) Data Set" from Kaggle to try and cluster types of cancer cells.

Here's the original dataset for your reference:

<https://www.kaggle.com/uciml/breast-cancer-wisconsin-data>

## This is a supervised learning dataset

(Because it has **labels** - The "diagnosis" column.)

```
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA # You don't necessarily have to use this
from sklearn.cluster import KMeans # You don't necessarily have to use this
from sklearn.preprocessing import StandardScaler # You don't necessarily have to use this

df = pd.read_csv("https://raw.githubusercontent.com/ryanleeallred/datasets/master/Cancer_Cells.csv")
print(df.shape)
df.head()
```

    (569, 33)

      id
      diagnosis
      radius_mean
      texture_mean
      perimeter_mean
      area_mean
      smoothness_mean
      compactness_mean
      concavity_mean
      concave points_mean
      symmetry_mean
      fractal_dimension_mean
      radius_se
      texture_se
      perimeter_se
      area_se
      smoothness_se
      compactness_se
      concavity_se
      concave points_se
      symmetry_se
      fractal_dimension_se
      radius_worst
      texture_worst
      perimeter_worst
      area_worst
      smoothness_worst
      compactness_worst
      concavity_worst
      concave points_worst
      symmetry_worst
      fractal_dimension_worst
      Unnamed: 32

      0
      842302
      M
      17.99
      10.38
      122.80
      1001.0
      0.11840
      0.27760
      0.3001
      0.14710
      0.2419
      0.07871
      1.0950
      0.9053
      8.589
      153.40
      0.006399
      0.04904
      0.05373
      0.01587
      0.03003
      0.006193
      25.38
      17.33
      184.60
      2019.0
      0.1622
      0.6656
      0.7119
      0.2654
      0.4601
      0.11890
      NaN

      1
      842517
      M
      20.57
      17.77
      132.90
      1326.0
      0.08474
      0.07864
      0.0869
      0.07017
      0.1812
      0.05667
      0.5435
      0.7339
      3.398
      74.08
      0.005225
      0.01308
      0.01860
      0.01340
      0.01389
      0.003532
      24.99
      23.41
      158.80
      1956.0
      0.1238
      0.1866
      0.2416
      0.1860
      0.2750
      0.08902
      NaN

      2
      84300903
      M
      19.69
      21.25
      130.00
      1203.0
      0.10960
      0.15990
      0.1974
      0.12790
      0.2069
      0.05999
      0.7456
      0.7869
      4.585
      94.03
      0.006150
      0.04006
      0.03832
      0.02058
      0.02250
      0.004571
      23.57
      25.53
      152.50
      1709.0
      0.1444
      0.4245
      0.4504
      0.2430
      0.3613
      0.08758
      NaN

      3
      84348301
      M
      11.42
      20.38
      77.58
      386.1
      0.14250
      0.28390
      0.2414
      0.10520
      0.2597
      0.09744
      0.4956
      1.1560
      3.445
      27.23
      0.009110
      0.07458
      0.05661
      0.01867
      0.05963
      0.009208
      14.91
      26.50
      98.87
      567.7
      0.2098
      0.8663
      0.6869
      0.2575
      0.6638
      0.17300
      NaN

      4
      84358402
      M
      20.29
      14.34
      135.10
      1297.0
      0.10030
      0.13280
      0.1980
      0.10430
      0.1809
      0.05883
      0.7572
      0.7813
      5.438
      94.44
      0.011490
      0.02461
      0.05688
      0.01885
      0.01756
      0.005115
      22.54
      16.67
      152.20
      1575.0
      0.1374
      0.2050
      0.4000
      0.1625
      0.2364
      0.07678
      NaN

## Now it's an unsupervised learning dataset

(Because we've removed the diagnosis label) - Use this version.

## Let's do it!

- You might want to do some data exploration to see if you can find specific columns that will help you find distinct clusters of cells
- You might want to use the elbow method to decide on the number of clusters to use.

```
kdf = df.copy()
kdf.drop(columns=['Unnamed: 32','diagnosis','id'],inplace=True)
```

```
# Instantiate the sklearn class, and pick a number of clusters.
kmeans = KMeans(n_clusters=3)
# fit the instantiate model to our data.
kmeans.fit(df)
# Assign clusters back to our dataframe
df['cluster'] = model.labels_
# what's our silhouette score?
metrics.silhouette_score(df, model.labels_, metric='euclidean')
```

```
# create a data frame of just the mean columns from the dataset:
features = kdf.iloc[:,2:10]

plt.scatter(x=features.area_mean,
            y=features.symmetry_mean,
            c=df.diagnosis)

```

```
data_centroid_x = features.area_mean.mean()
data_centroid_y = features.symmetry_mean.mean()
```

```
data_centroid_x,data_centroid_y
```

    (654.8891036906857, 0.181161862917399)

```
plt.scatter(features.area_mean,features.symmetry_mean)
plt.plot(data_centroid_x,data_centroid_y,'or')
```

```
centroids = features[['area_mean','symmetry_mean']].sample(3)
centroids = centroids.reset_index(drop=True)
centroids
```

      area_mean
      symmetry_mean

      0
      440.6
      0.2301

      1
      680.7
      0.1659

      2
      1077.0
      0.1966

```
plt.scatter(features.area_mean,features.symmetry_mean)
plt.plot(centroids.iloc[0].area_mean,centroids.iloc[0].symmetry_mean,'or')
plt.plot(centroids.iloc[1].area_mean,centroids.iloc[1].symmetry_mean,'or')
plt.plot(centroids.iloc[2].area_mean,centroids.iloc[2].symmetry_mean,'or')
```

## Check you work:

This is something that in a truly unsupervised learning situation **WOULD NOT BE POSSIBLE**. But for educational purposes go back and grab the true diagnosis column (label) from the original dataset. Take your cluster labels and compare them to the original diagnosis column. You can make scatterplots for each to see how they compare or you can calculate a percent accuracy score like:
\begin{align}
\frac{\text{Num Correct Labels}}{\text{Num Total Observations}}
\end{align}

```
# Your Code Here
```

# 2) Perform PCA on your dataset first and *then* use k-means clustering.

- You need to standardize your data before PCA.
- First try clustering just on PC1 and PC2 so that you can make a scatterplot of your clustering.
- Then use use a scree plot to decide how many principal components to include in your clustering, and use however many principal components you need in order to retain 90% of the variation of the original dataset

```
# Standardize the data:
scaled = StandardScaler().fit_transform(kdf)
# Apply Priciple component analasys:
pca = PCA(n_components=2)
# Create a datafram using our data from the pca:
pca_df = pd.DataFrame(pca.fit_transform(scaled),columns=['x','y'])

# Start K-means clustering:
# Instansiate our kmeans model:
kmeans = KMeans(2)
# Fit the kmeans model to our data:
kmeans.fit(pca_df)
labels = kmeans.labels_
new_series = pd.Series(labels)
pca_df['clusters'] = new_series.values

def get_centroids(df, column_header):
    new_centroids = df.groupby(column_header).mean()
    return new_centroids

def plot_clusters(dfpoints, column_header, centroids):
    colors = {0:'red', 1:'blue'}
    fig, ax = plt.subplots()
    ax.plot(centroids.loc[:2, 'x'], centroids.loc[:2, 'y'], "ok")
    grouped = dfpoints.groupby(column_header)
    for key, group in grouped:
        group.plot(ax=ax, kind='scatter', x='x', y='y', label=key, color=colors[key])
    plt.show()

centroids = get_centroids(pca_df, 'clusters')
plot_clusters(pca_df, 'clusters', centroids)
```

```
sum_of_squared_distances = []
K = range(1,15)
for k in K:
    km = KMeans(n_clusters=k)
    km = km.fit(pca_df)
    sum_of_squared_distances.append(km.inertia_)

plt.plot(K, sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('Sum_of_squared_distances')
plt.title('Elbow Method For Optimal k')
plt.show()

```

```
plt.scatter(pca_df.x,pca_df.y)
```

## Check your work:

- Compare your PC1, PC2 clustering scatterplot to the clustering scatterplots you made on the raw data
- Calculate accuracy scores for both the PC1,PC2 Principal component clustering and the 90% of explained variance clustering.

How do your accuracy scores -when preprocessing the data with PCA- compare to the accuracy when simply clustering on the raw data?

# Stretch Goals:

- Study for the Sprint Challenge
- Work on your Data Storytelling Project

```

```


```python
import matplotlib.pyplot as plt
import numpy as np
import sympy as sym

import models
import payoffs
import plotting
import selection_functions
import symbolics
```

```python
x1, x2, x3 = sym.symbols('x1, x2, x3', real=True, positive=True)
x4 = 1 - x1 - x2 - x3
T, R, P, S = sym.symbols('T, R, P, S', real=True, positive=True)
M, epsilon = sym.symbols("M, epsilon", real=True, positive=True)

UGA = symbolics.UGA
UgA = symbolics.UgA

UG, Ug = sym.symbols("UG, Ug", real=True, nonnegative=True)
```

# $M=0$, $\epsilon=0$

## Monomorphic G

```python
x = np.array([[x1], [1-x1], [0], [0]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, UGA, UgA, payoff_kernel)
N, = models.total_offspring(W, x)
```

```python
sym.factor(N, UGA(x1))
```

$\displaystyle 4.0 \left(0.5 P + \left(- 1.0 P + 0.5 S + 0.5 T\right) \operatorname{UGA}{\left(x_{1} \right)} + \left(0.5 P + 0.5 R - 0.5 S - 0.5 T\right) \operatorname{UGA}^{2}{\left(x_{1} \right)}\right)$

```python
(f,), _, _, _ = models.offspring_genotypes_evolution(W, x)
```

```python
# solving for the LPE using Python
x_star, = sym.solve(f, x1, implicit=True)
x_star =  sym.cancel(x_star.subs({UGA(x1): UG}))
```

```python
sym.factor(x_star, UG)
```

$\displaystyle \frac{0.5 UG \left(S + UG \left(R - S\right)\right)}{0.5 P + UG^{2} \left(0.5 P + 0.5 R - 0.5 S - 0.5 T\right) + UG \left(- 1.0 P + 0.5 S + 0.5 T\right)}$

```python
sym.factor(x_star, UG).subs({UG: 0})
```

$\displaystyle 0$

```python
sym.factor(x_star, UG).subs({UG: 1})
```

$\displaystyle 1.0$

```python
sym.solve(x1 - x_star.subs({UG: x1}), x1)
```

    [1.00000000000000, (P - S)/(P + R - S - T)]

## Monomorphic A

```python
x = np.array([[1-x3-epsilon], [epsilon/2], [x3], [epsilon/2]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, UGA, UgA, payoff_kernel)
N, = models.total_offspring(W, x)
```

```python
sym.limit(N, epsilon, 0)
```

$\displaystyle 2.0 R$

```python
_, _, (f,), _ = models.offspring_genotypes_evolution(W, x)
```

```python
sym.limit(f, epsilon, 0)
```

$\displaystyle 0$

```python
x = np.array([[x1], [x2], [x3], [1 - x1 - x2 - x3]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, UGA, UgA, payoff_kernel, M)

(f1,), (f2,), (f3,), _ = models.offspring_genotypes_evolution(W, x)
F = sym.Matrix([f1, f2, f3])
F_jac = F.jacobian([x1, x2, x3])
```

```python
dUGdxA = sym.symbols("dUGdxA", positive=True, real=True)
dUgdxA = sym.symbols("dUgdxA", positive=True, real=True)
xA = sym.symbols("xA", nonnegative=True, real=True)

evaluated_F_jac = (F_jac.subs({x1 + x3: xA})
                        .doit()
                        .subs({sym.Derivative(UGA(xA), xA): dUGdxA, sym.Derivative(UgA(xA), xA): dUgdxA})
                        .subs({x2: 0, xA: x1 + x3})
                        .subs({UGA(x1 + x3): 1, UgA(x1 + x3): 1})
                        .subs({x3: 1 - x1}))
```

```python
evaluated_F_jac
```

$\displaystyle \left[\begin{matrix}\frac{1.0 \left(1.0 R x_{1} \left(1 - x_{1}\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right)\right) \left(2.0 R x_{1} + 2.0 R \left(1 - x_{1}\right) + 1.0 T dUgdxA \left(1 - x_{1}\right) + 0.5 dUGdxA x_{1} \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) - x_{1} \left(0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - x_{1} \left(- 2.0 M + 2.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - 1.0 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 1.0 R + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 1.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} - 1 + \frac{- 0.5 R x_{1} + 1.0 x_{1} \left(- 2 M + 2 R\right) + x_{1} \left(- 2.0 M + 2.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 1.0 R + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} & \frac{1.0 \left(1.0 R x_{1} \left(1 - x_{1}\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right)\right) \left(2.0 R x_{1} + 2.0 R \left(1 - x_{1}\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) - 1.0 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} + \frac{- 0.5 R x_{1} + 0.5 x_{1} \left(- 2 M + 2 R\right) + 0.25 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} & \frac{1.0 \left(1.0 R x_{1} \left(1 - x_{1}\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right)\right) \left(1.0 T dUgdxA \left(1 - x_{1}\right) + 0.5 dUGdxA x_{1} \left(- 2 M + 2 T\right) - x_{1} \left(- 1.0 M + 1.0 R + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - x_{1} \left(- 1.0 M + 1.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 2.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} + \frac{0.5 R x_{1} + x_{1} \left(- 1.0 M + 1.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) + \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)}\\\frac{- 0.5 R x_{1} - 0.25 dUGdxA x_{1} \left(- 2 M + 2 T\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} & \frac{- 0.5 R x_{1} + 0.5 x_{1} \left(- 2 M + 2 R\right) + 0.25 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} - 1 & \frac{- 0.5 R x_{1} - 0.25 dUGdxA x_{1} \left(- 2 M + 2 T\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)}\\\frac{1.0 \left(0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right) \left(2.0 R x_{1} + 2.0 R \left(1 - x_{1}\right) + 1.0 T dUgdxA \left(1 - x_{1}\right) + 0.5 dUGdxA x_{1} \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) - x_{1} \left(0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - x_{1} \left(- 2.0 M + 2.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - 1.0 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 1.0 R + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 1.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} + \frac{- 0.5 R x_{1} - 1.0 R \left(1 - x_{1}\right) + x_{1} \left(0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 1.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} & \frac{1.0 \left(0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right) \left(2.0 R x_{1} + 2.0 R \left(1 - x_{1}\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) - 1.0 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} + \frac{- 0.5 R x_{1} - 1.0 R \left(1 - x_{1}\right) + 0.25 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} & \frac{1.0 \left(0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right) \left(1.0 T dUgdxA \left(1 - x_{1}\right) + 0.5 dUGdxA x_{1} \left(- 2 M + 2 T\right) - x_{1} \left(- 1.0 M + 1.0 R + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - x_{1} \left(- 1.0 M + 1.0 R + 1.0 dUGdxA x_{1} \left(- 2 M + 2 R\right) + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 1.0 x_{1} \left(- 2 M + 2 R\right) + 1.0 x_{1} \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} - 1.0 R x_{1} + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right)\right) - \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 2.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)\right)}{\left(R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)\right)^{2}} + \frac{0.5 R x_{1} + 1.0 R \left(1 - x_{1}\right) + x_{1} \left(- 1.0 M + 1.0 R + 0.5 dUGdxA \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) - 0.25 dUGdxA \left(- 2 M + 2 T\right) - 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(dUGdxA \left(- 2 M + 2 R\right) - 2 dUGdxA \left(- M + S\right)\right)\right) + \left(1 - x_{1}\right) \left(1.0 R dUgdxA x_{1} + 2.0 R dUgdxA \left(1 - x_{1}\right) - 1.0 R x_{1} - 2.0 R \left(1 - x_{1}\right) + 2.0 R - 1.0 T dUgdxA + 0.5 x_{1} \left(2 R dUgdxA - 2 S dUgdxA\right) + 1.0 \left(1 - x_{1}\right) \left(2 R dUgdxA - 2 S dUgdxA\right)\right)}{1.0 R x_{1} \left(1 - x_{1}\right) + 0.5 x_{1} \left(1 - x_{1}\right) \left(- 2 M + 2 R\right) + x_{1} \left(1.0 x_{1} \left(- 2 M + 2 R\right) + 0.5 \left(1 - x_{1}\right) \left(- 2 M + 2 R\right)\right) + \left(1 - x_{1}\right) \left(1.0 R x_{1} + 2.0 R \left(1 - x_{1}\right)\right)} - 1\end{matrix}\right]$

```python
simplified_F_jac = sym.simplify(evaluated_F_jac)
```

```python
eigenvals = simplified_F_jac.eigenvals()
```

```python
e1, e2, e3 = eigenvals
```

```python
e1.subs({M: 0})
```

$\displaystyle 0$

```python
sym.factor(e2.subs({M: 0}), deep=True)
```

$\displaystyle \frac{- 5 R + 2 T dUGdxA x_{1} - 2 T dUgdxA x_{1} + 2 T dUgdxA + \left|{R + 2 T dUGdxA x_{1} - 2 T dUgdxA x_{1} + 2 T dUgdxA}\right|}{8 R}$

```python
sym.factor(e3.subs({M: 0}), deep=True)
```

$\displaystyle \frac{- 5 R + 2 T dUGdxA x_{1} - 2 T dUgdxA x_{1} + 2 T dUgdxA - \left|{R + 2 T dUGdxA x_{1} - 2 T dUgdxA x_{1} + 2 T dUgdxA}\right|}{8 R}$

```python
sym.factor(R**2 + 4*R*T*dUGdxA*x1 - 4*R*T*dUgdxA*x1 + 4*R*T*dUgdxA + 4*T**2*dUGdxA**2*x1**2 - 8*T**2*dUGdxA*dUgdxA*x1**2 + 8*T**2*dUGdxA*dUgdxA*x1 + 4*T**2*dUgdxA**2*x1**2 - 8*T**2*dUgdxA**2*x1 + 4*T**2*dUgdxA**2)
```

$\displaystyle \left(R + 2 T dUGdxA x_{1} - 2 T dUgdxA x_{1} + 2 T dUgdxA\right)^{2}$

```python

```

```python
x = np.array([[x1], [x2], [x3], [1 - x1 - x2 - x3]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, UGA, UgA, payoff_kernel)
N, = models.total_offspring(W, x)
```

```python
sym.limit(N, epsilon, 0)
```

$\displaystyle 2.0 P$

```python
_, (f,), _, _ = models.offspring_genotypes_evolution(W, x)
```

```python
sym.limit(f, epsilon, 0)
```

$\displaystyle 0$

```python
(f1,), (f2,), (f3,), _ = models.offspring_genotypes_evolution(W, x)
F = sym.Matrix([f1, f2, f3])
F_jac = F.jacobian([x1, x2, x3])
```

```python
dUGdxA = sym.symbols("dUGdxA", positive=True, real=True)
dUgdxA = sym.symbols("dUgdxA", positive=True, real=True)
xA = sym.symbols("xA", nonnegative=True, real=True)

evaluated_F_jac = (F_jac.subs({x1: 0})
      .doit()
      .subs({sym.Derivative(UGA(x3), x3): dUGdxA, sym.Derivative(UgA(x3), x3): dUgdxA})
      .subs({UGA(x3): 0, UgA(x3): 0})
      .subs({x3: 0}))
```

```python
evaluated_F_jac
```

$\displaystyle \left[\begin{matrix}\frac{0.25 dUGdxA x_{2} \left(- 2 M + 2 S\right) + 0.5 x_{2} \left(- 2 M + 2 P\right) + 0.25 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)} - 1 & 0 & \frac{0.5 P x_{2} + 0.25 dUGdxA x_{2} \left(- 2 M + 2 S\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)}\\\frac{1.0 \left(1.0 P x_{2} \left(1 - x_{2}\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right)\right) \left(2.0 P x_{2} + 2.0 P \left(1 - x_{2}\right) - 1.0 S dUgdxA \left(1 - x_{2}\right) - 0.5 dUGdxA x_{2} \left(- 2 M + 2 S\right) - 1.0 x_{2} \left(- 2 M + 2 P\right) - x_{2} \left(1.0 M - 1.0 P - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) - x_{2} \left(1.0 M - 1.0 P - 1.0 dUGdxA x_{2} \left(- 2 M + 2 P\right) - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 1.0 x_{2} \left(- 2 M + 2 P\right) + 1.0 x_{2} \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) - 1.0 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) - \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} + 1.0 P x_{2} + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right) - \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} - 2.0 P dUgdxA \left(1 - x_{2}\right) + 1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right) - 2.0 P + 1.0 S dUgdxA + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right) + 1.0 \left(1 - x_{2}\right) \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right)\right)}{\left(P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)\right)^{2}} + \frac{- 1.0 P x_{2} + 0.5 x_{2} \left(- 2 M + 2 P\right) + x_{2} \left(1.0 M - 1.0 P - 1.0 dUGdxA x_{2} \left(- 2 M + 2 P\right) - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 1.0 x_{2} \left(- 2 M + 2 P\right) + 1.0 x_{2} \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) + 0.25 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} + 1.0 P x_{2} + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)} & \frac{1.0 \left(1.0 P x_{2} \left(1 - x_{2}\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right)\right) \left(2.0 P x_{2} + 2.0 P \left(1 - x_{2}\right) - 1.0 x_{2} \left(- 2 M + 2 P\right) - 1.0 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right)}{\left(P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)\right)^{2}} + \frac{- 1.0 P x_{2} + 1.0 P \left(1 - x_{2}\right) + 1.5 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)} - 1 & \frac{1.0 \left(1.0 P x_{2} \left(1 - x_{2}\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right)\right) \left(- 1.0 S dUgdxA \left(1 - x_{2}\right) - 0.5 dUGdxA x_{2} \left(- 2 M + 2 S\right) - x_{2} \left(1.0 M - 1.0 P - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) - x_{2} \left(1.0 M - 1.0 P - 1.0 dUGdxA x_{2} \left(- 2 M + 2 P\right) - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 1.0 x_{2} \left(- 2 M + 2 P\right) + 1.0 x_{2} \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) - \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} + 1.0 P x_{2} + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right) - \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} - 2.0 P dUgdxA \left(1 - x_{2}\right) + 1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right) - 2.0 P + 1.0 S dUgdxA + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right) + 1.0 \left(1 - x_{2}\right) \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right)\right)}{\left(P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)\right)^{2}} + \frac{- 0.5 P x_{2} + x_{2} \left(1.0 M - 1.0 P - 1.0 dUGdxA x_{2} \left(- 2 M + 2 P\right) - 0.5 dUGdxA \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.25 dUGdxA \left(- 2 M + 2 S\right) + 1.0 x_{2} \left(- 2 M + 2 P\right) + 1.0 x_{2} \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(dUGdxA \left(- 2 M + 2 T\right) - 2 dUGdxA \left(- M + P\right)\right)\right) + \left(1 - x_{2}\right) \left(- 1.0 P dUgdxA x_{2} + 1.0 P x_{2} + 0.5 x_{2} \left(- 2 P dUgdxA + 2 T dUgdxA\right)\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)}\\\frac{1.0 S dUgdxA \left(1 - x_{2}\right) + 0.25 dUGdxA x_{2} \left(- 2 M + 2 S\right) + 0.25 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)} & 0 & \frac{0.5 P x_{2} + 1.0 P \left(1 - x_{2}\right) + 1.0 S dUgdxA \left(1 - x_{2}\right) + 0.25 dUGdxA x_{2} \left(- 2 M + 2 S\right)}{1.0 P x_{2} \left(1 - x_{2}\right) + 0.5 x_{2} \left(1 - x_{2}\right) \left(- 2 M + 2 P\right) + x_{2} \left(1.0 x_{2} \left(- 2 M + 2 P\right) + 0.5 \left(1 - x_{2}\right) \left(- 2 M + 2 P\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)} - 1\end{matrix}\right]$

```python
eigenvals = evaluated_F_jac.eigenvals()
```

```python
e1, e2, e3 = eigenvals
```

```python
e1
```

$\displaystyle - \frac{3}{4}$

```python
sym.simplify(e2)
```

$\displaystyle \frac{- P + S dUGdxA x_{2} - S dUgdxA x_{2} + S dUgdxA}{2 P}$

```python
e3
```

$\displaystyle 0$

## Monomorphic g

```python
alpha = sym.symbols("alpha")
x = np.array([[0], [0], [x3], [1 - x3]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, lambda x: x**alpha, lambda x: x, payoff_kernel)
N, = models.total_offspring(W, x)
```

```python
N
```

$\displaystyle 0.5 x_{3} \left(1 - x_{3}\right) \left(2 P \left(1 - x_{3}\right) + 2 T x_{3}\right) + 0.5 x_{3} \left(1 - x_{3}\right) \left(2 R x_{3} + 2 S \left(1 - x_{3}\right)\right) + x_{3} \left(1.0 x_{3} \left(2 R x_{3} + 2 S \left(1 - x_{3}\right)\right) + 0.5 \left(1 - x_{3}\right) \left(2 P \left(1 - x_{3}\right) + 2 T x_{3}\right)\right) + \left(1 - x_{3}\right) \left(0.5 x_{3} \left(2 R x_{3} + 2 S \left(1 - x_{3}\right)\right) + 1.0 \left(1 - x_{3}\right) \left(2 P \left(1 - x_{3}\right) + 2 T x_{3}\right)\right)$

```python
_, _, (f,), _ = models.offspring_genotypes_evolution(W, x)
```

```python
# solving for the LPE using Python
x_star, = sym.solve(f, x3, implicit=True)
x_star =  sym.cancel(x_star.subs({UgA(x3): Ug}))
```

```python
sym.factor(x_star, Ug)
```

$\displaystyle \frac{0.5 Ug \left(S + Ug \left(R - S\right)\right)}{0.5 P + Ug^{2} \left(0.5 P + 0.5 R - 0.5 S - 0.5 T\right) + Ug \left(- 1.0 P + 0.5 S + 0.5 T\right)}$

```python
sym.factor(x_star, Ug).subs({Ug: 0})
```

$\displaystyle 0$

```python
sym.factor(x_star, Ug).subs({Ug: 1})
```

$\displaystyle 1.0$

```python
sym.solve(x3 - x_star.subs({Ug: x3}), x3)
```

    [1.00000000000000, (P - S)/(P + R - S - T)]

```python
alpha = sym.symbols("alpha", positive=True, real=True)
x = np.array([[x1], [x2], [x3], [1 - x2 - x3]])
payoff_kernel = np.array([[R, S], [T, P]])
W = models.generalized_sexual_selection(x, lambda x: x**alpha, lambda x: x, payoff_kernel)
N, = models.total_offspring(W, x)
```

```python
sym.simplify(N.subs({x1: 0, x3: 0}))
```

$\displaystyle 2.0 P$

```python
(f1,), (f2,), (f3,), _ = models.offspring_genotypes_evolution(W, x)
F = sym.Matrix([f1, f2, f3])
```

```python
f2.subs({x1: 0, x3: 0})
```

$\displaystyle - x_{2} + \frac{1.0 P x_{2} \left(1 - x_{2}\right) + x_{2} \left(2.0 P x_{2} + 1.0 P \left(1 - x_{2}\right)\right)}{2.0 P x_{2} \left(1 - x_{2}\right) + x_{2} \left(2.0 P x_{2} + 1.0 P \left(1 - x_{2}\right)\right) + \left(1 - x_{2}\right) \left(1.0 P x_{2} + 2.0 P \left(1 - x_{2}\right)\right)}$

```python
sym.solve(f2.subs({x1: 0, x3: 0}), x2)
```

    []

```python
sym.simplify(f2.subs({x1: 0, x3: 0}).diff(x2))
```

$\displaystyle 0$

```python
F_jac = F.jacobian([x1, x2, x3])
```

```python
sym.limit(F_jac[0,0].subs({x3: 0}), x1, 0)
```

$\displaystyle - \frac{\infty}{\operatorname{sign}{\left(4.0 P^{2} x_{2} - 2.0 P S x_{2} - 2.0 P T x_{2} \right)}}$

```python
dUGdxA = sym.symbols("dUGdxA", positive=True, real=True)
dUgdxA = sym.symbols("dUgdxA", positive=True, real=True)
xA = sym.symbols("xA", nonnegative=True, real=True)

(F_jac[0,0].subs({x1 + x3: xA})
      .doit()
      .subs({UGA(xA): 1, sym.Derivative(UGA(xA), xA): 0, sym.Derivative(UgA(xA), xA): 1})
      .subs({x1: 0, x3: 0}))
```

$\displaystyle \frac{\frac{1.0 R x_{2}}{xA} + \frac{0.25 \left(1 - x_{2}\right) \left(2 R \operatorname{UgA}{\left(xA \right)} + 2 S \left(1 - \operatorname{UgA}{\left(xA \right)}\right)\right) \operatorname{UgA}{\left(xA \right)}}{xA}}{0.5 x_{2} \left(1 - x_{2}\right) \left(1 - \operatorname{UgA}{\left(xA \right)}\right) \left(2 P \left(1 - \operatorname{UgA}{\left(xA \right)}\right) + 2 T \operatorname{UgA}{\left(xA \right)}\right) + \left(1 - x_{2}\right) \left(0.5 x_{2} \left(1 - \operatorname{UgA}{\left(xA \right)}\right) \left(2 P \left(1 - \operatorname{UgA}{\left(xA \right)}\right) + 2 T \operatorname{UgA}{\left(xA \right)}\right) + 1.0 \left(1 - x_{2}\right) \left(1 - \operatorname{UgA}{\left(xA \right)}\right) \left(2 P \left(1 - \operatorname{UgA}{\left(xA \right)}\right) + 2 T \operatorname{UgA}{\left(xA \right)}\right)\right)} - 1$

```python
sym.simplify(F_jac.subs({x1: 0})
            .doit()
            .subs({UGA(x3): 1, sym.Derivative(UGA(x3), x3): 0, sym.Derivative(UgA(x3), x3): 1})
            .subs({x3: 0}))
```

$\displaystyle \left[\begin{matrix}\text{NaN} & \frac{0.5 P R}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}} & - \frac{1.0 x_{2} \left(0.5 P^{2} x_{2} - 0.5 P^{2} + 0.5 P R x_{2} - 1.0 P R - 0.5 R S x_{2} + 0.5 R S - 0.5 R T x_{2} + 0.5 R T\right)}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}}\\\text{NaN} & \frac{- 1.0 P^{2} x_{2}^{2} + 2.0 P^{2} x_{2} - 1.0 P^{2} + 3.0 P R x_{2}^{2} - 4.0 P R x_{2} + 0.5 P R - 2.0 R^{2} x_{2}^{2}}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}} & \frac{1.0 x_{2} \left(1.0 P^{2} x_{2}^{2} - 1.5 P^{2} x_{2} + 0.5 P^{2} + 1.0 P R x_{2}^{2} - 2.5 P R x_{2} + 1.0 P R - 1.0 P S x_{2}^{2} + 2.0 P S x_{2} - 1.0 P S + 0.5 R S x_{2} - 0.5 R S - 1.0 R T x_{2}^{2} + 1.5 R T x_{2} - 0.5 R T\right)}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}}\\\text{NaN} & \frac{0.5 P R}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}} & \frac{- 1.5 P^{2} x_{2}^{2} + 2.5 P^{2} x_{2} - 1.0 P^{2} + 2.5 P R x_{2}^{2} - 2.0 P R x_{2} + 1.0 P S x_{2}^{2} - 2.0 P S x_{2} + 1.0 P S - 2.0 R^{2} x_{2}^{2} - 0.5 R S x_{2}^{2} + 0.5 R S x_{2} + 0.5 R T x_{2}^{2} - 0.5 R T x_{2}}{2.0 P^{2} x_{2}^{2} - 4.0 P^{2} x_{2} + 2.0 P^{2} - 4.0 P R x_{2}^{2} + 4.0 P R x_{2} + 2.0 R^{2} x_{2}^{2}}\end{matrix}\right]$

```python
sym.factor(x_star, Ug)
```

$\displaystyle \frac{8.0 \left(0.5 P \epsilon + Ug^{2} \left(0.5 P \epsilon + 0.25 R - 0.25 S - 0.5 T \epsilon\right) + Ug \left(- 1.0 P \epsilon + 0.25 S + 0.5 T \epsilon\right)\right)}{\left(8.0 \epsilon + 4.0\right) \left(0.5 P + Ug^{2} \left(0.5 P + 0.5 R - 0.5 S - 0.5 T\right) + Ug \left(- 1.0 P + 0.5 S + 0.5 T\right)\right)}$

```python
sym.factor(x_star, Ug).subs({Ug: 0})
```

$\displaystyle \frac{8.0 \epsilon}{8.0 \epsilon + 4.0}$

```python
sym.factor(x_star, Ug).subs({Ug: 1})
```

$\displaystyle \frac{4.0}{8.0 \epsilon + 4.0}$

```python
sym.solve(x3 - sym.factor(x_star, Ug).subs({Ug: x3}))
```

```python

```


# PharmSci 175/275 (UCI)
## What is this??
The material below is a supplement to the quantum mechanics (QM) lecture from Drug Discovery Computing Techniques, PharmSci 175/275 at UC Irvine.
Extensive materials for this course, as well as extensive background and related materials, are available on the course GitHub repository: [github.com/mobleylab/drug-computing](https://github.com/mobleylab/drug-computing)

# Using QM in Python

This material adapted (under CC-BY) from a [workshop example](https://github.com/QCMM/workshop2017/blob/master/Theory_electronic_structure_day3/i_inter_es.ipynb) of Stefano Vogt-Giesse (University of Concepcion) from the December, 2017 QCMM workshop in Chile, available under the [workshop repository](https://github.com/QCMM/workshop2017)

### Instructor: David L. Mobley

### Contributors to these materials:
- Stefano Vogt-Giesse
- David L. Mobley

## Choose whether to run under Google Colab or locally

You need to do different preparation to run this notebook locally vs Google Colab; skip to the appropriate section following depending on which you choose.

## Preparation for using Google Colab (SKIP IF RUNNING LOCALLY))

[](https://colab.research.google.com/github/MobleyLab/drug-computing/blob/master/uci-pharmsci/lectures/QM/psi4_example.ipynb)

If you are running this on Google Colab, you need to take a couple additional steps of preparation. **Note that these steps may take 5-10 minutes to complete.**

Psi4 installs via `conda`, not pip, so you will need to get conda set up on Colab:

```python
! wget https://repo.anaconda.com/miniconda/Miniconda3-py37_4.10.3-Linux-x86_64.sh
! chmod +x Miniconda3-py37_4.10.3-Linux-x86_64.sh
! bash ./Miniconda3-py37_4.10.3-Linux-x86_64.sh -b -f -p /usr/local
import sys
sys.path.append('/usr/local/lib/python3.7/site-packages/')
```

Then `conda`-install psi4:

```python
!conda install -c psi4 psi4 --yes
```

## Preparation for running locally (SKIP IF RUNNING USING COLAB)

For today's activity we will use the package of program psi4, so we will need to install it first. Assuming you already have anaconda/miniconda installed, you can install as follows (in a new conda environment, `psi4`:

**conda create -n psi4 psi4 psi4-rt jupyter matplotlib -c psi4/label/dev -c psi4**

(Note psi4 seems to be somewhat incompatible with the other software we are using in this course, so a separate environment is needed.)

This will install all the psi4 binaries and a python module which can be imported from the notebook. Then activate the environment via `conda activate psi4`.

You also need to ensure it works in your jupyter notebook, which you can do via (in the terminal, with your `psi4` environment active):
```
conda install ipykernel --name psi4
python -m ipykernel install --user
```

To finish the installation you need to provide a scratch directory in your `~/.bash_profile`, for example (assuming you want your scratch directory in this space):

**export PSI_SCRATCH=/home/user_name/scratch/psi4**

Then type `source ~/.bash_profile`. Now you may open jupyter-notebook and install psi4.

Every time you wish to use psi4 you will need to `conda activate psi4`.

# Exploring molecular interactions using electronic structure methods.

```python
import psi4
import numpy as np
```

# 1. Compute the energy of a diatomic molecule

As a first example we will compute the scf energy of the diatómic molecule hydrogen flouride (HF):

```python
# ==> Basic Psi4 options <==
# Memory
psi4.set_memory(int(5e8))
numpy_memory = 500

# Output
psi4.core.set_output_file('output.dat', False)

# Geometry input
hf_mol = psi4.geometry("""
0 1
H
F 1 0.917
""")

energy_hf_mol , wfn_hf_mol = psi4.energy('mp2/cc-pvtz', return_wfn=True)
print(energy_hf_mol) #Energy in Hartrees
```

      Memory set to 476.837 MiB by Python driver.
    -100.34290633086249

This corresponds to the MP2/cc-pVTZ energy for this system (HF).  Input coordinates are given in [Z matrix format](https://en.wikipedia.org/wiki/Z-matrix_(chemistry)), using internal coordinates (in this case just a bond distance).

### Exercise: Compute the energy of other diatomic molecules using similar methods
You might consider computing the energy of of F$_2$ and N$_2$ using the cc-pVDZ and cc-pVTZ methods.

# 2. Compute the dipole and quadrupole moment of diatomic molecules

Since we are intrested in studying long range  molecular interactions using classical elctrodynamics, it is necessary
to compute the dipole and quadrupole moments. Quantum mechanically the dipole can be computed using the one electron dipole operator:

\begin{equation}
\hat{\mu} = \sum_i q_i r_i
\end{equation}

where $q_i$ is the charge of the particle and $r_i$ is the position vector of the particle.  The dipole moment can be computed using the wavefunction through the expectation value of the operator $\mu$.

\begin{equation}
\mu = <\psi|\hat{\mu}|\psi>
\end{equation}

In psi4 we can obtain the dipole moment from the wafefunction object that was defined above

```python
psi4.oeprop(wfn_hf_mol, 'DIPOLE', 'QUADRUPOLE', title='HF SCF')

mux = psi4.core.get_variable('HF SCF DIPOLE X') # in debye
muy = psi4.core.get_variable('HF SCF DIPOLE Y')
muz = psi4.core.get_variable('HF SCF DIPOLE Z')
quad_zz = psi4.core.get_variable('HF SCF QUADRUPOLE ZZ')
```

    /var/folders/7z/88tvgj2941bclbbw1xhn0t8r0000gn/T/ipykernel_58639/3045530770.py:3: FutureWarning: Using `psi4.core.get_variable` instead of `psi4.core.variable` (or `psi4.core.scalar_variable` for scalar variables only) is deprecated, and in 1.4 it will stop working

      mux = psi4.core.get_variable('HF SCF DIPOLE X') # in debye
    /var/folders/7z/88tvgj2941bclbbw1xhn0t8r0000gn/T/ipykernel_58639/3045530770.py:4: FutureWarning: Using `psi4.core.get_variable` instead of `psi4.core.variable` (or `psi4.core.scalar_variable` for scalar variables only) is deprecated, and in 1.4 it will stop working

      muy = psi4.core.get_variable('HF SCF DIPOLE Y')
    /var/folders/7z/88tvgj2941bclbbw1xhn0t8r0000gn/T/ipykernel_58639/3045530770.py:5: FutureWarning: Using `psi4.core.get_variable` instead of `psi4.core.variable` (or `psi4.core.scalar_variable` for scalar variables only) is deprecated, and in 1.4 it will stop working

      muz = psi4.core.get_variable('HF SCF DIPOLE Z')
    /var/folders/7z/88tvgj2941bclbbw1xhn0t8r0000gn/T/ipykernel_58639/3045530770.py:6: FutureWarning: Using `psi4.core.get_variable` instead of `psi4.core.variable` (or `psi4.core.scalar_variable` for scalar variables only) is deprecated, and in 1.4 it will stop working

      quad_zz = psi4.core.get_variable('HF SCF QUADRUPOLE ZZ')

```python
print(muz)
print(quad_zz)
mu = (np.sqrt(mux**2 + muy**2 + muz**2))
print(mu)
```

    -1.9411550055413282
    -3.2710162959870983
    1.9411550055413282

# 3.  Compute a potential energy surface of HF dimer.

In order to study the physical interactions between two molecules it is convinient to draw
a potential energy surface along the interaction coordinate. In this section we will
obtain a potential energy profile for the most favorable dipole-dipole interaction, which is the
horizontal orientation with oposing dipole vectors, HF---FH. First we need to define a list containing
the distances between both dimers for which the energy will be obtained.

```python
hf_dimer = psi4.geometry("""
        0 1
        H
        F 1 0.917
        H 2 R 1 180.0
        F 3 0.917 2 180.0 1 0.0
        """)
```

Next, we write a loop and in each step of the loop we compute the energy at the mp4 level of theory.

```python
energy = []
dist   = []

Rval = np.arange(1.5,10.0,0.1)

for d in Rval:
    hf_dimer.R = d
    psi4.set_options({'freeze_core': 'True'})
    en = psi4.energy('scf/cc-pvtz')
    print(en)
    print(d)
    energy.append(en)
    dist.append(d)

```

    -200.11594007613158
    1.5
    -200.11908196505507
    1.6
    -200.12084511099732
    1.7000000000000002
    -200.12174182364433
    1.8000000000000003
    -200.12210646361427
    1.9000000000000004
    -200.12214740636392
    2.0000000000000004
    -200.12199128717546
    2.1000000000000005
    -200.1217185553115
    2.2000000000000006
    -200.12138348389254
    2.3000000000000007
    -200.12102248369894
    2.400000000000001
    -200.1206584432208
    2.500000000000001
    -200.12030471982058
    2.600000000000001
    -200.11996870241614
    2.700000000000001
    -200.11965427235268
    2.800000000000001
    -200.1193632136904
    2.9000000000000012
    -200.11909600301578
    3.0000000000000013
    -200.11885228615392
    3.1000000000000014
    -200.11863115920457
    3.2000000000000015
    -200.11843131725777
    3.3000000000000016
    -200.118251147143
    3.4000000000000017
    -200.11808882795262
    3.5000000000000018
    -200.1179424573063
    3.600000000000002
    -200.1178101821683
    3.700000000000002
    -200.11769030211374
    3.800000000000002
    -200.117581325851
    3.900000000000002
    -200.11748198151327
    4.000000000000002
    -200.11739119494283
    4.100000000000002
    -200.11730805348418
    4.200000000000003
    -200.11723176950403
    4.3000000000000025
    -200.11716165120245
    4.400000000000002
    -200.11709708268364
    4.500000000000003
    -200.11703751178317
    4.600000000000003
    -200.1169824430437
    4.700000000000003
    -200.1169314331076
    4.8000000000000025
    -200.11688408717754
    4.900000000000003
    -200.1168400551661
    5.0000000000000036
    -200.11679902773142
    5.100000000000003
    -200.11676073196662
    5.200000000000003
    -200.11672492703738
    5.300000000000003
    -200.1166914000603
    5.400000000000004
    -200.1166599622753
    5.5000000000000036
    -200.11663044566174
    5.600000000000003
    -200.11660269999402
    5.700000000000004
    -200.11657659037843
    5.800000000000004
    -200.11655199514317
    5.900000000000004
    -200.11652880412805
    6.0000000000000036
    -200.11650691722386
    6.100000000000004
    -200.11648624315433
    6.200000000000005
    -200.11646669847164
    6.300000000000004
    -200.1164482067375
    6.400000000000004
    -200.11643069773467
    6.500000000000004
    -200.11641410687145
    6.600000000000005
    -200.1163983746509
    6.700000000000005
    -200.1163834461709
    6.800000000000004
    -200.1163692707005
    6.900000000000005
    -200.11635580132997
    7.000000000000005
    -200.11634299461193
    7.100000000000005
    -200.1163308102652
    7.200000000000005
    -200.116319210949
    7.300000000000005
    -200.11630816193497
    7.400000000000006
    -200.1162976309706
    7.500000000000005
    -200.11628758805156
    7.600000000000005
    -200.11627800521813
    7.7000000000000055
    -200.1162688564144
    7.800000000000006
    -200.1162601173462
    7.900000000000006
    -200.11625176532
    8.000000000000005
    -200.11624377915305
    8.100000000000005
    -200.11623613901043
    8.200000000000006
    -200.11622882636473
    8.300000000000006
    -200.11622182384693
    8.400000000000006
    -200.11621511520096
    8.500000000000007
    -200.11620868514754
    8.600000000000007
    -200.1162025193921
    8.700000000000006
    -200.11619660448488
    8.800000000000006
    -200.11619092780117
    8.900000000000006
    -200.11618547746463
    9.000000000000007
    -200.11618024229836
    9.100000000000007
    -200.1161752117998
    9.200000000000006
    -200.11617037606567
    9.300000000000008
    -200.11616572573791
    9.400000000000007
    -200.11616125203147
    9.500000000000007
    -200.11615694665616
    9.600000000000007
    -200.11615280173663
    9.700000000000006

Now we are ready to plot the potential energy profile. We will use the matplotlib python library for this
purpose. The function `ref_zero_kcal` transforms the energy which is in Hartee to kcal/mol and takes the
energy of the dimer with the farthest separation as the reference energy.

```python
import matplotlib.pyplot as plt
%matplotlib inline
```

```python
def ref_zero_kcal(en_list):
    energy_kcal = []
    for x in range(len(en_list)):
        energy_kcal.append((en_list[x] - en_list[-1])*627.51)
    return energy_kcal

energy_kcal = ref_zero_kcal(energy)
plt.plot(dist,energy_kcal)
plt.xlabel('Distance')
plt.ylabel('Energy (kcal/mol)')
```

There are many more examples/tutorials in the Psi4 GitHub repositories, especially see `Tutorials` under the (psi4numpy repository](https://github.com/psi4/psi4numpy) for many Jupyter notebooks.


This Jupyter Notebook calculates the FRC between two 2D images.
The basic requirement is for the images to be ${squared}$, regardless of the size.
The first cell loads the libraries needed for the computation.

```python
import sys
import imageio
import numpy as np
import secondary_utils as su
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import numpy.fft as fft
import matplotlib.font_manager as font_manager
from scipy.interpolate import interp1d
from skimage.filters import window
#from astropy.io import fits
```

Two important functions:
- openimage (quite self-explanatory)
- rings: generates the array of indices to be used to calculate the FRC. Through analytic shape it returns the coordinates of the pixels within the rings.

```python
def openimage(path):
    return(imageio.imread(path))

def rings(x):

    shape = np.shape(x)
    nr,nc = shape
    nrdc = np.floor(nr/2)
    ncdc = np.floor(nc/2)
    r = np.arange(nr)-nrdc
    c = np.arange(nc)-ncdc
    [R,C] = np.meshgrid(r,c)
    index = np.round(np.sqrt(R**2+C**2))
    indexf = np.floor(np.sqrt(R**2+C**2))
    indexC = np.ceil(np.sqrt(R**2+C**2))
    print(np.shape(index))
    maxindex = nr/2

    output = np.zeros(int(maxindex),dtype = complex)

    print('performed by index method')
    indices = []
    indicesf, indicesC = [], []
    for i in np.arange(int(maxindex)):
        indicesf.append(np.where(indexf == i))
        indicesC.append(np.where(indexC == i))

    for i in np.arange(int(maxindex)):
        output[i] = (sum(x[indicesf[i]])+sum(x[indicesC[i]]))/2
    return output
```

The two following functions are the core:
- apply_hanning_2d to apply the hanning window to the Fourier transformed images in order to reduce the noise
- FRC: Calculates the Fourier Ring Correlation through the formula:
\begin{equation}\text{FRC}_{12}(r_{i})=\frac{\underset{r\epsilon r_{i}}{\sum}F_{1}(r)\cdot F_{2}(r)^{*}}{\sqrt{\underset{r\epsilon r_{i}}{\sum}|F_{1}|^{2}(r)\cdot\underset{r\epsilon r_{i}}{\sum}|F_{2}|^{2}(r)}}\end{equation}

```python
def apply_hanning_2d(img):

    wimage = img * window('hann', img.shape)

    print("Hann applied")
    return(wimage)

def FRC(i1, i2,hanning):

    ''' Performing the fourier transform of input
    images to determine the FRC
    '''
    I1 = fft.fftshift(fft.fft2(i1))
    I2 = fft.fftshift(fft.fft2(i2))

    if hanning==True:
        I1 = apply_hanning_2d(I1)
        I2 = apply_hanning_2d(I2)

    C  = rings(I1*np.conjugate(I2))
    C = np.real(C)
    C1 = rings(np.abs(I1)**2)
    C2 = rings(np.abs(I2)**2)
    C  = C.astype(np.float64)
    C1 = np.real(C1).astype(np.float64)
    C2 = np.real(C2).astype(np.float64)
    FSC    = abs(C)/np.sqrt(C1*C2)
    x_fsc  = np.arange(np.shape(C)[0])/(np.shape(i1)[0]/2)
    r      = np.arange(np.shape(i1)[0]/2) # array (0:1:L/2-1)
    n      = 2*np.pi*r # perimeter of r's from above
    n[0]   = 1
    eps    = np.finfo(float).eps
    #t1 = np.divide(np.ones(np.shape(n)),n+eps)

    inv_sqrt_n = np.divide(np.ones(np.shape(n)),np.sqrt(n)) # 1/sqrt(n)
    x_T    = r/(np.shape(i1)[0]/2)
    #one bit
    T1 = (0.5+2.4142*inv_sqrt_n)/(1.5+1.4142*inv_sqrt_n)
    #half bit
    T2 = (0.4142+2.287*inv_sqrt_n)/ (1.4142+1.287*inv_sqrt_n)

    return (x_fsc, FSC, x_T, T1,T2)

def gauss_noise(image, mean=0, var=0.001):

    '''
                 Add Gaussian noise
                 mean: mean value
                 var: variance
    '''

    image = np.array(image, dtype=float)
    noise = np.random.normal(mean, var ** 0.5, image.shape)
    out = image + noise
    if out.min() < 0:
        low_clip = -1.
    else:
        low_clip = 0.
    out = np.clip(out, low_clip, 1.0)
    out = np.uint8(out*255)

    return out

#lena  = su.imageio_imread('./demo_images/514/Result of Siemens0.jpg')
lena  = su.imageio_imread('./demo_images/barques.jpg')
lena  = lena[:, :,0]
lena  = lena[750:1250,1500:2000]
print(np.max(lena))
lena  = lena.astype(float)
lena  = su.normalize_data_ab(0, 1, lena)
```

    244

Calculate the FRC and the threshold curve through the half-bit formula

```python
xf, frc, x_T, T1,T2 = FRC(Image1, Image2,hanning=False)
```

    (500, 500)
    performed by index method
    (500, 500)
    performed by index method
    (500, 500)
    performed by index method

Plot everything!

```python
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
#plt.plot(x_T[:-1], T1[:-1], label = 'one-bit', color='blue')
plt.plot(x_T[:-1], T2[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Posizione 0')
plt.legend()
plt.show()
```

```python
'''
Image1 = gauss_noise(lena,0,0.001)
Image2 = gauss_noise(lena,0,0.001)
f, axrr = plt.subplots(1, 2)
axrr[0].imshow(Image1, cmap='Greys_r')
axrr[0].set_title('Measurement 1')
axrr[1].imshow(Image2, cmap='Greys_r')
axrr[1].set_title('Measurement 2')
plt.show()
xf, frc, x_T, T1,T2 = FRC(Image1, Image2,hanning=False)
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T1[:-1], label = 'one-bit', color='blue')
plt.plot(x_T[:-1], T2[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Posizione 0')
plt.legend()
plt.show()
'''
fig, axs = plt.subplots(2, 2, figsize=(15, 15))
fig.subplots_adjust(left=0.01, bottom=0.06, right=0.99, top=0.65, wspace=0.12)
plt.rcParams["font.family"] = "serif"
plt.style.use('classic')
font = font_manager.FontProperties(family='serif', size=14)
fig.patch.set_facecolor('white')
coordinates = [(0,0),(0,1),(1,0),(1,1)]
variation = [0.001,0.01,0.1,1]
for i in range(4):
    Image1 = gauss_noise(lena,0,variation[i])
    Image2 = gauss_noise(lena,0,variation[i])
    xf, frc, x_T, T1,T2 = FRC(Image1, Image2,hanning=False)
    axs[coordinates[i]].plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
    axs[coordinates[i]].plot(x_T[:-1], T2[:-1], label = 'half-bit', color='red')
    axs[coordinates[i]].set_xlim(-0.01, 0.51)
    axs[coordinates[i]].set_xticks(np.arange(0, 0.6, step=0.1))
    axs[coordinates[i]].grid(linestyle='dotted', color='black', alpha=0.5)
    axs[coordinates[i]].set_ylabel("FRC($r_i$)",fontname="serif")
    axs[coordinates[i]].set_xlabel("1/resolution",fontname="serif")
    axs[coordinates[i]].set_title('Noise variance: '+str(variation[i]),fontname="serif")
    axs[coordinates[i]].legend()
file_path = "/Users/oriolsansplanell/Dropbox/Aplicaciones/Overleaf/Thesis/Figures/Fourier/"
file_name = "4-example-frc"
plt.savefig(file_path+file_name+".pdf", bbox_inches='tight')
plt.show()

fig=figure(figsize=(8,4))
plt.rcParams["font.family"] = "serif"
plt.style.use('classic')
font = font_manager.FontProperties(family='serif', size=12)
fig.patch.set_facecolor('white')
Image1 = gauss_noise(lena,0,0.001)
Image2 = gauss_noise(lena,0,0.001)
xf, frc, x_T, T1,T2 = FRC(Image1, Image2,hanning=False)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T2[:-1], label = 'half-bit', color='red')
plt.xlim(-0.01, 0.51)
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)",fontname="serif")
plt.xlabel("1/resolution",fontname="serif")
plt.title('Noise variance: '+str(variation[i]),fontname="serif")
plt.legend()
file_path = "/Users/oriolsansplanell/Dropbox/Aplicaciones/Overleaf/Thesis/Figures/Fourier/"
file_name = "FRC-T"
plt.savefig(file_path+file_name+".pdf", bbox_inches='tight')
plt.show()
```

```python
for i in range(50):
    Image1 = openimage("Res-pos-0-0.tif")[300:-200+10*i,300:-200+10*i]
    Image2 = openimage("Res-pos-0-1.tif")[300:-200+10*i,300:-200+10*i]
    '''
    f, axrr = plt.subplots(1, 2)
    axrr[0].imshow(Image1, cmap='Greys_r')
    axrr[0].set_title('Measurement 1')
    axrr[1].imshow(Image2, cmap='Greys_r')
    axrr[1].set_title('Measurement 2')
    plt.show()
    '''
    xf, frc, x_T, T1,T2 = FRC(Image1, Image2,hanning=False)
    plt.xlim(-0.01, 0.51)
    plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
    plt.plot(x_T[:-1], T1[:-1], label = 'one-bit', color='blue')
    plt.plot(x_T[:-1], T2[:-1], label = 'half-bit', color='red')
    plt.xticks(np.arange(0, 0.6, step=0.1))
    plt.grid(linestyle='dotted', color='black', alpha=0.5)
    plt.ylabel("FRC($r_i$)")
    plt.xlabel("1/resolution")
    plt.title('Posizione 2')
    plt.legend()
    plt.show()
    plt.close()
```

```python
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Spada Indiana 1 +/-')
plt.legend()
plt.show()
```

```python
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Spada Indiana 1 0/0')
plt.legend()
plt.show()
```

```python
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Spada Indiana original')
plt.legend()
plt.show()
```

```python
plt.xlim(-0.01, 0.51)
plt.plot(xf[:-1]/2, frc[:-1], label = 'FRC', color='black')
plt.plot(x_T[:-1], T[:-1], label = 'half-bit', color='red')
plt.xticks(np.arange(0, 0.6, step=0.1))
plt.grid(linestyle='dotted', color='black', alpha=0.5)
plt.ylabel("FRC($r_i$)")
plt.xlabel("1/resolution")
plt.title('Spada Italiana 1 aprox')
plt.legend()
plt.show()
```

```python

```