```python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
```

```python
from sympy import *
```

```python
s, kon, koff, konp, koffp, g, a, b, a1, a2, b1, b2, a1p, b1p = \
    symbols((r'\sigma_R', r'\kappa_\text{on}', r'\kappa_\text{off}', r'\kappa_\text{on}^\prime', r'\kappa_\text{off}^\prime', r'\gamma', r'\alpha', r'\beta', r'\alpha_1', r'\alpha_2', r'\beta_1', r'\beta_2', r'\alpha_1^\prime', r'\beta_1^\prime'), positive=True, real=True)
srp, kon1, koff1, kon2, koff2, b2p, gp = \
    symbols((r'\sigma_r^\prime', r'\kappa_1^\text{on}', r'\kappa_1^\text{off}', r'\kappa_2^\text{on}', r'\kappa_2^\text{off}', r'\beta_2^\prime', r'\gamma^\prime'), positive=True, real=True)
```

```python
R, r, rp, C1, C2, C1p = \
    symbols(('R', 'r', r'r^\prime', r'C_1', r'C_2', r'C_1^\prime'), positive=True, real=True)
```

```python

```

```python
def coefficient_plot(*expressions, height=3):
    # expression, title, width
    total_width = sum(e[2] for e in expressions)
    width_ratios = [e[2] / total_width for e in expressions]
    fig, axs = plt.subplots(figsize=(total_width, height), ncols=len(expressions), gridspec_kw={'width_ratios': width_ratios})
    for (i, (ax, (exp, title, width, *rest))) in enumerate(zip(axs, expressions)):
        ax.set_title(title)
        terms = poly(exp).terms()
        degs = [sum(x[0]) for x in terms]
        coeffs = [x[1] for x in terms]
        df = pd.DataFrame({'Degree': degs, 'Coefficient': coeffs}, dtype='int64')
        sns.histplot(data=df, x='Coefficient', hue='Degree', fill=True,
             discrete=True, multiple='stack', palette='rocket', ax=ax)
        if len(rest) > 0:
            ax.set_xticks(rest[0])
        if len(rest) > 1:
            ax.set_ylim(top=rest[1])
        if i > 0:
            ax.set_ylabel('')
    fig.tight_layout()
```

```python

```

## C1KO

```python
rhs_R = s - kon*R*r*r + koff*C2 - R + 2*b2*g*C2
rhs_r = 1 - 2*kon*R*r*r + 2*koff*C2 - g*r + 2*b2*g*C2 + 2*a2*C2
rhs_C2 = kon*R*r*r - koff*C2 - a2*C2 - 2*b2*g*C2
```

```python
rhs_R
```

$\displaystyle 2 C_{2} \beta_2 \gamma + C_{2} \kappa_\text{off} - R \kappa_\text{on} r^{2} - R + \sigma_R$

```python
rhs_r
```

$\displaystyle 2 C_{2} \alpha_2 + 2 C_{2} \beta_2 \gamma + 2 C_{2} \kappa_\text{off} - 2 R \kappa_\text{on} r^{2} - \gamma r + 1$

```python
rhs_C2
```

$\displaystyle - C_{2} \alpha_2 - 2 C_{2} \beta_2 \gamma - C_{2} \kappa_\text{off} + R \kappa_\text{on} r^{2}$

```python
J = Matrix([rhs_R, rhs_r, rhs_C2]).jacobian(Matrix([R, r, C2]))
J
```

$\displaystyle \left[\begin{matrix}- \kappa_\text{on} r^{2} - 1 & - 2 R \kappa_\text{on} r & 2 \beta_2 \gamma + \kappa_\text{off}\\- 2 \kappa_\text{on} r^{2} & - 4 R \kappa_\text{on} r - \gamma & 2 \alpha_2 + 2 \beta_2 \gamma + 2 \kappa_\text{off}\\\kappa_\text{on} r^{2} & 2 R \kappa_\text{on} r & - \alpha_2 - 2 \beta_2 \gamma - \kappa_\text{off}\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
p
```

$\displaystyle \operatorname{PurePoly}{\left( \lambda^{3} + \left(4 R \kappa_\text{on} r + \alpha_2 + 2 \beta_2 \gamma + \gamma + \kappa_\text{off} + \kappa_\text{on} r^{2} + 1\right) \lambda^{2} + \left(4 R \beta_2 \gamma \kappa_\text{on} r + 4 R \kappa_\text{on} r + \alpha_2 \gamma + \alpha_2 \kappa_\text{on} r^{2} + \alpha_2 + 2 \beta_2 \gamma^{2} + 2 \beta_2 \gamma + \gamma \kappa_\text{off} + \gamma \kappa_\text{on} r^{2} + \gamma + \kappa_\text{off}\right) \lambda + 4 R \beta_2 \gamma \kappa_\text{on} r + \alpha_2 \gamma \kappa_\text{on} r^{2} + \alpha_2 \gamma + 2 \beta_2 \gamma^{2} + \gamma \kappa_\text{off}, \lambda, domain=\mathbb{Z}\left[r, R, \alpha_2, \beta_2, \gamma, \kappa_\text{off}, \kappa_\text{on}\right] \right)}$

#### All coefficients are positive.

```python
# Note cfs[x] is a_x in some literature for RH (https://math24.net/routh-hurwitz-criterion.html)
# In our SI, the subsript order is the opposite:  cfs[-(x+1)] is a_x

u = (cfs[1]*cfs[2] - cfs[0]*cfs[3]).expand().simplify()
u
```

$\displaystyle 16 R^{2} \beta_2 \gamma \kappa_\text{on}^{2} r^{2} + 16 R^{2} \kappa_\text{on}^{2} r^{2} + 4 R \alpha_2 \beta_2 \gamma \kappa_\text{on} r + 4 R \alpha_2 \gamma \kappa_\text{on} r + 4 R \alpha_2 \kappa_\text{on}^{2} r^{3} + 8 R \alpha_2 \kappa_\text{on} r + 8 R \beta_2^{2} \gamma^{2} \kappa_\text{on} r + 12 R \beta_2 \gamma^{2} \kappa_\text{on} r + 4 R \beta_2 \gamma \kappa_\text{off} \kappa_\text{on} r + 4 R \beta_2 \gamma \kappa_\text{on}^{2} r^{3} + 16 R \beta_2 \gamma \kappa_\text{on} r + 4 R \gamma \kappa_\text{off} \kappa_\text{on} r + 4 R \gamma \kappa_\text{on}^{2} r^{3} + 8 R \gamma \kappa_\text{on} r + 8 R \kappa_\text{off} \kappa_\text{on} r + 4 R \kappa_\text{on}^{2} r^{3} + 4 R \kappa_\text{on} r + \alpha_2^{2} \gamma + \alpha_2^{2} \kappa_\text{on} r^{2} + \alpha_2^{2} + 4 \alpha_2 \beta_2 \gamma^{2} + 2 \alpha_2 \beta_2 \gamma \kappa_\text{on} r^{2} + 4 \alpha_2 \beta_2 \gamma + \alpha_2 \gamma^{2} + 2 \alpha_2 \gamma \kappa_\text{off} + 2 \alpha_2 \gamma \kappa_\text{on} r^{2} + 2 \alpha_2 \gamma + \alpha_2 \kappa_\text{off} \kappa_\text{on} r^{2} + 2 \alpha_2 \kappa_\text{off} + \alpha_2 \kappa_\text{on}^{2} r^{4} + 2 \alpha_2 \kappa_\text{on} r^{2} + \alpha_2 + 4 \beta_2^{2} \gamma^{3} + 4 \beta_2^{2} \gamma^{2} + 2 \beta_2 \gamma^{3} + 4 \beta_2 \gamma^{2} \kappa_\text{off} + 4 \beta_2 \gamma^{2} \kappa_\text{on} r^{2} + 4 \beta_2 \gamma^{2} + 4 \beta_2 \gamma \kappa_\text{off} + 2 \beta_2 \gamma \kappa_\text{on} r^{2} + 2 \beta_2 \gamma + \gamma^{2} \kappa_\text{off} + \gamma^{2} \kappa_\text{on} r^{2} + \gamma^{2} + \gamma \kappa_\text{off}^{2} + 2 \gamma \kappa_\text{off} \kappa_\text{on} r^{2} + 2 \gamma \kappa_\text{off} + \gamma \kappa_\text{on}^{2} r^{4} + 2 \gamma \kappa_\text{on} r^{2} + \gamma + \kappa_\text{off}^{2} + \kappa_\text{off} \kappa_\text{on} r^{2} + \kappa_\text{off}$

```python
[cf.is_positive for cf in [*cfs, u]]
```

    [True, True, True, True, True]

```python
coefficient_plot([cfs[3], '$a_0$', 2, range(6), 8],
                 [cfs[2], '$a_1$', 2, range(6), 8],
                 [cfs[1], '$a_2$', 2, range(6), 8],
                 [u, '$u$', 6])
#plt.savefig('rh_c1ko.svg')
```

```python

```

## C2KO

```python
rhs_R = s - kon*R*r - konp*R*r + koff*C1 + koffp*C1p - R + b1*g*C1 + b1p*g*C1p
rhs_r = 1 - kon*R*r - konp*R*r + koff*C1 + koffp*C1p - g*r + a1*C1 + a1p*C1p
rhs_C1 = kon*R*r - koff*C1 - a1*C1 - b1*g*C1
rhs_C1p = konp*R*r - koffp*C1p - a1p*C1p - b1p*g*C1p
```

```python
J = Matrix([rhs_R, rhs_r, rhs_C1, rhs_C1p]).jacobian(Matrix([R, r, C1, C1p]))
J
```

$\displaystyle \left[\begin{matrix}- \kappa_\text{on} r - \kappa_\text{on}^\prime r - 1 & - R \kappa_\text{on} - R \kappa_\text{on}^\prime & \beta_1 \gamma + \kappa_\text{off} & \beta_1^\prime \gamma + \kappa_\text{off}^\prime\\- \kappa_\text{on} r - \kappa_\text{on}^\prime r & - R \kappa_\text{on} - R \kappa_\text{on}^\prime - \gamma & \alpha_1 + \kappa_\text{off} & \alpha_1^\prime + \kappa_\text{off}^\prime\\\kappa_\text{on} r & R \kappa_\text{on} & - \alpha_1 - \beta_1 \gamma - \kappa_\text{off} & 0\\\kappa_\text{on}^\prime r & R \kappa_\text{on}^\prime & 0 & - \alpha_1^\prime - \beta_1^\prime \gamma - \kappa_\text{off}^\prime\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
u1 = (cfs[1]*cfs[2] - cfs[0]*cfs[3]).expand().simplify()
```

```python
cfs[4]
```

$\displaystyle R \alpha_1 \beta_1^\prime \gamma \kappa_\text{on}^\prime + R \alpha_1^\prime \beta_1 \gamma \kappa_\text{on} + R \beta_1 \beta_1^\prime \gamma^{2} \kappa_\text{on} + R \beta_1 \beta_1^\prime \gamma^{2} \kappa_\text{on}^\prime + R \beta_1 \gamma \kappa_\text{off}^\prime \kappa_\text{on} + R \beta_1^\prime \gamma \kappa_\text{off} \kappa_\text{on}^\prime + \alpha_1 \alpha_1^\prime \gamma \kappa_\text{on} r + \alpha_1 \alpha_1^\prime \gamma \kappa_\text{on}^\prime r + \alpha_1 \alpha_1^\prime \gamma + \alpha_1 \beta_1^\prime \gamma^{2} \kappa_\text{on} r + \alpha_1 \beta_1^\prime \gamma^{2} + \alpha_1 \gamma \kappa_\text{off}^\prime \kappa_\text{on} r + \alpha_1 \gamma \kappa_\text{off}^\prime + \alpha_1^\prime \beta_1 \gamma^{2} \kappa_\text{on}^\prime r + \alpha_1^\prime \beta_1 \gamma^{2} + \alpha_1^\prime \gamma \kappa_\text{off} \kappa_\text{on}^\prime r + \alpha_1^\prime \gamma \kappa_\text{off} + \beta_1 \beta_1^\prime \gamma^{3} + \beta_1 \gamma^{2} \kappa_\text{off}^\prime + \beta_1^\prime \gamma^{2} \kappa_\text{off} + \gamma \kappa_\text{off} \kappa_\text{off}^\prime$

```python
cfs[3]
```

$\displaystyle R \alpha_1 \beta_1^\prime \gamma \kappa_\text{on}^\prime + R \alpha_1 \kappa_\text{on}^\prime + R \alpha_1^\prime \beta_1 \gamma \kappa_\text{on} + R \alpha_1^\prime \kappa_\text{on} + R \beta_1 \beta_1^\prime \gamma^{2} \kappa_\text{on} + R \beta_1 \beta_1^\prime \gamma^{2} \kappa_\text{on}^\prime + R \beta_1 \gamma \kappa_\text{off}^\prime \kappa_\text{on} + R \beta_1 \gamma \kappa_\text{on} + R \beta_1 \gamma \kappa_\text{on}^\prime + R \beta_1^\prime \gamma \kappa_\text{off} \kappa_\text{on}^\prime + R \beta_1^\prime \gamma \kappa_\text{on} + R \beta_1^\prime \gamma \kappa_\text{on}^\prime + R \kappa_\text{off} \kappa_\text{on}^\prime + R \kappa_\text{off}^\prime \kappa_\text{on} + \alpha_1 \alpha_1^\prime \gamma + \alpha_1 \alpha_1^\prime \kappa_\text{on} r + \alpha_1 \alpha_1^\prime \kappa_\text{on}^\prime r + \alpha_1 \alpha_1^\prime + \alpha_1 \beta_1^\prime \gamma^{2} + \alpha_1 \beta_1^\prime \gamma \kappa_\text{on} r + \alpha_1 \beta_1^\prime \gamma + \alpha_1 \gamma \kappa_\text{off}^\prime + \alpha_1 \gamma \kappa_\text{on} r + \alpha_1 \gamma \kappa_\text{on}^\prime r + \alpha_1 \gamma + \alpha_1 \kappa_\text{off}^\prime \kappa_\text{on} r + \alpha_1 \kappa_\text{off}^\prime + \alpha_1^\prime \beta_1 \gamma^{2} + \alpha_1^\prime \beta_1 \gamma \kappa_\text{on}^\prime r + \alpha_1^\prime \beta_1 \gamma + \alpha_1^\prime \gamma \kappa_\text{off} + \alpha_1^\prime \gamma \kappa_\text{on} r + \alpha_1^\prime \gamma \kappa_\text{on}^\prime r + \alpha_1^\prime \gamma + \alpha_1^\prime \kappa_\text{off} \kappa_\text{on}^\prime r + \alpha_1^\prime \kappa_\text{off} + \beta_1 \beta_1^\prime \gamma^{3} + \beta_1 \beta_1^\prime \gamma^{2} + \beta_1 \gamma^{2} \kappa_\text{off}^\prime + \beta_1 \gamma^{2} \kappa_\text{on}^\prime r + \beta_1 \gamma^{2} + \beta_1 \gamma \kappa_\text{off}^\prime + \beta_1^\prime \gamma^{2} \kappa_\text{off} + \beta_1^\prime \gamma^{2} \kappa_\text{on} r + \beta_1^\prime \gamma^{2} + \beta_1^\prime \gamma \kappa_\text{off} + \gamma \kappa_\text{off} \kappa_\text{off}^\prime + \gamma \kappa_\text{off} \kappa_\text{on}^\prime r + \gamma \kappa_\text{off} + \gamma \kappa_\text{off}^\prime \kappa_\text{on} r + \gamma \kappa_\text{off}^\prime + \kappa_\text{off} \kappa_\text{off}^\prime$

```python
poly(u1).coeffs().__len__()
```

    282

```python
min(poly(u1).coeffs())
```

$\displaystyle 1$

```python
v1 = (cfs[1]*cfs[2]*cfs[3] - cfs[1]**2*cfs[4]-cfs[0]*cfs[3]**2).expand().simplify()
```

```python
poly(v1).coeffs().__len__()
```

    5091

```python
min(poly(v1).coeffs())
```

$\displaystyle 1$

```python
[cf.is_positive for cf in [*cfs, u1, v1]]
```

    [True, True, True, True, True, True, True]

**All coefficients and necessary expressions are always positive.**

```python
coefficient_plot([cfs[4], '$a_0$', 1, range(3), 55],
                 [cfs[3], '$a_1$', 1, range(3), 55],
                 [cfs[2], '$a_2$', 1, range(3), 55],
                 [cfs[1], '$a_3$', 1, range(3), 55],
                 [u1, '$u$', 1.5],
                 [v1, '$v$', 10, range(0, 21, 5)])
#plt.savefig('rh_c2ko.svg')
```

```python

```

## MMI1

```python
rhs_R = s - kon*R*r + koff*C1 - R + b*g*C1
rhs_r = 1 - kon*R*r + koff*C1 - g*r + a*C1
rhs_C1 = kon*R*r - koff*C1 - a*C1 - b*g*C1
```

```python
rhs_R
```

$\displaystyle C_{1} \beta \gamma + C_{1} \kappa_\text{off} - R \kappa_\text{on} r - R + \sigma_R$

```python
rhs_r
```

$\displaystyle C_{1} \alpha + C_{1} \kappa_\text{off} - R \kappa_\text{on} r - \gamma r + 1$

```python
rhs_C1
```

$\displaystyle - C_{1} \alpha - C_{1} \beta \gamma - C_{1} \kappa_\text{off} + R \kappa_\text{on} r$

```python
J = Matrix([rhs_R, rhs_r, rhs_C1]).jacobian(Matrix([R, r, C1]))
J
```

$\displaystyle \left[\begin{matrix}- \kappa_\text{on} r - 1 & - R \kappa_\text{on} & \beta \gamma + \kappa_\text{off}\\- \kappa_\text{on} r & - R \kappa_\text{on} - \gamma & \alpha + \kappa_\text{off}\\\kappa_\text{on} r & R \kappa_\text{on} & - \alpha - \beta \gamma - \kappa_\text{off}\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
p
```

$\displaystyle \operatorname{PurePoly}{\left( \lambda^{3} + \left(R \kappa_\text{on} + \alpha + \beta \gamma + \gamma + \kappa_\text{off} + \kappa_\text{on} r + 1\right) \lambda^{2} + \left(R \beta \gamma \kappa_\text{on} + R \kappa_\text{on} + \alpha \gamma + \alpha \kappa_\text{on} r + \alpha + \beta \gamma^{2} + \beta \gamma + \gamma \kappa_\text{off} + \gamma \kappa_\text{on} r + \gamma + \kappa_\text{off}\right) \lambda + R \beta \gamma \kappa_\text{on} + \alpha \gamma \kappa_\text{on} r + \alpha \gamma + \beta \gamma^{2} + \gamma \kappa_\text{off}, \lambda, domain=\mathbb{Z}\left[r, R, \alpha, \beta, \gamma, \kappa_\text{off}, \kappa_\text{on}\right] \right)}$

```python
# Third-order system: all four coefficients must be positive...
cfs[0]
```

$\displaystyle 1$

```python
cfs[1]
```

$\displaystyle R \kappa_\text{on} + \alpha + \beta \gamma + \gamma + \kappa_\text{off} + \kappa_\text{on} r + 1$

```python
cfs[2]
```

$\displaystyle R \beta \gamma \kappa_\text{on} + R \kappa_\text{on} + \alpha \gamma + \alpha \kappa_\text{on} r + \alpha + \beta \gamma^{2} + \beta \gamma + \gamma \kappa_\text{off} + \gamma \kappa_\text{on} r + \gamma + \kappa_\text{off}$

```python
cfs[3]
```

$\displaystyle R \beta \gamma \kappa_\text{on} + \alpha \gamma \kappa_\text{on} r + \alpha \gamma + \beta \gamma^{2} + \gamma \kappa_\text{off}$

```python
# ...as must Δ2 = a1*a2 - a0*a3
u = (cfs[1]*cfs[2] - cfs[0]*cfs[3]).expand().simplify()
u
```

$\displaystyle R^{2} \beta \gamma \kappa_\text{on}^{2} + R^{2} \kappa_\text{on}^{2} + R \alpha \beta \gamma \kappa_\text{on} + R \alpha \gamma \kappa_\text{on} + R \alpha \kappa_\text{on}^{2} r + 2 R \alpha \kappa_\text{on} + R \beta^{2} \gamma^{2} \kappa_\text{on} + 2 R \beta \gamma^{2} \kappa_\text{on} + R \beta \gamma \kappa_\text{off} \kappa_\text{on} + R \beta \gamma \kappa_\text{on}^{2} r + 2 R \beta \gamma \kappa_\text{on} + R \gamma \kappa_\text{off} \kappa_\text{on} + R \gamma \kappa_\text{on}^{2} r + 2 R \gamma \kappa_\text{on} + 2 R \kappa_\text{off} \kappa_\text{on} + R \kappa_\text{on}^{2} r + R \kappa_\text{on} + \alpha^{2} \gamma + \alpha^{2} \kappa_\text{on} r + \alpha^{2} + 2 \alpha \beta \gamma^{2} + \alpha \beta \gamma \kappa_\text{on} r + 2 \alpha \beta \gamma + \alpha \gamma^{2} + 2 \alpha \gamma \kappa_\text{off} + 2 \alpha \gamma \kappa_\text{on} r + 2 \alpha \gamma + \alpha \kappa_\text{off} \kappa_\text{on} r + 2 \alpha \kappa_\text{off} + \alpha \kappa_\text{on}^{2} r^{2} + 2 \alpha \kappa_\text{on} r + \alpha + \beta^{2} \gamma^{3} + \beta^{2} \gamma^{2} + \beta \gamma^{3} + 2 \beta \gamma^{2} \kappa_\text{off} + 2 \beta \gamma^{2} \kappa_\text{on} r + 2 \beta \gamma^{2} + 2 \beta \gamma \kappa_\text{off} + \beta \gamma \kappa_\text{on} r + \beta \gamma + \gamma^{2} \kappa_\text{off} + \gamma^{2} \kappa_\text{on} r + \gamma^{2} + \gamma \kappa_\text{off}^{2} + 2 \gamma \kappa_\text{off} \kappa_\text{on} r + 2 \gamma \kappa_\text{off} + \gamma \kappa_\text{on}^{2} r^{2} + 2 \gamma \kappa_\text{on} r + \gamma + \kappa_\text{off}^{2} + \kappa_\text{off} \kappa_\text{on} r + \kappa_\text{off}$

```python
min(Poly(u).coeffs())
```

$\displaystyle 1$

```python
[exp.is_positive for exp in [*cfs, u]]
```

    [True, True, True, True, True]

**All coefficients and required expressions are positive.**

```python
coefficient_plot([cfs[3], '$a_0$', 1.3, range(3), 12],
                 [cfs[2], '$a_1$', 1.3, range(3), 12],
                 [cfs[1], '$a_2$', 1.3, range(3), 12],
                 [u, '$u$', 2, range(4)])
#plt.savefig('rh_mmi1.svg')
```

```python

```

## MMI2-SSB

```python
rhs_R = s - 2*kon*R*r + 2*koff*C1 - R + 2*b1*g*C1
rhs_r = 1 - 2*kon*R*r + 2*koff*C1 - 2*kon*C1*r + 2*koff*C2 - g*r + 2*a1*C1 + 2*a2*C2
rhs_C1 = kon*R*r - koff*C1 - kon*C1*r + koff*C2 - a1*C1 - b1*g*C1 + b2*g*C2
rhs_C2 = 2*kon*C1*r - 2*koff*C2 - a2*C2 - 2*b2*g*C2
```

```python
rhs_R
```

$\displaystyle 2 C_{1} \beta_1 \gamma + 2 C_{1} \kappa_\text{off} - 2 R \kappa_\text{on} r - R + \sigma_R$

```python
rhs_r
```

$\displaystyle 2 C_{1} \alpha_1 + 2 C_{1} \kappa_\text{off} - 2 C_{1} \kappa_\text{on} r + 2 C_{2} \alpha_2 + 2 C_{2} \kappa_\text{off} - 2 R \kappa_\text{on} r - \gamma r + 1$

```python
rhs_C1
```

$\displaystyle - C_{1} \alpha_1 - C_{1} \beta_1 \gamma - C_{1} \kappa_\text{off} - C_{1} \kappa_\text{on} r + C_{2} \beta_2 \gamma + C_{2} \kappa_\text{off} + R \kappa_\text{on} r$

```python
rhs_C2
```

$\displaystyle 2 C_{1} \kappa_\text{on} r - C_{2} \alpha_2 - 2 C_{2} \beta_2 \gamma - 2 C_{2} \kappa_\text{off}$

```python
J = Matrix([rhs_R, rhs_r, rhs_C1, rhs_C2]).jacobian(Matrix([R, r, C1, C2]))
J
```

$\displaystyle \left[\begin{matrix}- 2 \kappa_\text{on} r - 1 & - 2 R \kappa_\text{on} & 2 \beta_1 \gamma + 2 \kappa_\text{off} & 0\\- 2 \kappa_\text{on} r & - 2 C_{1} \kappa_\text{on} - 2 R \kappa_\text{on} - \gamma & 2 \alpha_1 + 2 \kappa_\text{off} - 2 \kappa_\text{on} r & 2 \alpha_2 + 2 \kappa_\text{off}\\\kappa_\text{on} r & - C_{1} \kappa_\text{on} + R \kappa_\text{on} & - \alpha_1 - \beta_1 \gamma - \kappa_\text{off} - \kappa_\text{on} r & \beta_2 \gamma + \kappa_\text{off}\\0 & 2 C_{1} \kappa_\text{on} & 2 \kappa_\text{on} r & - \alpha_2 - 2 \beta_2 \gamma - 2 \kappa_\text{off}\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
p
```

$\displaystyle \operatorname{PurePoly}{\left( \lambda^{4} + \left(2 C_{1} \kappa_\text{on} + 2 R \kappa_\text{on} + \alpha_1 + \alpha_2 + \beta_1 \gamma + 2 \beta_2 \gamma + \gamma + 3 \kappa_\text{off} + 3 \kappa_\text{on} r + 1\right) \lambda^{3} + \left(4 C_{1} \alpha_1 \kappa_\text{on} - 2 C_{1} \alpha_2 \kappa_\text{on} + 2 C_{1} \beta_1 \gamma \kappa_\text{on} + 4 C_{1} \beta_2 \gamma \kappa_\text{on} + 4 C_{1} \kappa_\text{off} \kappa_\text{on} + 4 C_{1} \kappa_\text{on}^{2} r + 2 C_{1} \kappa_\text{on} + 2 R \alpha_2 \kappa_\text{on} + 2 R \beta_1 \gamma \kappa_\text{on} + 4 R \beta_2 \gamma \kappa_\text{on} + 4 R \kappa_\text{off} \kappa_\text{on} + 4 R \kappa_\text{on}^{2} r + 2 R \kappa_\text{on} + \alpha_1 \alpha_2 + 2 \alpha_1 \beta_2 \gamma + \alpha_1 \gamma + 2 \alpha_1 \kappa_\text{off} + 2 \alpha_1 \kappa_\text{on} r + \alpha_1 + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma + \alpha_2 \kappa_\text{off} + 3 \alpha_2 \kappa_\text{on} r + \alpha_2 + 2 \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} + 2 \beta_1 \gamma \kappa_\text{off} + \beta_1 \gamma + 2 \beta_2 \gamma^{2} + 2 \beta_2 \gamma \kappa_\text{off} + 4 \beta_2 \gamma \kappa_\text{on} r + 2 \beta_2 \gamma + 3 \gamma \kappa_\text{off} + 3 \gamma \kappa_\text{on} r + \gamma + 2 \kappa_\text{off}^{2} + 4 \kappa_\text{off} \kappa_\text{on} r + 3 \kappa_\text{off} + 2 \kappa_\text{on}^{2} r^{2} + \kappa_\text{on} r\right) \lambda^{2} + \left(4 C_{1} \alpha_1 \beta_2 \gamma \kappa_\text{on} + 8 C_{1} \alpha_1 \kappa_\text{on}^{2} r + 4 C_{1} \alpha_1 \kappa_\text{on} - 2 C_{1} \alpha_2 \beta_1 \gamma \kappa_\text{on} - 4 C_{1} \alpha_2 \kappa_\text{on}^{2} r - 2 C_{1} \alpha_2 \kappa_\text{on} + 4 C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} - 4 C_{1} \beta_1 \gamma \kappa_\text{on}^{2} r + 2 C_{1} \beta_1 \gamma \kappa_\text{on} + 4 C_{1} \beta_2 \gamma \kappa_\text{off} \kappa_\text{on} + 8 C_{1} \beta_2 \gamma \kappa_\text{on}^{2} r + 4 C_{1} \beta_2 \gamma \kappa_\text{on} + 4 C_{1} \kappa_\text{off} \kappa_\text{on} + 2 R \alpha_2 \beta_1 \gamma \kappa_\text{on} + 2 R \alpha_2 \kappa_\text{on} + 4 R \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} + 4 R \beta_1 \gamma \kappa_\text{off} \kappa_\text{on} + 2 R \beta_1 \gamma \kappa_\text{on} + 4 R \beta_2 \gamma \kappa_\text{on}^{2} r + 4 R \beta_2 \gamma \kappa_\text{on} + 4 R \kappa_\text{off} \kappa_\text{on} + 4 R \kappa_\text{on}^{2} r + \alpha_1 \alpha_2 \gamma + 2 \alpha_1 \alpha_2 \kappa_\text{on} r + \alpha_1 \alpha_2 + 2 \alpha_1 \beta_2 \gamma^{2} + 4 \alpha_1 \beta_2 \gamma \kappa_\text{on} r + 2 \alpha_1 \beta_2 \gamma + 2 \alpha_1 \gamma \kappa_\text{off} + 2 \alpha_1 \gamma \kappa_\text{on} r + \alpha_1 \gamma + 4 \alpha_1 \kappa_\text{off} \kappa_\text{on} r + 2 \alpha_1 \kappa_\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma \kappa_\text{off} + 3 \alpha_2 \gamma \kappa_\text{on} r + \alpha_2 \gamma + \alpha_2 \kappa_\text{off} + 2 \alpha_2 \kappa_\text{on}^{2} r^{2} + \alpha_2 \kappa_\text{on} r + 2 \beta_1 \beta_2 \gamma^{3} + 2 \beta_1 \beta_2 \gamma^{2} + 2 \beta_1 \gamma^{2} \kappa_\text{off} + \beta_1 \gamma^{2} + 2 \beta_1 \gamma \kappa_\text{off} + 2 \beta_2 \gamma^{2} \kappa_\text{off} + 4 \beta_2 \gamma^{2} \kappa_\text{on} r + 2 \beta_2 \gamma^{2} + 2 \beta_2 \gamma \kappa_\text{off} + 2 \gamma \kappa_\text{off}^{2} + 4 \gamma \kappa_\text{off} \kappa_\text{on} r + 3 \gamma \kappa_\text{off} + 2 \gamma \kappa_\text{on}^{2} r^{2} + \gamma \kappa_\text{on} r + 2 \kappa_\text{off}^{2}\right) \lambda + 8 C_{1} \alpha_1 \beta_2 \gamma \kappa_\text{on}^{2} r + 4 C_{1} \alpha_1 \beta_2 \gamma \kappa_\text{on} - 4 C_{1} \alpha_2 \beta_1 \gamma \kappa_\text{on}^{2} r - 2 C_{1} \alpha_2 \beta_1 \gamma \kappa_\text{on} + 4 C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} + 4 C_{1} \beta_2 \gamma \kappa_\text{off} \kappa_\text{on} + 2 R \alpha_2 \beta_1 \gamma \kappa_\text{on} + 4 R \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} + 4 R \beta_1 \gamma \kappa_\text{off} \kappa_\text{on} + 4 R \beta_2 \gamma \kappa_\text{on}^{2} r + 2 \alpha_1 \alpha_2 \gamma \kappa_\text{on} r + \alpha_1 \alpha_2 \gamma + 4 \alpha_1 \beta_2 \gamma^{2} \kappa_\text{on} r + 2 \alpha_1 \beta_2 \gamma^{2} + 4 \alpha_1 \gamma \kappa_\text{off} \kappa_\text{on} r + 2 \alpha_1 \gamma \kappa_\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \gamma \kappa_\text{off} + 2 \alpha_2 \gamma \kappa_\text{on}^{2} r^{2} + \alpha_2 \gamma \kappa_\text{on} r + 2 \beta_1 \beta_2 \gamma^{3} + 2 \beta_1 \gamma^{2} \kappa_\text{off} + 2 \beta_2 \gamma^{2} \kappa_\text{off} + 2 \gamma \kappa_\text{off}^{2}, \lambda, domain=\mathbb{Z}\left[r, C_{1}, R, \alpha_1, \alpha_2, \beta_1, \beta_2, \gamma, \kappa_\text{off}, \kappa_\text{on}\right] \right)}$

```python
# Fourth-order system: all five coefficients must be positive...
cfs[0]
```

$\displaystyle 1$

```python
cfs[1]
```

$\displaystyle 2 C_{1} \kappa_\text{on} + 2 R \kappa_\text{on} + \alpha_1 + \alpha_2 + \beta_1 \gamma + 2 \beta_2 \gamma + \gamma + 3 \kappa_\text{off} + 3 \kappa_\text{on} r + 1$

```python
cfs[2].factor(C1, kon)
```

$\displaystyle 4 C_{1} \kappa_\text{on}^{2} r + C_{1} \kappa_\text{on} \left(4 \alpha_1 - 2 \alpha_2 + 2 \beta_1 \gamma + 4 \beta_2 \gamma + 4 \kappa_\text{off} + 2\right) + \alpha_1 \alpha_2 + 2 \alpha_1 \beta_2 \gamma + \alpha_1 \gamma + 2 \alpha_1 \kappa_\text{off} + \alpha_1 + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma + \alpha_2 \kappa_\text{off} + \alpha_2 + 2 \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} + 2 \beta_1 \gamma \kappa_\text{off} + \beta_1 \gamma + 2 \beta_2 \gamma^{2} + 2 \beta_2 \gamma \kappa_\text{off} + 2 \beta_2 \gamma + 3 \gamma \kappa_\text{off} + \gamma + 2 \kappa_\text{off}^{2} + 3 \kappa_\text{off} + \kappa_\text{on}^{2} \left(4 R r + 2 r^{2}\right) + \kappa_\text{on} \left(2 R \alpha_2 + 2 R \beta_1 \gamma + 4 R \beta_2 \gamma + 4 R \kappa_\text{off} + 2 R + 2 \alpha_1 r + 3 \alpha_2 r + 4 \beta_2 \gamma r + 3 \gamma r + 4 \kappa_\text{off} r + r\right)$

```python
[c for c in Poly(cfs[2]).coeffs() if c < 0] # negative coefficient
```

    [-2]

```python
cfs[3].factor(C1, kon)
```

$\displaystyle C_{1} \kappa_\text{on}^{2} \left(8 \alpha_1 r - 4 \alpha_2 r - 4 \beta_1 \gamma r + 8 \beta_2 \gamma r\right) + C_{1} \kappa_\text{on} \left(4 \alpha_1 \beta_2 \gamma + 4 \alpha_1 - 2 \alpha_2 \beta_1 \gamma - 2 \alpha_2 + 4 \beta_1 \beta_2 \gamma^{2} + 2 \beta_1 \gamma + 4 \beta_2 \gamma \kappa_\text{off} + 4 \beta_2 \gamma + 4 \kappa_\text{off}\right) + \alpha_1 \alpha_2 \gamma + \alpha_1 \alpha_2 + 2 \alpha_1 \beta_2 \gamma^{2} + 2 \alpha_1 \beta_2 \gamma + 2 \alpha_1 \gamma \kappa_\text{off} + \alpha_1 \gamma + 2 \alpha_1 \kappa_\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma \kappa_\text{off} + \alpha_2 \gamma + \alpha_2 \kappa_\text{off} + 2 \beta_1 \beta_2 \gamma^{3} + 2 \beta_1 \beta_2 \gamma^{2} + 2 \beta_1 \gamma^{2} \kappa_\text{off} + \beta_1 \gamma^{2} + 2 \beta_1 \gamma \kappa_\text{off} + 2 \beta_2 \gamma^{2} \kappa_\text{off} + 2 \beta_2 \gamma^{2} + 2 \beta_2 \gamma \kappa_\text{off} + 2 \gamma \kappa_\text{off}^{2} + 3 \gamma \kappa_\text{off} + 2 \kappa_\text{off}^{2} + \kappa_\text{on}^{2} \left(4 R \beta_2 \gamma r + 4 R r + 2 \alpha_2 r^{2} + 2 \gamma r^{2}\right) + \kappa_\text{on} \left(2 R \alpha_2 \beta_1 \gamma + 2 R \alpha_2 + 4 R \beta_1 \beta_2 \gamma^{2} + 4 R \beta_1 \gamma \kappa_\text{off} + 2 R \beta_1 \gamma + 4 R \beta_2 \gamma + 4 R \kappa_\text{off} + 2 \alpha_1 \alpha_2 r + 4 \alpha_1 \beta_2 \gamma r + 2 \alpha_1 \gamma r + 4 \alpha_1 \kappa_\text{off} r + 3 \alpha_2 \gamma r + \alpha_2 r + 4 \beta_2 \gamma^{2} r + 4 \gamma \kappa_\text{off} r + \gamma r\right)$

```python
[c for c in Poly(cfs[3]).coeffs() if c < 0]
```

    [-4, -4, -2, -2]

```python
cfs[4]
```

$\displaystyle 8 C_{1} \alpha_1 \beta_2 \gamma \kappa_\text{on}^{2} r + 4 C_{1} \alpha_1 \beta_2 \gamma \kappa_\text{on} - 4 C_{1} \alpha_2 \beta_1 \gamma \kappa_\text{on}^{2} r - 2 C_{1} \alpha_2 \beta_1 \gamma \kappa_\text{on} + 4 C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} + 4 C_{1} \beta_2 \gamma \kappa_\text{off} \kappa_\text{on} + 2 R \alpha_2 \beta_1 \gamma \kappa_\text{on} + 4 R \beta_1 \beta_2 \gamma^{2} \kappa_\text{on} + 4 R \beta_1 \gamma \kappa_\text{off} \kappa_\text{on} + 4 R \beta_2 \gamma \kappa_\text{on}^{2} r + 2 \alpha_1 \alpha_2 \gamma \kappa_\text{on} r + \alpha_1 \alpha_2 \gamma + 4 \alpha_1 \beta_2 \gamma^{2} \kappa_\text{on} r + 2 \alpha_1 \beta_2 \gamma^{2} + 4 \alpha_1 \gamma \kappa_\text{off} \kappa_\text{on} r + 2 \alpha_1 \gamma \kappa_\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \gamma \kappa_\text{off} + 2 \alpha_2 \gamma \kappa_\text{on}^{2} r^{2} + \alpha_2 \gamma \kappa_\text{on} r + 2 \beta_1 \beta_2 \gamma^{3} + 2 \beta_1 \gamma^{2} \kappa_\text{off} + 2 \beta_2 \gamma^{2} \kappa_\text{off} + 2 \gamma \kappa_\text{off}^{2}$

```python
[c for c in Poly(cfs[4]).coeffs() if c < 0]
```

    [-4, -2]

```python
[cf.is_positive for cf in cfs]
```

    [True, True, None, None, None]

**Not all coefficients are always positive - test inconclusive.**

```python
[(i, len(Poly(cf).terms())) for (i, cf) in enumerate(cfs) if isinstance(cf, Add)]
```

    [(1, 10), (2, 40), (3, 56), (4, 24)]

```python

```

## MMI2-ASB

```python
rhs_R = s - kon1*R*r + koff1*C1 - R + b1*g*C1
rhs_r = 1 - kon1*R*r + koff1*C1 - kon2*C1*r + koff2*C2 - g*r + a1*C1 + 2*a2*C2
rhs_C1 = kon1*R*r - koff1*C1 - kon2*C1*r + koff2*C2 - a1*C1 - b1*g*C1 + b2*g*C2
rhs_C2 = kon2*C1*r - koff2*C2 - a2*C2 - b2*g*C2
```

```python
rhs_R
```

$\displaystyle C_{1} \beta_1 \gamma + C_{1} \kappa_1^\text{off} - R \kappa_1^\text{on} r - R + \sigma_R$

```python
rhs_r
```

$\displaystyle C_{1} \alpha_1 + C_{1} \kappa_1^\text{off} - C_{1} \kappa_2^\text{on} r + 2 C_{2} \alpha_2 + C_{2} \kappa_2^\text{off} - R \kappa_1^\text{on} r - \gamma r + 1$

```python
rhs_C1
```

$\displaystyle - C_{1} \alpha_1 - C_{1} \beta_1 \gamma - C_{1} \kappa_1^\text{off} - C_{1} \kappa_2^\text{on} r + C_{2} \beta_2 \gamma + C_{2} \kappa_2^\text{off} + R \kappa_1^\text{on} r$

```python
rhs_C2
```

$\displaystyle C_{1} \kappa_2^\text{on} r - C_{2} \alpha_2 - C_{2} \beta_2 \gamma - C_{2} \kappa_2^\text{off}$

```python
J = Matrix([rhs_R, rhs_r, rhs_C1, rhs_C2]).jacobian(Matrix([R, r, C1, C2]))
J
```

$\displaystyle \left[\begin{matrix}- \kappa_1^\text{on} r - 1 & - R \kappa_1^\text{on} & \beta_1 \gamma + \kappa_1^\text{off} & 0\\- \kappa_1^\text{on} r & - C_{1} \kappa_2^\text{on} - R \kappa_1^\text{on} - \gamma & \alpha_1 + \kappa_1^\text{off} - \kappa_2^\text{on} r & 2 \alpha_2 + \kappa_2^\text{off}\\\kappa_1^\text{on} r & - C_{1} \kappa_2^\text{on} + R \kappa_1^\text{on} & - \alpha_1 - \beta_1 \gamma - \kappa_1^\text{off} - \kappa_2^\text{on} r & \beta_2 \gamma + \kappa_2^\text{off}\\0 & C_{1} \kappa_2^\text{on} & \kappa_2^\text{on} r & - \alpha_2 - \beta_2 \gamma - \kappa_2^\text{off}\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
p
```

$\displaystyle \operatorname{PurePoly}{\left( \lambda^{4} + \left(C_{1} \kappa_2^\text{on} + R \kappa_1^\text{on} + \alpha_1 + \alpha_2 + \beta_1 \gamma + \beta_2 \gamma + \gamma + \kappa_1^\text{off} + \kappa_1^\text{on} r + \kappa_2^\text{off} + \kappa_2^\text{on} r + 1\right) \lambda^{3} + \left(2 C_{1} \alpha_1 \kappa_2^\text{on} - C_{1} \alpha_2 \kappa_2^\text{on} + C_{1} \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \kappa_1^\text{off} \kappa_2^\text{on} + C_{1} \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \kappa_2^\text{on} + R \alpha_2 \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} + R \beta_2 \gamma \kappa_1^\text{on} + R \kappa_1^\text{on} \kappa_2^\text{off} + 2 R \kappa_1^\text{on} \kappa_2^\text{on} r + R \kappa_1^\text{on} + \alpha_1 \alpha_2 + \alpha_1 \beta_2 \gamma + \alpha_1 \gamma + \alpha_1 \kappa_1^\text{on} r + \alpha_1 \kappa_2^\text{off} + \alpha_1 + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma + \alpha_2 \kappa_1^\text{off} + \alpha_2 \kappa_1^\text{on} r + \alpha_2 \kappa_2^\text{on} r + \alpha_2 + \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} + \beta_1 \gamma \kappa_2^\text{off} + \beta_1 \gamma + \beta_2 \gamma^{2} + \beta_2 \gamma \kappa_1^\text{off} + \beta_2 \gamma \kappa_1^\text{on} r + \beta_2 \gamma + \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{on} r + \gamma \kappa_2^\text{off} + \gamma \kappa_2^\text{on} r + \gamma + \kappa_1^\text{off} \kappa_2^\text{off} + \kappa_1^\text{off} + \kappa_1^\text{on} \kappa_2^\text{off} r + \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \kappa_2^\text{off} + \kappa_2^\text{on} r\right) \lambda^{2} + \left(C_{1} \alpha_1 \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \alpha_1 \kappa_1^\text{on} \kappa_2^\text{on} r + 2 C_{1} \alpha_1 \kappa_2^\text{on} - C_{1} \alpha_2 \beta_1 \gamma \kappa_2^\text{on} - C_{1} \alpha_2 \kappa_1^\text{on} \kappa_2^\text{on} r - C_{1} \alpha_2 \kappa_2^\text{on} + C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_2^\text{on} - C_{1} \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{off} \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \kappa_1^\text{off} \kappa_2^\text{on} + R \alpha_2 \beta_1 \gamma \kappa_1^\text{on} + R \alpha_2 \kappa_1^\text{on} + R \beta_1 \beta_2 \gamma^{2} \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} + R \beta_1 \gamma \kappa_1^\text{on} + R \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + R \beta_2 \gamma \kappa_1^\text{on} + R \kappa_1^\text{on} \kappa_2^\text{off} + 2 R \kappa_1^\text{on} \kappa_2^\text{on} r + \alpha_1 \alpha_2 \gamma + \alpha_1 \alpha_2 \kappa_1^\text{on} r + \alpha_1 \alpha_2 + \alpha_1 \beta_2 \gamma^{2} + \alpha_1 \beta_2 \gamma \kappa_1^\text{on} r + \alpha_1 \beta_2 \gamma + \alpha_1 \gamma \kappa_1^\text{on} r + \alpha_1 \gamma \kappa_2^\text{off} + \alpha_1 \gamma + \alpha_1 \kappa_1^\text{on} \kappa_2^\text{off} r + \alpha_1 \kappa_2^\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma \kappa_1^\text{off} + \alpha_2 \gamma \kappa_1^\text{on} r + \alpha_2 \gamma \kappa_2^\text{on} r + \alpha_2 \gamma + \alpha_2 \kappa_1^\text{off} + \alpha_2 \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \alpha_2 \kappa_2^\text{on} r + \beta_1 \beta_2 \gamma^{3} + \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} \kappa_2^\text{off} + \beta_1 \gamma^{2} + \beta_1 \gamma \kappa_2^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{on} r + \beta_2 \gamma^{2} + \beta_2 \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{off} \kappa_2^\text{off} + \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{on} \kappa_2^\text{off} r + \gamma \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \gamma \kappa_2^\text{off} + \gamma \kappa_2^\text{on} r + \kappa_1^\text{off} \kappa_2^\text{off}\right) \lambda + C_{1} \alpha_1 \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \alpha_1 \beta_2 \gamma \kappa_2^\text{on} - C_{1} \alpha_2 \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r - C_{1} \alpha_2 \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{off} \kappa_2^\text{on} + R \alpha_2 \beta_1 \gamma \kappa_1^\text{on} + R \beta_1 \beta_2 \gamma^{2} \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} + R \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + \alpha_1 \alpha_2 \gamma \kappa_1^\text{on} r + \alpha_1 \alpha_2 \gamma + \alpha_1 \beta_2 \gamma^{2} \kappa_1^\text{on} r + \alpha_1 \beta_2 \gamma^{2} + \alpha_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} r + \alpha_1 \gamma \kappa_2^\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \gamma \kappa_1^\text{off} + \alpha_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \alpha_2 \gamma \kappa_2^\text{on} r + \beta_1 \beta_2 \gamma^{3} + \beta_1 \gamma^{2} \kappa_2^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{off} + \gamma \kappa_1^\text{off} \kappa_2^\text{off}, \lambda, domain=\mathbb{Z}\left[r, C_{1}, R, \alpha_1, \alpha_2, \beta_1, \beta_2, \gamma, \kappa_1^\text{off}, \kappa_1^\text{on}, \kappa_2^\text{off}, \kappa_2^\text{on}\right] \right)}$

```python
# Fourth-order system: all five coefficients must be positive...
cfs[0]
```

$\displaystyle 1$

```python
cfs[1]
```

$\displaystyle C_{1} \kappa_2^\text{on} + R \kappa_1^\text{on} + \alpha_1 + \alpha_2 + \beta_1 \gamma + \beta_2 \gamma + \gamma + \kappa_1^\text{off} + \kappa_1^\text{on} r + \kappa_2^\text{off} + \kappa_2^\text{on} r + 1$

```python
cfs[2]
```

$\displaystyle 2 C_{1} \alpha_1 \kappa_2^\text{on} - C_{1} \alpha_2 \kappa_2^\text{on} + C_{1} \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \kappa_1^\text{off} \kappa_2^\text{on} + C_{1} \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \kappa_2^\text{on} + R \alpha_2 \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} + R \beta_2 \gamma \kappa_1^\text{on} + R \kappa_1^\text{on} \kappa_2^\text{off} + 2 R \kappa_1^\text{on} \kappa_2^\text{on} r + R \kappa_1^\text{on} + \alpha_1 \alpha_2 + \alpha_1 \beta_2 \gamma + \alpha_1 \gamma + \alpha_1 \kappa_1^\text{on} r + \alpha_1 \kappa_2^\text{off} + \alpha_1 + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma + \alpha_2 \kappa_1^\text{off} + \alpha_2 \kappa_1^\text{on} r + \alpha_2 \kappa_2^\text{on} r + \alpha_2 + \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} + \beta_1 \gamma \kappa_2^\text{off} + \beta_1 \gamma + \beta_2 \gamma^{2} + \beta_2 \gamma \kappa_1^\text{off} + \beta_2 \gamma \kappa_1^\text{on} r + \beta_2 \gamma + \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{on} r + \gamma \kappa_2^\text{off} + \gamma \kappa_2^\text{on} r + \gamma + \kappa_1^\text{off} \kappa_2^\text{off} + \kappa_1^\text{off} + \kappa_1^\text{on} \kappa_2^\text{off} r + \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \kappa_2^\text{off} + \kappa_2^\text{on} r$

```python
cfs[3]
```

$\displaystyle C_{1} \alpha_1 \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \alpha_1 \kappa_1^\text{on} \kappa_2^\text{on} r + 2 C_{1} \alpha_1 \kappa_2^\text{on} - C_{1} \alpha_2 \beta_1 \gamma \kappa_2^\text{on} - C_{1} \alpha_2 \kappa_1^\text{on} \kappa_2^\text{on} r - C_{1} \alpha_2 \kappa_2^\text{on} + C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_2^\text{on} - C_{1} \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{off} \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \beta_2 \gamma \kappa_2^\text{on} + 2 C_{1} \kappa_1^\text{off} \kappa_2^\text{on} + R \alpha_2 \beta_1 \gamma \kappa_1^\text{on} + R \alpha_2 \kappa_1^\text{on} + R \beta_1 \beta_2 \gamma^{2} \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} + R \beta_1 \gamma \kappa_1^\text{on} + R \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + R \beta_2 \gamma \kappa_1^\text{on} + R \kappa_1^\text{on} \kappa_2^\text{off} + 2 R \kappa_1^\text{on} \kappa_2^\text{on} r + \alpha_1 \alpha_2 \gamma + \alpha_1 \alpha_2 \kappa_1^\text{on} r + \alpha_1 \alpha_2 + \alpha_1 \beta_2 \gamma^{2} + \alpha_1 \beta_2 \gamma \kappa_1^\text{on} r + \alpha_1 \beta_2 \gamma + \alpha_1 \gamma \kappa_1^\text{on} r + \alpha_1 \gamma \kappa_2^\text{off} + \alpha_1 \gamma + \alpha_1 \kappa_1^\text{on} \kappa_2^\text{off} r + \alpha_1 \kappa_2^\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \beta_1 \gamma + \alpha_2 \gamma \kappa_1^\text{off} + \alpha_2 \gamma \kappa_1^\text{on} r + \alpha_2 \gamma \kappa_2^\text{on} r + \alpha_2 \gamma + \alpha_2 \kappa_1^\text{off} + \alpha_2 \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \alpha_2 \kappa_2^\text{on} r + \beta_1 \beta_2 \gamma^{3} + \beta_1 \beta_2 \gamma^{2} + \beta_1 \gamma^{2} \kappa_2^\text{off} + \beta_1 \gamma^{2} + \beta_1 \gamma \kappa_2^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{on} r + \beta_2 \gamma^{2} + \beta_2 \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{off} \kappa_2^\text{off} + \gamma \kappa_1^\text{off} + \gamma \kappa_1^\text{on} \kappa_2^\text{off} r + \gamma \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \gamma \kappa_2^\text{off} + \gamma \kappa_2^\text{on} r + \kappa_1^\text{off} \kappa_2^\text{off}$

```python
cfs[4]
```

$\displaystyle C_{1} \alpha_1 \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + C_{1} \alpha_1 \beta_2 \gamma \kappa_2^\text{on} - C_{1} \alpha_2 \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r - C_{1} \alpha_2 \beta_1 \gamma \kappa_2^\text{on} + C_{1} \beta_1 \beta_2 \gamma^{2} \kappa_2^\text{on} + C_{1} \beta_2 \gamma \kappa_1^\text{off} \kappa_2^\text{on} + R \alpha_2 \beta_1 \gamma \kappa_1^\text{on} + R \beta_1 \beta_2 \gamma^{2} \kappa_1^\text{on} + R \beta_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} + R \beta_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r + \alpha_1 \alpha_2 \gamma \kappa_1^\text{on} r + \alpha_1 \alpha_2 \gamma + \alpha_1 \beta_2 \gamma^{2} \kappa_1^\text{on} r + \alpha_1 \beta_2 \gamma^{2} + \alpha_1 \gamma \kappa_1^\text{on} \kappa_2^\text{off} r + \alpha_1 \gamma \kappa_2^\text{off} + \alpha_2 \beta_1 \gamma^{2} + \alpha_2 \gamma \kappa_1^\text{off} + \alpha_2 \gamma \kappa_1^\text{on} \kappa_2^\text{on} r^{2} + \alpha_2 \gamma \kappa_2^\text{on} r + \beta_1 \beta_2 \gamma^{3} + \beta_1 \gamma^{2} \kappa_2^\text{off} + \beta_2 \gamma^{2} \kappa_1^\text{off} + \gamma \kappa_1^\text{off} \kappa_2^\text{off}$

```python
[cf.is_positive for cf in cfs]
```

    [True, True, None, None, None]

**Not all coefficients are always positive - test inconclusive.**

```python
[(i, len(Poly(cf).terms())) for (i, cf) in enumerate(cfs) if isinstance(cf, Add)]
```

    [(1, 12), (2, 44), (3, 58), (4, 24)]

```python

```

```python

```

## MMI2-DMI

```python
rhs_R = s - kon*R*r - konp*R*rp + koff*C1 + koffp*C1p - R + b1*g*C1 + b1p*gp*C1p
rhs_r = 1 - kon*R*r + koff*C1 - kon*C1p*r + koff*C2 - g*r + a1*C1 + a2*C2
rhs_rp = srp - konp*R*rp + koffp*C1p - konp*C1*rp + koffp*C2 - gp*rp + a1p*C1p + a2*C2
rhs_C1 = kon*R*r - koff*C1 - konp*C1*rp + koffp*C2 - a1*C1 - b1*g*C1 + b2p*gp*C2
rhs_C1p = konp*R*rp - koffp*C1p - kon*C1p*r + koff*C2 - a1p*C1p - b1p*gp*C1p + b2*g*C2
rhs_C2 =  kon*C1p*r + konp*C1*rp - koff*C2 - koffp*C2 -   a2*C2 - b2*g*C2 - b2p*gp*C2
```

```python
rhs_R
```

$\displaystyle C_{1} \beta_1 \gamma + C_{1} \kappa_\text{off} + C_1^\prime \beta_1^\prime \gamma^\prime + C_1^\prime \kappa_\text{off}^\prime - R \kappa_\text{on} r - R \kappa_\text{on}^\prime r^\prime - R + \sigma_R$

```python
rhs_r
```

$\displaystyle C_{1} \alpha_1 + C_{1} \kappa_\text{off} - C_1^\prime \kappa_\text{on} r + C_{2} \alpha_2 + C_{2} \kappa_\text{off} - R \kappa_\text{on} r - \gamma r + 1$

```python
rhs_rp
```

$\displaystyle - C_{1} \kappa_\text{on}^\prime r^\prime + C_1^\prime \alpha_1^\prime + C_1^\prime \kappa_\text{off}^\prime + C_{2} \alpha_2 + C_{2} \kappa_\text{off}^\prime - R \kappa_\text{on}^\prime r^\prime - \gamma^\prime r^\prime + \sigma_r^\prime$

```python
rhs_C1
```

$\displaystyle - C_{1} \alpha_1 - C_{1} \beta_1 \gamma - C_{1} \kappa_\text{off} - C_{1} \kappa_\text{on}^\prime r^\prime + C_{2} \beta_2^\prime \gamma^\prime + C_{2} \kappa_\text{off}^\prime + R \kappa_\text{on} r$

```python
rhs_C1p
```

$\displaystyle - C_1^\prime \alpha_1^\prime - C_1^\prime \beta_1^\prime \gamma^\prime - C_1^\prime \kappa_\text{off}^\prime - C_1^\prime \kappa_\text{on} r + C_{2} \beta_2 \gamma + C_{2} \kappa_\text{off} + R \kappa_\text{on}^\prime r^\prime$

```python
rhs_C2
```

$\displaystyle C_{1} \kappa_\text{on}^\prime r^\prime + C_1^\prime \kappa_\text{on} r - C_{2} \alpha_2 - C_{2} \beta_2 \gamma - C_{2} \beta_2^\prime \gamma^\prime - C_{2} \kappa_\text{off} - C_{2} \kappa_\text{off}^\prime$

```python
J = Matrix([rhs_R, rhs_r, rhs_rp, rhs_C1, rhs_C1p, rhs_C2]).jacobian(Matrix([R, r, rp, C1, C1p, C2]))
J
```

$\displaystyle \left[\begin{matrix}- \kappa_\text{on} r - \kappa_\text{on}^\prime r^\prime - 1 & - R \kappa_\text{on} & - R \kappa_\text{on}^\prime & \beta_1 \gamma + \kappa_\text{off} & \beta_1^\prime \gamma^\prime + \kappa_\text{off}^\prime & 0\\- \kappa_\text{on} r & - C_1^\prime \kappa_\text{on} - R \kappa_\text{on} - \gamma & 0 & \alpha_1 + \kappa_\text{off} & - \kappa_\text{on} r & \alpha_2 + \kappa_\text{off}\\- \kappa_\text{on}^\prime r^\prime & 0 & - C_{1} \kappa_\text{on}^\prime - R \kappa_\text{on}^\prime - \gamma^\prime & - \kappa_\text{on}^\prime r^\prime & \alpha_1^\prime + \kappa_\text{off}^\prime & \alpha_2 + \kappa_\text{off}^\prime\\\kappa_\text{on} r & R \kappa_\text{on} & - C_{1} \kappa_\text{on}^\prime & - \alpha_1 - \beta_1 \gamma - \kappa_\text{off} - \kappa_\text{on}^\prime r^\prime & 0 & \beta_2^\prime \gamma^\prime + \kappa_\text{off}^\prime\\\kappa_\text{on}^\prime r^\prime & - C_1^\prime \kappa_\text{on} & R \kappa_\text{on}^\prime & 0 & - \alpha_1^\prime - \beta_1^\prime \gamma^\prime - \kappa_\text{off}^\prime - \kappa_\text{on} r & \beta_2 \gamma + \kappa_\text{off}\\0 & C_1^\prime \kappa_\text{on} & C_{1} \kappa_\text{on}^\prime & \kappa_\text{on}^\prime r^\prime & \kappa_\text{on} r & - \alpha_2 - \beta_2 \gamma - \beta_2^\prime \gamma^\prime - \kappa_\text{off} - \kappa_\text{off}^\prime\end{matrix}\right]$

```python
p = J.charpoly()
cfs = p.all_coeffs()
```

```python
[(i, len(Poly(cf).terms())) for (i, cf) in enumerate(cfs) if isinstance(cf, Add)]
```

    [(1, 18), (2, 142), (3, 576), (4, 1075), (5, 906), (6, 274)]

```python
# This is a sixth-order system with huge coefficients - check simple necessary conditions like a[n] > 0 first
[c for c in poly(cfs[6]).coeffs() if c < 0]
```

    [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]

```python
max(c for c in poly(cfs[6]).coeffs())
```

$\displaystyle 1$

```python
a6 = cfs[-1]
a6.args[-1]
```

$\displaystyle - C_1^\prime R \alpha_2 \beta_1 \beta_1^\prime \gamma \gamma^\prime \kappa_\text{on}^{2} \kappa_\text{on}^\prime r$

```python
a6.args[81]
```

$\displaystyle C_{1} \alpha_1^\prime \beta_2^\prime \gamma \gamma^\prime \kappa_\text{off} \kappa_\text{on}^\prime$

```python
print(cfs[6].is_positive)
```

    None

**A necessary coefficient is not always positive - test inconclusive.**

```python

```


# Simple Linear Regression

We used the correlation function to measure the strength of the linear relationship between two variables. For most applications, knowing that such a linear relationship exists isn’t enough. We’ll want to be able to understand the nature of the relationship. This is where we’ll use simple linear regression.

## The Model

$$y_i = \beta x_i + \alpha + \epsilon_i$$

where

- $y_i$ is the number of minutes user i spends on the site daily,
- $x_i$ is the number of friends user i has
- $\alpha$ is the constant when x = 0.
- $ε_i$ is a (hopefully small) error term representing the fact that there are other factors not accounted for by this simple model.

# Least Squares Fit

最小二乘法

$$ y_i = X_i^T w$$

The constant could be represent by 1 in X

The squared error could be written as:

$$ \sum_{i = 1}^m (y_i -X_i^T w)^2 $$

If we know $\alpha$ and $\beta$, then we can make predictions.

Since we know the actual output $y_i$ we can compute the error for each pair.

Since the negative errors cancel out with the positive ones, we use squared errors.

The least squares solution is to choose the $\alpha$ and $\beta$ that make **sum_of_squared_errors** as small as possible.

The choice of beta means that when the input value increases by standard_deviation(x), the prediction increases by correlation(x, y) * standard_deviation(y).

- In the case when x and y are perfectly positively correlated, a one standard deviation increase in x results in a one-standard-deviation-of-y increase in the prediction.
- When they’re perfectly negatively correlated, the increase in x results in a decrease in the prediction.
- And when the correlation is zero, beta is zero, which means that changes in x don’t affect the prediction at all.

> In this case, the slope of the fitted line is equal to the correlation between y and x corrected by the ratio of standard deviations of these variables.

$$ y_i = \alpha + \beta x_i + \varepsilon_i $$

$$ \hat\varepsilon_i =y_i-a -b x_i $$

$$ \text{Find }\min_{a,\, b} Q(a, b), \quad \text{for } Q(a, b) = \sum_{i=1}^n\hat\varepsilon_i^{\,2} = \sum_{i=1}^n (y_i -a - b x_i)^2\ $$

By expanding to get a quadratic expression in $a$ and $b$, we can derive values of $a$ and $b$ that minimize the objective function $Q$ (these minimizing values are denoted $\hat{\alpha}$ and $\hat{\beta}$):

\begin{align}
 \hat\alpha & = \bar{y} - \hat\beta\,\bar{x}, \\
  \hat\beta &= \frac{ \sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y}) }{ \sum_{i=1}^n (x_i - \bar{x})^2 } = \frac{ \operatorname{Cov}(x, y) }{ \operatorname{Var}(x) } = r_{xy} \frac{s_y}{s_x}. \\[6pt]
\end{align}

- $r_{xy}$ as the sample correlation coefficient between x and y
- $s_x$ and $s_y$ as the uncorrected sample standard deviations of x and y

> Kenney, J. F. and Keeping, E. S. (1962) "Linear Regression and Correlation." Ch. 15 in ''Mathematics of Statistics'', Pt. 1, 3rd ed. Princeton, NJ: Van Nostrand, pp. 252–285

Substituting the above expressions for $\hat{\alpha}$ and $\hat{\beta}$ into

$$f = \hat{\alpha} + \hat{\beta} x,$$

yields

$$\frac{ f - \bar{y}}{s_y} = r_{xy} \frac{ x - \bar{x}}{s_x}  .$$

> Kenney, J. F. and Keeping, E. S. (1962) "Linear Regression and Correlation." Ch. 15 in ''Mathematics of Statistics'', Pt. 1, 3rd ed. Princeton, NJ: Van Nostrand, pp. 252–285

```python
num_friends_good = [49,41,40,25,21,21,19,19,18,18,16,15,15,15,15,14,14,13,13,13,13,12,12,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
daily_minutes_good = [68.77,51.25,52.08,38.36,44.54,57.13,51.4,41.42,31.22,34.76,54.01,38.79,47.59,49.1,27.66,41.03,36.73,48.65,28.12,46.62,35.57,32.98,35,26.07,23.77,39.73,40.57,31.65,31.21,36.32,20.45,21.93,26.02,27.34,23.49,46.94,30.5,33.8,24.23,21.4,27.94,32.24,40.57,25.07,19.42,22.39,18.42,46.96,23.72,26.41,26.97,36.76,40.32,35.02,29.47,30.2,31,38.11,38.18,36.31,21.03,30.86,36.07,28.66,29.08,37.28,15.28,24.17,22.31,30.17,25.53,19.85,35.37,44.6,17.23,13.47,26.33,35.02,32.09,24.81,19.33,28.77,24.26,31.98,25.73,24.86,16.28,34.51,15.23,39.72,40.8,26.06,35.76,34.76,16.13,44.04,18.03,19.65,32.62,35.59,39.43,14.18,35.24,40.13,41.82,35.45,36.07,43.67,24.61,20.9,21.9,18.79,27.61,27.21,26.61,29.77,20.59,27.53,13.82,33.2,25,33.1,36.65,18.63,14.87,22.2,36.81,25.53,24.62,26.25,18.21,28.08,19.42,29.79,32.8,35.99,28.32,27.79,35.88,29.06,36.28,14.1,36.63,37.49,26.9,18.58,38.48,24.48,18.95,33.55,14.24,29.04,32.51,25.63,22.22,19,32.73,15.16,13.9,27.2,32.01,29.27,33,13.74,20.42,27.32,18.23,35.35,28.48,9.08,24.62,20.12,35.26,19.92,31.02,16.49,12.16,30.7,31.22,34.65,13.13,27.51,33.2,31.57,14.1,33.42,17.44,10.12,24.42,9.82,23.39,30.93,15.03,21.67,31.09,33.29,22.61,26.89,23.48,8.38,27.81,32.35,23.84]
alpha, beta = 22.9475, 0.90386

```

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

plt.scatter(num_friends_good, daily_minutes_good)
plt.plot(num_friends_good, [alpha + beta*i for i in num_friends_good], 'b-')
plt.xlabel('# of friends', fontsize = 20)
plt.ylabel('minutes per day', fontsize = 20)
plt.title('simple linear regression model', fontsize = 20)
plt.show()
```

Of course, we need a better way to figure out how well we’ve fit the data than staring at the graph.

A common measure is the coefficient of determination (or R-squared), which measures the fraction of the total variation in the dependent variable that is captured by the model.

# Multiple Regression using Matrix Method

Machine Learning in Action

https://github.com/computational-class/machinelearninginaction/

$$ y_i = X_i^T w$$

The constant could be represent by 1 in X

The squared error could be written as:

$$ \sum_{i = 1}^m (y_i -X_i^T w)^2 $$

We can also write this in matrix notation as $(y-Xw)^T(y-Xw)$.

If we take the derivative of this with respect to w, we’ll get $X^T(y-Xw)$.

We can set this to zero and solve for w to get the following equation:

$$\hat w = (X^T X)^{-1}X^T y$$

```python
# https://github.com/computational-class/machinelearninginaction/blob/master/Ch08/regression.py
import pandas as pd
import random

dat = pd.read_csv('../data/ex0.txt', sep = '\t', names = ['x1', 'x2', 'y'])
dat['x3'] = [yi*.3 + .5*random.random() for yi in dat['y']]
dat.head()
```

      x1
      x2
      y
      x3

      0
      1.0
      0.067732
      3.176513
      1.384291

      1
      1.0
      0.427810
      3.816464
      1.354246

      2
      1.0
      0.995731
      4.550095
      1.801504

      3
      1.0
      0.738336
      4.256571
      1.374716

      4
      1.0
      0.981083
      4.560815
      1.637240

```python
from numpy import mat, linalg, corrcoef

def standRegres(xArr,yArr):
    xMat = mat(xArr); yMat = mat(yArr).T
    xTx = xMat.T*xMat
    if linalg.det(xTx) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = xTx.I * (xMat.T*yMat)
    return ws
```

```python
xs = [[dat.x1[i], dat.x2[i], dat.x3[i]] for i in dat.index]
y = dat.y
print(xs[:2])
ws = standRegres(xs, y)
print(ws)
```

    [[1.0, 0.067732, 1.3842912829135907], [1.0, 0.42781, 1.3542457458385966]]
    [[2.88936235]
     [1.63851705]
     [0.10238664]]

```python
xMat=mat(xs)
yMat=mat(y)
yHat = xMat*ws

xCopy=xMat.copy()
xCopy.sort(0)
yHat=xCopy*ws
```

```python
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xMat[:,1].flatten().A[0], yMat.T[:,0].flatten().A[0])
ax.plot(xCopy[:,1],yHat, 'r-')
plt.ylim(0, 5)
plt.show()
```

```python
yHat = xMat*ws
corrcoef(yHat.T, yMat)
```

    array([[ 1.        ,  0.98666465],
           [ 0.98666465,  1.        ]])

# Doing Statistics with statsmodels

http://www.statsmodels.org/stable/index.html

statsmodels is a Python module that provides classes and functions for the estimation of many different statistical models, as well as for conducting statistical tests, and statistical data exploration.

```python
import statsmodels.api as sm
import statsmodels.formula.api as smf
```

```python
dat = pd.read_csv('ex0.txt', sep = '\t', names = ['x1', 'x2', 'y'])
dat['x3'] = [yi*.3 - .1*random.random() for yi in y]
dat.head()
```

      x1
      x2
      y
      x3

      0
      1.0
      0.067732
      3.176513
      0.885553

      1
      1.0
      0.427810
      3.816464
      1.100010

      2
      1.0
      0.995731
      4.550095
      1.323654

      3
      1.0
      0.738336
      4.256571
      1.267457

      4
      1.0
      0.981083
      4.560815
      1.300163

```python
results = smf.ols('y ~ x2 + x3', data=dat).fit()

results.summary()
```

OLS Regression Results

  Dep. Variable:	            y	          R-squared:         	    0.986

  Model:	                   OLS	         Adj. R-squared:    	    0.986

  Method:	             Least Squares	    F-statistic:       	    7167.

  Date:	             Wed, 14 Nov 2018	   Prob (F-statistic):	 1.04e-184

  Time:	                 13:42:03	       Log-Likelihood:    	   284.06

  No. Observations:	         200	        AIC:               	   -562.1

  Df Residuals:	             197	        BIC:               	   -552.2

  Df Model:	                   2

  Covariance Type:	      nonrobust

      	         coef	     std err	      t	      P>|t|	  [0.025	    0.975]

  Intercept	     1.7146	     0.093	    18.372	  0.000	     1.531	     1.899

  x2	            0.9264	     0.057	    16.228	  0.000	     0.814	     1.039

  x3	            1.5151	     0.109	    13.909	  0.000	     1.300	     1.730

  Omnibus:	        4.027	   Durbin-Watson:     	    1.852

  Prob(Omnibus):	  0.134	   Jarque-Bera (JB):  	    2.762

  Skew:	          -0.112	   Prob(JB):          	    0.251

  Kurtosis:	       2.469	   Cond. No.          	     58.2

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

```python
fig = plt.figure(figsize=(12,8))
fig = sm.graphics.plot_partregress_grid(results, fig = fig)
plt.show()
```

```python
import numpy as np
X = np.array(num_friends_good)
X = sm.add_constant(X, prepend=False)

mod = sm.OLS(daily_minutes_good, X)
res = mod.fit()
print(res.summary())
```

                                OLS Regression Results
    ==============================================================================
    Dep. Variable:                      y   R-squared:                       0.329
    Model:                            OLS   Adj. R-squared:                  0.326
    Method:                 Least Squares   F-statistic:                     98.60
    Date:                Wed, 14 Nov 2018   Prob (F-statistic):           3.68e-19
    Time:                        13:58:41   Log-Likelihood:                -711.76
    No. Observations:                 203   AIC:                             1428.
    Df Residuals:                     201   BIC:                             1434.
    Df Model:                           1
    Covariance Type:            nonrobust
    ==============================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
    ------------------------------------------------------------------------------
    x1             0.9039      0.091      9.930      0.000       0.724       1.083
    const         22.9476      0.846     27.133      0.000      21.280      24.615
    ==============================================================================
    Omnibus:                       26.873   Durbin-Watson:                   2.027
    Prob(Omnibus):                  0.000   Jarque-Bera (JB):                7.541
    Skew:                           0.004   Prob(JB):                       0.0230
    Kurtosis:                       2.056   Cond. No.                         13.9
    ==============================================================================

    Warnings:
    [1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

```python
fig = plt.figure(figsize=(6,8))
fig = sm.graphics.plot_partregress_grid(res, fig = fig)
plt.show()
```


# **Обратный маятник**

---

Рассматривается проблема стабилизации обратного маятника. Движение маятника происходит в плоскости XoZ. Невесомый стержень закреплён на тележке, которая может перемещаться под действием горизонтально приложенной силы u(так называемый x inverted pendulum).

Пусть m, M - массы грузика и тележки соответственно. l - расстояние от точки крепления до грузика. Кинетическая и потенциальная энергия системы имеют вид
$$K = \frac{1}{2} M \dot x^2 + \frac{1}{2} m (\dot x_{p}^2 + \dot z_{p}^2)$$
$$П = mgz_{p}$$
где $x_{p} = x + l \sin{\theta}$, $z_{p} = x + l \cos{\theta}$, g - ускорение свободного падения

Из уравнений Лагранжа следует система
\begin{equation*}
 \begin{cases}
   (M + m) \ddot x + m l \ddot \theta \cos{\theta} - m l \dot \theta^2 \sin{\theta} = u\\
   \ddot x \cos{\theta} + l \ddot \theta - g sin{\theta} = 0
 \end{cases}
\end{equation*}
Приводя к нормальной форме Коши, получим
\begin{equation}
 \begin{cases}
   \dot x_{0} = x_{1}\\
   \dot x_{1} = \frac{- m g \cos{x_{2}} \sin{x_{2}} + m l x_{3}^2 \sin{x_{2}} + u}{M + m \sin^2{x_{2}}} + d_{1}\\
   \dot x_{2} = x_{3}\\
   \dot x_{3} = \frac{m l x_{3}^2 \cos{x_{2}} \sin{x_{2}} - u \cos{x_{2}} + (M + m) g \sin{x_{2}} }{M l + m l \sin^2{x_{2}}} + d_{2}
 \end{cases}
\end{equation}
где $d_{1}$ и $d_{2}$ - сторонние возмущения, которые могут действовать на систему

# Стабилизация угла

Для выполения задачи достаточно одного ПИД-регулятора.

```python
from math import *
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
```

Параметры маятника

```python
class Parameters:

    def __init__(self):
        self.m = 0.1
        self.M = 1.
        self.l = 0.3
        self.g = 9.8
```

Right hand system - правая часть системы дифференциальных уравнений

```python
def rhs(t, x, p, u):
    dxdt = np.zeros(4)
    dxdt[0] = x[1]
    dxdt[1] = (-p.m * p.g * cos(x[2]) * sin(x[2]) + p.m * p.l * sin(x[2]) * x[3] ** 2 + u) / (
            p.M + p.m * sin(x[2]) ** 2)
    dxdt[2] = x[3]
    dxdt[3] = (-p.m * p.l * cos(x[2]) * sin(x[2]) * x[3] ** 2 - cos(x[2]) * u + (p.M + p.m) * p.g * sin(x[2])) / (
            p.M * p.l + p.m * p.l * sin(x[2]) ** 2)
    return dxdt
```

Управляющее устройство(ПИД - регулятор)

```python
class PID_Controller():
    def __init__(self, dt, kp, ki, kd):
        self.kp = kp
        self.ki = ki
        self.kd = kd
        self.integral = 0
        self.dt = dt
        self.e_prev = None

    def update(self, state, reference):
        error = state - reference
        if self.e_prev is None:
            dedt = 0
        else:
            # Производная ошибки считается простейшей формулой первого порядка аппроксимации
            dedt = (error - self.e_prev) / self.dt
        # Интеграл считается простейшей формулой прямоугольников
        self.integral = self.integral + error * self.dt

        self.e_prev = error
        return self.kp * error + self.ki * self.integral + self.kd * dedt
```

Требуемое значение угла, в котором должен удерживаться маятник

```python
def reference_angle(t):
    return 0
```

Непосредственно решатель системы уравнений

```python
def solve(t0, tf, dt, x0, p, pid):
    time = np.arange(t0, tf, dt)
    result = x0
    force = []
    for i in range(len(time) - 1):
        u = pid.update(x0[2], reference_angle(time[i]))
        force = np.append(force, u)
        solution = solve_ivp(lambda t, x: rhs(t, x, p, u), (time[i], time[i + 1]), x0)
        x0 = solution.y[:, -1]
        result = np.vstack((result, x0))
    return time, force, result
```

Параметры регулятора и интегратора, графики решений.

Если в начальный момент времени маятник отклонён на 0.5 радиан, то он быстро приходит в верхнее положение и остается там при $k_{p} = 25$, $k_{i} = 15$, $k_{d} = 3$

```python
t_0 = 0
t_f = 20
d_t = 0.01
init = [0., 0., 0.5, 0.]
param = Parameters()
pid = PID_Controller(d_t, 25, 15, 3)
[t, f, x] = solve(t_0, t_f, d_t, init, param, pid)
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(1, 2, 1)
ax.plot(t, x[:, 2], label = 'Result')
ax.plot(t,np.zeros_like(x[:, 2]),'--',color ='r', label = 'Reference')
ax.grid()
ax.legend()
ax.set_xlabel('Time, sec')
ax.set_ylabel('Angle, rad')
ax = fig.add_subplot(1, 2, 2)
ax.plot(t[0:-1], f)
ax.set_xlabel('Time, sec')
ax.set_ylabel('Force, N')
ax.grid()
plt.show()
```

#Стабилизация угла и положения тележки

Добвим второй ПИД-регулятор, который позволит контролировать положение тележки, то есть точки крепления стержня.

Пусть теперь тележка стоит на коротком столе, а потому её движение ограничено:  $-0.5 \leq x \geq 0.5$. Учтём это простейшим образом, не допуская длительного воздействия, выводящего систему за данные рамки

```python
def reference_position(t):
    return 0
def rhs(t, x, p, u):
    dxdt = np.zeros(4)
    dxdt[0] = x[1]
    dxdt[1] = (-p.m * p.g * cos(x[2]) * sin(x[2]) + p.m * p.l * sin(x[2]) * x[3] ** 2 + u) / (
            p.M + p.m * sin(x[2]) ** 2)
    dxdt[2] = x[3]
    dxdt[3] = (-p.m * p.l * cos(x[2]) * sin(x[2]) * x[3] ** 2 - cos(x[2]) * u + (p.M + p.m) * p.g * sin(x[2])) / (
            p.M * p.l + p.m * p.l * sin(x[2]) ** 2)
    return dxdt
def solve(t0, tf, dt, x0, p, pid1, pid2):
    time = np.arange(t0, tf, dt)
    result = x0
    force = []
    for i in range(len(time) - 1):
        u = pid1.update(x0[2], reference_angle(time[i])) - pid2.update(x0[0], reference_position(time[i]))
        force = np.append(force, u)
        solution = solve_ivp(lambda t, x: rhs(t, x, p, u), (time[i], time[i + 1]), x0)
        x0 = solution.y[:, -1]
        if abs(x0[0]) > 0.5:
            x0[0] = 0.5 * np.sign(x0[0])
            i = i - 1
        result = np.vstack((result, x0))
    return time, force, result
```

Если в нулевой момент времени маятник отклонён на 0.5 радиан, а тележка находится в начале координат и неподвижна, то при тех же параметрах регулятора угла и при параметрах $k_{p} = -2.4$, $k_{i} = -1$, $k_{d} = -0.75$ регулятора положения тележки система быстро приходит в требуемое состояние $x = 0 $, $\theta = 0$

```python
t_0 = 0
t_f = 20
d_t = 0.01
init = [0., 0., 0.5, 0.]
param = Parameters()
pid1 = PID_Controller(d_t, 25, 15, 3)
pid2 = PID_Controller(d_t, -2.4, -1, -0.75)
[t, f, x] = solve(t_0, t_f, d_t, init, param, pid1, pid2)
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(1, 3, 1)
ax.plot(t, x[:, 2], label = 'Result')
ax.plot(t,np.zeros_like(x[:, 2]),'--',color ='r', label = 'Reference')
ax.grid()
ax.legend()
ax.set_xlabel('Time, sec')
ax.set_ylabel('Angle, rad')
ax = fig.add_subplot(1, 3, 2)
ax.plot(t, x[:, 0], label = 'Result')
ax.plot(t,np.zeros_like(x[:, 0]),'--',color ='r', label = 'Reference')
ax.set_xlabel('Time, sec')
ax.set_ylabel('x position, N')
ax.grid()
ax.legend()
ax = fig.add_subplot(1, 3, 3)
ax.plot(t[0:-1], f)
ax.set_xlabel('Time, sec')
ax.set_ylabel('Force, N')
ax.grid()
plt.show()
```

# Выход на заданную тректорию

С помощью двух воздействий можно также заставлять маятник двигаться по желаемой траектории. Пусть мы хотим добиться периодического движения вида $x = 0.3 \sin{0.05 \pi t}$, а на систему дополнительно влияют сильные сторонние возмущения $d_{1} = d_{2}= 20 \sin{20 \pi t}$. Не изменяя параметры регуляторов, можно быстро получить необходимое движение.

```python
def reference_position(t):
    return 0.3 * sin(0.05 * pi * t)
def rhs(t, x, p, u):
    dxdt = np.zeros(4)
    dxdt[0] = x[1]
    dxdt[1] = (-p.m * p.g * cos(x[2]) * sin(x[2]) + p.m * p.l * sin(x[2]) * x[3] ** 2 + u) / (
            p.M + p.m * sin(x[2]) ** 2) + 20 * sin(20 * pi * t)
    dxdt[2] = x[3]
    dxdt[3] = (-p.m * p.l * cos(x[2]) * sin(x[2]) * x[3] ** 2 - cos(x[2]) * u + (p.M + p.m) * p.g * sin(x[2])) / (
            p.M * p.l + p.m * p.l * sin(x[2]) ** 2) + 20 * sin(20 * pi * t)
    return dxdt
```

```python
t_0 = 0
t_f = 100
d_t = 0.01
init = [0., 0., 0.5, 0.]
param = Parameters()
pid1 = PID_Controller(d_t, 25, 15, 3)
pid2 = PID_Controller(d_t, -2.4, -1, -0.75)
[t, f, x] = solve(t_0, t_f, d_t, init, param, pid1, pid2)
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(1, 3, 1)
ax.plot(t, x[:, 2], label = 'Result')
ax.plot(t,np.zeros_like(x[:, 2]),'--',color ='r', label = 'Reference')
ax.grid()
ax.legend()
ax.set_xlabel('Time, sec')
ax.set_ylabel('Angle, rad')
ax = fig.add_subplot(1, 3, 2)
ax.plot(t, x[:, 0], label = 'Result')
ax.plot(t, [reference_position(t[i]) for i in range(len(t))], color = 'r', label ='Reference')
ax.legend()
ax.set_xlabel('Time, sec')
ax.set_ylabel('x position, N')
ax.grid()
ax = fig.add_subplot(1, 3, 3)
ax.plot(t[0:-1], f)
ax.set_xlabel('Time, sec')
ax.set_ylabel('Force, N')
ax.grid()
plt.show()
```

# Выводы

Таким образом, грамотная конструкция системы управления с использованием обыкновенных ПИД-регуляторов способна контролировать поведение обратного маятника: не только удерживать систему в заданном положении, но и устанавливать предложенную траекторию. Однако, главная проблема заключается в тонкости подбора параметров регуляторов, который фактически происходит методом проб и ошибок

Homework Problems/Lecture 4-5 PID/PID for Inverted Pendulum.ipynb


```python
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import sympy
import numpy as np
import warnings
warnings.filterwarnings('ignore')
plt.style.use("seaborn-muted")
solve = lambda x,y: sympy.solve(x-y)[0] if len(sympy.solve(x-y))==1 else "Not Single Solution"
```

# Market Equilibrium and Government Intervention

## Market Equilibrium

Over the past two weeks, we have individually introduced supply and demand, represented as functions of quantity supplied or demanded, respectively. Now let's bring them together.

The intersection of both curves is called market equilibrium: the condition when quantity supplied equals quantity demanded at a given price. This is the "market-clearing" condition: $Q_s = Q_d$

This means that for any given equation of S and D, we can find both the equilibrium quantity and price. As demand and supply functions solve for price, we simply equate the two functions together. To demonstrate this, we will use SymPy.

For simplicity's sake, we have defined `solve(x, y)` that returns the value of the input variable that results in equivalent values for the equations `x` and `y` that were passed in.

```python
Q = sympy.Symbol('Q')
P_demand = -0.04 * Q + 20
P_supply = 0.02 * Q + 14

Q_star = solve(P_demand, P_supply)
Q_star
```

    100.000000000000

`Q_star`, the quantity of goods produced and sold at equilibrium, is equal to 100. To get the equilibrium price, we can substitute `!_star` into either `P_demand` or `P_supply`:

```python
P_star = P_demand.subs(Q, Q_star)
P_star
```

    16.0000000000000

This gives us that `P_star` is equal to 16.

### Movements away from equilibrium

What happens to market equilibrium when either supply or demand shifts due to an exogenous shock?

Let's assume that consumers now prefer Green Tea as their hot beverage of choice moreso than before. We have an outward shift of the demand curve - quantity demanded is greater at every price. The market is no longer in equilibrium.

```python
# [INSERT GRAPH: DEMAND SHIFT RIGHT, SHORTAGE AT ORIGINAL EQ PRICE]
```

At the same price level (the former equilibrium price), there is a shortage of Green Tea. The amount demanded by consumers exceeds that supplied by producers: $Q_d > Q_s$. As a result, buyers would bid up the price of Green Tea. At the same time, producers are incentivized to produce more, causing $Q_s$ to increase along the supply curve. This market-clearing force (sometimes referred to as the "invisible hand") pushes $Q_s$ and $Q_d$ to converge to a new equilibrium level of quantity and price.

## Theoretical Government intervention

Now that we have discussed cases of market equilibrium with just demand and supply, also known as free market cases, we will examine what happens when the government intervenes. In all of these cases, the market is pushed from equilibrium to a state of disequilibrium. This causes the price to change and, as a result, the number of participants in the market.

### Taxation and its effects on supply and demand

The primary method that governments use to intervene in markets is taxation. This occurs through percentage or fixed taxes that increase the net price that consumers eventually have to pay for a good or service. Taxes can be levied on either the producer or the consumer.

```python
# [INSERT GRAPH: SUPPLY CURVE SHIFT LEFT]
```

If it is levied on producers, this decreases the quantity of goods they can supply at each price as the tax is effectively acting as an additional cost of production. This shifts the supply curve leftward.

```python
# [INSERT GRAPH: DEMAND CURVE SHIFT LEFT]
```

If the tax is levied on consumers, this increases the price per unit they must pay, thereby reducing quantity demanded at every price. This shifts the demand curve leftward.

```python
# [INSERT GRAPH: DWL TRIANGLE + TAX WEDGE]
```

The resulting equilibrium - both price and quantity - is the same in both cases. However, the prices paid by producers and consumers are different. Let us denote the equilibrium quantity to be $Q^*$. The price that producers pay $P_p$ occurs where $Q^*$ intersects with the supply curve. At the same time, the price that consumers pay $P_c$ occurs where $Q^*$ intersects  the demand curve.

You will notice that the vertical distance between $P_p$ and $P_c$ will be the value of the tax. That is to say, $P_c = P_p + \text{tax}$. We call the vertical distance between $P_p$ and $P_c$ at quantity $Q^*$ the tax wedge.

### Incidence

Determining who bears the greater burden, or incidence, of the tax depends on the elasticity of the good. An inelastic good would mean that it is more of a neccesity than an elastic one. This allows the producer to push more of the tax onto consumers, who are willing to purchase as much of the good even as prices increase. The incidence or burden of paying the tax will thus fall on consumers moreso than producers.

With elastic goods, incidences are distributed in the opposite way. As elastic goods are less of a necessity, consumers will substitute away from them when prices increase. Thus, when the tax is levied on an elastic good and its price increases, consumers will be more willing to switch to other goods, thereby leaving the producer to bear more of the tax-paying burden.

One can calculate the burden share, or the proportion of the tax paid by consumers or producers:

Consumer's burden share: $\dfrac{\text{Increase in unit price after the tax paid by consumers} + \text{Increase in price paid per unit by consumers to producers}}{\text{Tax per unit}}$

Producer's burden share: $\dfrac{\text{Increase in unit price after the tax paid by producers} - \text{Increase in price paid per unit by consumers to producers}}{\text{Tax per unit}}$

Graphically, the total tax burden is the rectangle formed by the tax wedge and the horizontal distance between 0 and $Q^*$: $Q^* \cdot \text{tax}$ This is also how you calculate the revenue from the tax earned by the government.

### Deadweight Loss

Naturally, the introduction of the tax disrupts the economy and pushes it away from equilibrium. For consumers, the higher price they must pay essentially "prices" out some individuals - they are now unwilling to pay more for the good. This leads them to leave the market that they previously participated in. At the same time, for producers, the introduction of the tax increases production costs and cuts into their revenues. Some of the businesses that were willing to produce at moderately high costs now find themselves unable to make a profit with the introduction of the tax. They too leave the market. There are market actors who are no longer able to purchase or sell the good.

We call this loss of transactions: deadweight welfare loss. It is represented by the triangle with a vertex at the original market equilibrium and a base at the tax wedge. The area of the deadweight loss triangle, also known as Harberger's triangle, is the size of the welfare loss - the total value of transactions lost as a result of the tax.

Another way to think about deadweight loss is the change (decrease) in total surplus. Consumer and producer surplus decrease significantly, but this is slightly offset by the revenue earned by the government from the tax.

We can calculate the size of Harberger's triangle using the following formula: $\dfrac{1}{2} \cdot \dfrac{\epsilon_s \cdot \epsilon_d}{\epsilon_s - \epsilon_d} \cdot \dfrac{Q^*}{P_p} (\text{tax})^2$ where $\epsilon_s$ is the price elasticity of supply and  $\epsilon_d$ is the price elasticity of demand.

### Salience

We noted in our discussion about taxes that the equilibrium quantity and price is the same regardless of whether the tax is levied on producers or consumers. This is the traditional theory's assumption: that individuals, whether they be producers or consumers, are fully aware of the taxes they pay. They decide how much to produce or consume with this in mind.

We call the visibility at which taxes are displayed their salience. As an example, the final price of a food item in a restaurant is not inclusive of sales tax. Traditional economic theory would say that this difference between advertized or poster price and the actual price paid by a consumer has no bearing on the quantity they demand. That is to say taxes are fully salient. However, recent research has suggested that this is not the case.

A number of recent studies, including by Chetty, Looney and Kroft in 2009, found that posting prices that included sales tax actually reduces demand for those goods. Individuals are not fully informed or rational, implying that tax salience does matter.

### Subsidy

Another form of government intervention is a subsidy. It involves either a monetary benefit given by the government or a reduction in taxes granted to individual businesses or whole industries. They intend to lower production costs, and thus increase the quantity supplied of goods and services at equilibrium.

```python
# [INSERT GRAPH: SUPPLY CURVE SHIFT RIGHT]
```

We represent this visually as a rightward shift in the supply curve. As costs are lower, producers are now willing to supply more goods and services at every price. The demand curve remains unchanged as a subsidy goes directly to producers. The resulting equilibrium has a lower price $P^*$ and higher quantity $Q^*$. It is assumed that the lower production costs would be passed onto consumers through lower market prices. $P^*$ is what consumers pay, but producers receive $P_P = P^* + \text{subsidy}$. This is depicted visually by the price along the new supply curve at quantity $Q^*$.

Consumer surplus increases as more individuals are able to purchase the good than before. Similarly, producer surplus has increased as the subsidy takes care of part (if not all) of their costs. Overall market surplus has increased.

The welfare gain is depicted in a similar way to that of a tax: a triangle with a vertex at the original market equilibrium and a base along $Q^*$. The cost of the subsidy to the government is $\text{per-unit subsidy} \cdot Q^*$.

### Price controls

The last type of government intervention is far more forceful than taxes and subsidies. Instead of changing the per-unit cost of a good paid by consumers or producers, price controls directly fix the market price. It does not change the amount different consumers are willing and able to pay or how much producers are willing and able to produce.

When price controls are introduced to a market, the equilibrium price and quantity no longer exist. Consumer and producers are forced to sell or buy goods at the prescribed price.

```python
# [INSERT GRAPH: PRICE FLOOR]
```

Let's take a price floor as the first example. This is when the government imposes a minimum price for which a good can be sold. For this to be effective, the price floor must be set above the equilibrium price, otherwise the market would just settle at equilibrium. This results in a surplus, as producers are willing to sell far more goods than consumers are willing to purchase. This results in a surplus as $Q_s > Q_d$. Normally, without government intervention, the invisible hand would push prices down until $Q_s = Q_d$. However, with the price floor, the market remains in a state of disequilibrium.

Minimum wages act as a price floor, keeping wages above a certain level. At this higher rate, more workers are willing to work than there is demand for, creating unemployment. **For extra commentary, should I talk about Card & Auerbach's study, or would that be extraneous?**

# Umar: bolded above

```python
# [INSERT GRAPH: PRICE CEILING]
```

The other control is a price ceiling. This is when the government imposes a maximum price for which a good can be sold. This results in a shortage as consumers are to purchase more goods than producer are willing to sell: $Q_d > Q_s$.

Rent control in Berkeley acts as a price ceiling, keeping rents from rising above a certain level.

## Optional: World Trade vs. Autarky

Throughout the class so far, we have assumed that the economy is operating by itself, in isolation. Economists use the word "autarky" to represent the scenario when a country is economically self-sufficient. The key consequence of this is that the country's economy is unaffected by global events, allowing us to conclude that any changes to equilibrium price or quantity are purely a result of shifts in domestic demand or supply.

We will now assume that the country is no longer in autarky, and is thus subject to world prices, demand and supply.

```python
# [INSERT GRAPH: AUTARKY]
```

We now label our demand and supply curves as domestic demand and supply, respectively. Their intersection represents where the market would be if we were in autarky. However, as we are now open to world trade, the price where the market operates is now determined by the world price, which may be totally different from the equilibrium price.

Where the world price line intersects with domestic demand represents the total quantity demanded within the market. Where it intersects with the domestic supply curve is the total quantity supplied.

Let's first imagine a scenario where world price is lower than domestic price. From our graph, we find that $Q_s < Q_d$. This is a shortage. Domestic producers are not willing to fully provide all goods demanded by consumers in the economy. The difference between what is produced domestically and the total quantity demanded will be fulfilled by international producers. Thus, $Q_d - Q_s =$ imports. As prices are lower than what some consumers were willing to pay, there is a significant consumer surplus. Unfortunately for producers, as world prices are lower than what they would have been able to charge, they lose producer surplus. There is a net gain in the change in consumer surplus is greater than the loss in producer surplus. This is represented by the triangle between the curves and above the world price line.

Now, let's imagine that world price is greater than domestic price. In this case, $Q_s > Q_d$. We now have a surplus where the quantity demanded by consumers is far less than what domestic producers are willing to provide at this higher price. Thus, domestic producers will willingly sell goods to domestic customers and sell the rest on the global market. $Q_s - Q_d =$ exports. As prices are higher than what some consumers were willing to pay, there is a significant decrease in consumer surplus. Producers however rejoice as world prices are higher than what they would have been able to charge. They gain producer surplus. There is a net gain in total surplus as the increase in producer surplus is greater than the loss in consumer surplus. This is represented by the triangle between the curves and above the world price line.


# "Epidemic dynamics: The SIR model"
> "The Susceptible-Infected-Recovered model with python"

- toc: true
- badges: true
- comments: true
- categories: [jupyter, Covid19]

## Modeling

The Susceptible-Infected-Recovered (SIR) model is useful for understanding the spread of infections and epidemics and pretty easy to explain and understand (Wikipedia explanation [here](https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology#The_SIR_model) isn't bad.

Guided by this didactic ['Coronavirus Curve - Numberphile](https://www.youtube.com/watch?v=k6nLfCbAzgo) video I wrote simple python code (using the python Scipy ordinary differential equatio solver) to run and visualize some simulations. I've also set it up to run it as an interactive [web app](https://ricardian.herokuapp.com/).

## The Model
The proportion of the population are infected $I_t$, susceptible $S$, and recovered $R$ evolve over time according to these equation swhich depend on the transmission rate $\beta$ and the recovery rate $\gamma$:

$$
\begin{align}
\frac{dI}{dt} &= \beta \cdot S \cdot I - \gamma \cdot I  \\
\frac{dS}{dt} &=-\beta \cdot S \cdot I  \\
\frac{dR}{dt} &= \gamma \cdot I
\end{align}
$$

```python
#hide
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from ipywidgets import interact
```

We'll need some initial conditions and sample parameters:

```python
#collapse-show
N = 1          # Size of the population (so everything in proportions)
I0 = 0.01      # Initial proportion of the population infected
S0 = N - I0    # Initial proportion of the population susceptible
R0 = 0.0       # Initial proportion of the population recovered
maxT = 25      # max number of periods in simulation

beta = 0.5      # transmission rate
gamma = 0.1     # recovery rate
```

A `SIR()` function that for any time $t$ and current value of stock variables $y=(S,I,R)$ updates $\frac{dS}{dt}, \frac{dI}{dt}$ and $\frac{dR}{dt}$

```python
#collapse-hide
def SIR(y, t, beta, gamma):
    '''the SIR model'''
    S, I, R = y
    dSdt = -beta*S*I
    dIdt = beta*S*I - gamma*I
    dRdt = gamma*I
    return([dSdt, dIdt, dRdt])
```

and some code to solve and plot this system of ordinary differential equations for any given value of $\beta$ and $\gamma$

```python
#collapse-hide
def plotSIR(beta = beta, gamma = gamma, maxT = maxT):
    '''Solve differential equations in SIR and plot'''
    t = np.linspace(0, maxT, 1000)
    soln = odeint(SIR,[S0,I0,R0], t, args=(beta, gamma))
    soln = np.array(soln)
    plt.figure(figsize=[8,6])

    plt.plot(t, soln[:,0], linewidth=3, label = 'S(t)')
    plt.plot(t, soln[:,1], linewidth=3, label = 'I(t)')
    plt.plot(t, soln[:,2], linewidth=3, label = 'R(t)')
    plt.grid()
    plt.legend()
    plt.xlabel("Time")
    plt.ylabel("proportions")
    plt.title("SIR model")
    plt.show()
```

Below is a plot with the default parameters ($\beta=0.5$, $\gamma=0.1$) to have a graphic in case widget above does not display.

```python
plotSIR(beta, gamma, maxT)
```

Now if you have a python kernel running in the background (e.g. you opened this with binder or google colab) you can change the model parameters with sliders and see how that changes the predicted course of the infection over time. You can also do this with the interactive [web app](https://ricardian.herokuapp.com/) version of this notebook.

```python
#collapse-hide
interact(plotSIR, beta=(0,1,0.05), gamma=(0,1,0.05), maxT=(5,100,5));
```

    interactive(children=(FloatSlider(value=0.5, description='beta', max=1.0, step=0.05), FloatSlider(value=0.1, d…