(glue)=

# Inserting variables into pages with `glue`

You often wish to run analyses in one notebook and insert them into your
documents text elsewhere. For example, if you'd like to include a figure,
or if you want to cite a statistic that you have run.

The **`glue` submodule** allows you to add a key to variables in a notebook,
then display those variables in your book by referencing the key.

This page describes how to add keys to variables in notebooks, and how to insert them
into your book's content in a variety of ways.

(glue/gluing)=

## Gluing variables in your notebook

You can use `myst_nb.glue()` to assign value of a variable to
a key of your choice. `glue` will store all of the information that is normally used to **display**
that variable (ie, whatever happens when you display the variable by putting it at the end of a cell).
Choose a key that you will remember, as you will use it later.

The following code glues a variable inside the notebook:

```python
from myst_nb import glue
a = "my variable!"
glue("my_variable", a)
```

    'my variable!'

You can then insert it into your text like so: {glu:}`my_variable`.

That was accomplished with the following code: `` {glu:}`my_variable` ``.

### Gluing numbers, plots, and tables

You can glue anything in your notebook and display it later with `{glu:}`. Here
we'll show how to glue and paste **numbers and images**. We'll simulate some
data and run a simple bootstrap on it. We'll hide most of this process below,
to focus on the glueing part.

```python
# Simulate some data and bootstrap the mean of the data
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

n_points = 10000
n_boots = 1000
mean, sd = (3, .2)
data = sd*np.random.randn(n_points) + mean
bootstrap_indices = np.random.randint(0, n_points, n_points*n_boots).reshape((n_boots, n_points))
```

In the cell below, `data` contains our data, and `bootstrap_indices` is a collection of sample indices in each bootstrap. Below we'll calculate a few statistics of interest, and
**`glue()`** them into the notebook.

```python
# Calculate the mean of a bunch of random samples
means = data[bootstrap_indices].mean(0)
# Calcualte the 95% confidence interval for the mean
clo, chi = np.percentile(means, [2.5, 97.5])

# Store the values in our notebook
glue("boot_mean", means.mean())
glue("boot_clo", clo)
glue("boot_chi", chi)
```

    3.00261156894278

    2.990468243815575

    3.0150764461656254

By default, `glue` will display the value of the variable you are gluing. This
is useful for sanity-checking its value at glue-time. If you'd like to **prevent display**,
use the `display=False` option. Note that below, we also *overwrite* the value of
`boot_chi` (but using the same value):

```python
glue("boot_chi_notdisplayed", chi, display=False)
```

You can also glue visualizations, such as matplotlib figures (here we use `display=False` to ensure that the figure isn't plotted twice):

```python
# Visualize the historgram with the intervals
fig, ax = plt.subplots()
ax.hist(means)
for ln in [clo, chi]:
    ax.axvline(ln, ls='--', c='r')
ax.set_title("Bootstrap distribution and 95% CI")

# And a wider figure to show a timeseries
fig2, ax = plt.subplots(figsize=(6, 2))
ax.plot(np.sort(means), lw=3, c='r')
ax.set_axis_off()

glue("boot_fig", fig, display=False)
glue("sorted_means_fig", fig2, display=False)
```

The same can be done for DataFrames (or other table-like objects) as well.

```python
bootstrap_subsets = data[bootstrap_indices][:3, :5].T
df = pd.DataFrame(bootstrap_subsets, columns=["first", "second", "third"])
glue("df_tbl", df)
```

      first
      second
      third

      0
      3.300893
      3.103483
      2.924411

      1
      2.842613
      3.025658
      3.004245

      2
      3.158234
      3.209659
      2.975594

      3
      2.835368
      2.969986
      3.062939

      4
      3.120003
      3.011689
      2.992528

```{tip}
Since we are going to paste this figure into our document at a later point,
you may wish to remove the output here, using the `remove_output` tag
(see {ref}`use/removing`).
```

(glue/pasting)=

## Pasting glued variables into your page

Once you have glued variables into a notebook, you can then **paste**
those variables into your text in your book anywhere you like (even on other pages).
These variables can be pasted using one of the roles or directives in the `glu:` *family*.

### The `glu` role/directive

The simplest role and directive are `glu:any`,
which paste the glued output inline or as a block respectively,
with no additional formatting.
Simply add this:

````
```{glu:} your-key
```
````

For example, we'll paste the plot we generated above with the following text:

````md
```{glu:} boot_fig
```
````

Here's how it looks:

```{glu:} boot_fig
```

Or we can paste inline objects like so:

```md
Inline text; {glu:}`boot_mean`, and figure; {glu:}`boot_fig`.
```

Inline text; {glu:}`boot_mean`, and figure; {glu:}`boot_fig`.

```{tip}
We recommend using wider, shorter figures when plotting in-line, with a ratio
around 6x2. For example, here's is an in-line figure of sorted means
from our bootstrap: {glu:}`sorted_means_fig`.
It can be used to make a visual point that isn't too complex! For more
ideas, check out [how sparklines are used](https://en.wikipedia.org/wiki/Sparkline).
```

Next we'll cover some more specific pasting functionality, which gives you more
control over how the outputs look in your pages.

## Controling the pasted outputs

You can control the pasted outputs by using a sub-command of `{glu:}`. These are called like so:
`` {glu:subcommand}`key` ``. These subcommands allow you to control more of the look, feel, and
content of the pasted output.

```{tip}
When you use `{glu:}` you are actually using a short-hand for `{glu:any}`. This is a
generic command that doesn't make many assumptions about what you are gluing.
```

### The `glu:text` role

The `glu:text` role, is specific to text outputs.
For example, the following text:

```
The mean of the bootstrapped distribution was {glu:text}`boot_mean` (95% confidence interval {glu:text}`boot_clo`/{glu:text}`boot_chi`).
```

Is rendered as:
The mean of the bootstrapped distribution was {glu:text}`boot_mean` (95% confidence interval {glu:text}`boot_clo`/{glu:text}`boot_chi`)

```{note}
`glu:text` only works with glued variables that contain a `text/plain` output.
```

With `glu:text` we can **add formatting to the output**.
This is particularly useful if you are displaying numbers and
want to round the results. To add formatting, use this pattern:

* `` {glu:text}`mykey:formatstring` ``

For example, the following: ``My rounded mean: {glu:text}`boot_mean:.2f` `` will be rendered like this: My rounded mean: {glu:text}`boot_mean:.2f` (95% CI: {glu:text}`boot_clo:.2f`/{glu:text}`boot_chi:.2f`).

### The `glu:figure` directive

With `glu:figure` you can apply more formatting to figure like objects,
such as giving them a caption and referencable label:

````md
```{glu:figure} boot_fig
:figwidth: 300px
:name: "fig-boot"

This is a **caption**, with an embedded `{glu:text}` element: {glu:text}`boot_mean:.2f`!
```
````

```{glu:figure} boot_fig
:figwidth: 300px
:name: "fig-boot"

This is a **caption**, with an embedded `{glu:text}` element: {glu:text}`boot_mean:.2f`!
```

```md
Here is a {ref}`reference to the figure <fig-boot>`
```

Here is a {ref}`reference to the figure <fig-boot>`

Here's a table:

````md
```{glu:figure} df_tbl
:figwidth: 300px
:name: "tbl:df"

A caption for a pandas table.
```

````
```{glu:figure} df_tbl
:figwidth: 300px
:name: "tbl:df"

A caption for a pandas table.
```

### The `glu:math` directive

The `glu:math` directive, is specific to latex math outputs
(glued variables that contain a `text/latex` mimetype),
and works similarly to the [sphinx math directive](https://www.sphinx-doc.org/en/1.8/usage/restructuredtext/directives.html#math).
For example:

```python
import sympy as sym
f = sym.Function('f')
y = sym.Function('y')
n = sym.symbols(r'\alpha')
f = y(n)-2*y(n-1/sym.pi)-5*y(n-2)
glue("sym_eq", sym.rsolve(f,y(n),[1,4]))
```

$\displaystyle \left(\sqrt{5} i\right)^{\alpha} \left(\frac{1}{2} - \frac{2 \sqrt{5} i}{5}\right) + \left(- \sqrt{5} i\right)^{\alpha} \left(\frac{1}{2} + \frac{2 \sqrt{5} i}{5}\right)$

````md
Insert the equation here:

```{glu:math} sym_eq
:label: eq-sym
```

Which we reference as Equation {eq}`eq-sym`
````

Insert the equation here:

```{glu:math} sym_eq
:label: eq-sym
```

Which we reference as Equation {eq}`eq-sym`.

```{note}
`glu:math` only works with glued variables that contain a `text/latex` output.
```

## Advanced glue usecases

Here are a few more specific and advanced uses of the `glue` submodule.

### Pasting from pages you don't include in the documentation

Sometimes you'd like to use variables from notebooks that are not meant to be
shown to users. In this case, you should bundle the notebook with the rest of your
content pages, but include `orphan:` in the metadata of the notebook.

For example, the following text: `` {glu:}`orphaned_var` was created in {ref}`orphaned-nb` ``.
Results in: {glu:}`orphaned_var` was created in {ref}`orphaned-nb`

### Pasting into tables

In addition to pasting blocks of outputs, or in-line with text, you can also paste directly
into tables. This allows you to compose complex collections of structured data using outputs
that were generated in other notebooks. For example the following table:

````md
| name                            |       plot                  | mean                      | ci                                                |
|:-------------------------------:|:---------------------------:|---------------------------|---------------------------------------------------|
| histogram and raw text          | {glu:}`boot_fig`             | {glu:}`boot_mean`          | {glu:}`boot_clo`-{glu:}`boot_chi`                   |
| sorted means and formatted text | {glu:}`sorted_means_fig`     | {glu:text}`boot_mean:.3f` | {glu:text}`boot_clo:.3f`-{glu:text}`boot_chi:.3f` |
````

Results in:

| name                            |       plot                  | mean                      | ci                                                |
|:-------------------------------:|:---------------------------:|---------------------------|---------------------------------------------------|
| histogram and raw text          | {glu:}`boot_fig`             | {glu:}`boot_mean`          | {glu:}`boot_clo`-{glu:}`boot_chi`                   |
| sorted means and formatted text | {glu:}`sorted_means_fig`     | {glu:text}`boot_mean:.3f` | {glu:text}`boot_clo:.3f`-{glu:text}`boot_chi:.3f` |


# Tutorial 4 - Pressure vs Temperature

Surfinpy has the functionality to generate phase diagrams as a function of pressure vs temperature based upon the methodology used in Molinari et al. (J. Phys. Chem. C  116, 12, 7073-7082) according to

\begin{align}
\gamma_{adsorbed, T, P} & = \gamma_{bare} + ( C ( E_{ads, T} - RTln(\frac{p}{p^o})
\end{align}

where $\gamma_{adsorbed, T, p}$ is the surface energy of the surface with adsorbed species at temperature (T) and pressure (P), $\gamma_{bare}$ is the suface energy of the bare surface, C is the coverage of adsorbed species, $E_{ads}$ is the adsorption energy,

\begin{align}
E_{ads, T} & =  E_{slab, adsorbant} - (E_{slab, bare} + n_{H_2O} E_{H_2O, T}) / n_{H_2O}
\end{align}

where $E_{slab, adsorbant}$ is the energy of the surface and the adsorbed species, $n_{H_2O}$ is he number of adsorbed species,

\begin{align}
E_{H_2O, (T)} & = E_{H_2O, (g)} - TS_{(T)}
\end{align}

where $S_{(T)}$ is the experimental entropy of gaseous water in the standard state.

So let's look at this in action. The module used is called p_vs_t

```python
import matplotlib.pyplot as plt
from surfinpy import utils as ut
from surfinpy import p_vs_t
from surfinpy import data
```

There are a number of user inputs that are required, the inputs are similiar to mu_vs_mu but with some small differences. First we need the energy from DFT of the adsorbant (eV), the energy of the surface ($j_m^2$) and the data for each surface. For clarity, in the `surfinpy.data.DataSet` objects for each surface it is always assumed that the adsorbing species is 'Y'.

```python
adsorbant = -14.00
```

```python
SE = 1.40
```

```python
stoich = data.DataSet(cation = 24, x = 48, y = 0, area = 60.22, energy = -575.00, label = 'Bare')
H2O =    data.DataSet(cation = 24, x = 48, y = 2, area = 60.22, energy = -605.00, label = '1 Water')
H2O_2 =  data.DataSet(cation = 24, x = 48, y = 8, area = 60.22, energy = -695.00, label = '2 Water')
```

```python
data = [H2O, H2O_2]
```

The coverage of the adsorbing species (in this case water) is also needed. surfinpy has a built in function within the utils module to do this for you, it takes the number of adsorbing species and the surface area and calculates the coverage for you.

```python
cov = ut.calculate_coverage(data)
```

We need the thermochemical data for the adsorbed species in order to make the DFT energy of our adsorbing species a temperature dependent term. This data has been taken directly from the NIST Janaf database and has been cut to just include the temperature and S values within the temperature range that interests us (In this case 0 - 1000 K).

```python
thermochem = ut.read_nist("H2O.txt")
```

Now we can generate our phase diagram .

```python
system = p_vs_t.calculate(stoich, data, SE, adsorbant, thermochem, coverage=cov)
ax = system.plot(colourmap='Spectral', figsize=(6, 4), ylabel='$log_{10} (P_{H_2O})$')
plt.savefig("../../../docs/source/Figures/Surfaces_7.png", dpi=600)
plt.show()
```

```python

```


```python
from games_setup import *
from SBMLLint.common import constants as cn
from SBMLLint.common.molecule import Molecule, MoleculeStoichiometry
from SBMLLint.common.reaction import Reaction
from SBMLLint.games.som import SOM
from SBMLLint.common.simple_sbml import SimpleSBML

import collections
import itertools
import networkx as nx
import numpy as np
import pandas as pd
from sympy.matrices import Matrix, eye
#
from SBMLLint.common.stoichiometry_matrix import StoichiometryMatrix
from SBMLLint.games.mesgraph import MESGraph
from SBMLLint.games.games_pp import GAMES_PP, SOMStoichiometry, SOMReaction, TOLERANCE

from scipy.linalg import lu, inv
```

    Current Directory: /Users/woosubs/Desktop/ModelEngineering/SBMLLint/SBMLLint/notebook

```python
# make sure to run #BIOMD 18
# #112 is very tricky to detect errors (no errors?!)
# Biomodels 383 can be used to test reduction
simple = load_file_from_games(190)
# for r in simple.reactions:
#   if r.category != cn.REACTION_BOUNDARY:
#     print(r.makeIdentifier(is_include_kinetics=False))
```

```python
# mat_t = m.som_stoichiometry_matrix.T
# mat_t
# p, l_raw, l_raw = lu(mat_t)
# print(p)
# print(l_raw)
# print(l_raw)
# l = np.round(l_raw, 3)
# print("l is\n", l)
```

```python
m = GAMES_PP(simple)
res = m.analyze()
```

    We just analyzed the data...
    Type I error:  []
    Type II error:  []
    Canceling error:  []
    Echelon error:  [SSAT_for_S: {A=Met=SAM} -> , SSAT_for_D: {A=Met=SAM} -> ]
    Type III error:  []

```python
try:
  k = inv(m.lower)
except:
  print("l is singular")
k
```

    array([[ 1., -0.,  0., -0.],
           [ 0.,  1.,  0., -0.],
           [ 1.,  0.,  1., -0.],
           [ 0.,  1.,  0.,  1.]])

```python
print(m.rref_df['reaction_45']['{CamR_Ca3_BCD_CaMKII=CamR_Ca3_BCD_CaMKIIp}'])
print(m.rref_df['reaction_45']['{CamR_Ca4_ABCD_CaMKII=CamR_Ca4_ABCD_CaMKIIp}'])
```

    -0.002
    0.002

```python
m.getStoichiometryMatrix(simple.reactions, simple.molecules)
```

      PGA_prod_Vc
      PGA_prod_Vo
      PGA_cons
      NADPH_prod

      RuBP
      -1.0
      -1.0
      1.0
      0.0

      CO2
      -1.0
      -1.0
      0.0
      0.0

      NADPH
      -2.0
      -2.0
      0.0
      1.0

      PGA
      2.0
      1.5
      -1.0
      0.0

      NADP
      0.0
      0.0
      0.0
      -1.0

```python
# som_reactions = []
# for r in m.reactions:
#   som_reactions.append(m.convertReactionToSOMReaction(r))
# som_mat = m.getStoichiometryMatrix(
#     som_reactions,
#     m.nodes,
#     som=True)
# som_mat
# echelon = m.decomposeMatrix(som_mat)
# echelon.T
```

```python
rref = m.getRREFMatrix(echelon)
rref.T
```

      {NADP}
      {PGA}
      {RuBP}
      {NADPH}
      {CO2}

      NADPH_prod
      -1.0
      0.0
      0.0
      0.0
      -0.5

      PGA_prod_Vc
      0.0
      2.0
      0.0
      0.0
      0.0

      PGA_cons
      0.0
      0.0
      0.5
      0.0
      0.0

      PGA_prod_Vo
      0.0
      0.0
      0.0
      -1.0
      -0.5

```python
last_row = rref.T.iloc[-1:]
last_row
```

      {NADP}
      {PGA}
      {RuBP}
      {NADPH}
      {CO2}

      PGA_prod_Vo
      0.0
      0.0
      0.0
      -1.0
      -0.5

```python
nonzero_species = (last_row != 0).idxmax(axis=1)[0]
rref.T[nonzero_species]
```

    NADPH_prod     0.0
    PGA_prod_Vc    0.0
    PGA_cons       0.0
    PGA_prod_Vo   -1.0
    Name: {NADPH}, dtype: float64

```python
last_row[nonzero_species][0]
```

    -1.0

```python
m.analyze()
```

    We just analyzed the data...
    Type I error:  []
    Type II error:  []
    Canceling error:  []
    Echelon error:  [PGA_prod_Vo: 0.50 {PGA, RuBP} -> ]
    Type III error:  []
    Type I-SOM error:  set()

    True

```python
som_co2 = list(m.nodes)[0]
som_pga = list(m.nodes)[1]
m.get_edge_data(som_co2, som_pga)['reaction']
```

    ['PGA_prod_Vc']

```python
m.nodes
```

    NodeView(({CO2}, {PGA, RuBP}, {NADP, NADPH}))

```python

```

```python
p, l, u = lu(m.som_stoichiometry_matrix.T)
```

```python
inv(l)
```

    array([[ 1.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  1.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.33333333,  1.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.2       ,  0.6       ,  1.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  1.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        , -0.33333333,  0.        , -0.66666667,  0.        ,
             1.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.33333333,  0.        ,  0.66666667,  0.        ,
             0.        ,  1.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 1.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  1.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  1.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  1.        ,
             0.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             1.        ,  0.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             1.        ,  1.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  1.        ,  0.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  1.        ,
             0.        ,  0.        ,  0.        ,  1.        ,  0.        ,
             0.        ,  0.        ,  1.        ,  0.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        , -0.5       ,
             0.        ,  0.        ,  0.        , -0.5       ,  0.        ,
             0.        ,  0.        , -0.5       ,  1.        , -0.        ],
           [ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
             0.        ,  0.        ,  0.        ,  0.        ,  1.        ]])

```python

```

```python
mat_df = m.som_stoichiometry_matrix
mat_t = mat_df.T
idx_mat_t = mat_t.index
cols_mat_t = mat_t.columns
#################################
if mat_t.shape[0] > mat_t.shape[1]:
  diff = mat_t.shape[0] - mat_t.shape[1]
  for i in range(diff):
    mat_t["_" + str(i)] = np.zeros(mat_t.shape[0])
#################################
# LU decomposition
perm, lower, upper = lu(mat_t)
# # the following is applicable to transposed stoichiometry matrix
# # 'perm' is an orthogonal matrix, meaning perm.T == inv(perm)
# ## permuted_m = inv(perm).dot(mat_t)
perm_inverse = perm.T
permuted_m = (perm_inverse).dot(mat_t)
pivot_index = [list(k).index(1) for k in perm_inverse]
new_idx_mat_t = [idx_mat_t[idx] for idx in pivot_index]
# # we save as; perm_df * lower_operation * mat_t = rref_df
if diff:
  perm_df = pd.DataFrame(permuted_m,
      index=new_idx_mat_t,
      columns=mat_t.columns).drop(columns = mat_t.columns[-diff:]).T
  rref_df = pd.DataFrame(upper,
      index=new_idx_mat_t,
      columns=mat_t.columns).drop(columns = mat_t.columns[-diff:]).T
else:
  perm_df = pd.DataFrame(permuted_m,
      index=new_idx_mat_t,
      columns=mat_t.columns).T
  rref_df = pd.DataFrame(upper,
      index=new_idx_mat_t,
      columns=mat_t.columns).T
lower_inverse = pd.DataFrame(inv(lower),
     columns=new_idx_mat_t)
# self.permuted_matrix = perm_df
# self.lower_inverse = lower_inverse
# self.rref_df = rref_df
```

```python
lower_inverse
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      0
      1.0
      0.0
      0.0
      0.0
      0.0
      0.0
      -0.0

      1
      0.0
      1.0
      0.0
      0.0
      0.0
      0.0
      -0.0

      2
      0.0
      -1.0
      1.0
      0.0
      0.0
      0.0
      -0.0

      3
      0.0
      1.0
      0.0
      1.0
      0.0
      0.0
      -0.0

      4
      0.0
      1.0
      0.0
      0.0
      1.0
      0.0
      -0.0

      5
      0.0
      -1.0
      0.0
      0.0
      0.0
      1.0
      -0.0

      6
      1.0
      0.0
      0.0
      0.0
      0.0
      0.0
      1.0

```python
perm_df
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      {Pr}
      1.0
      0.00
      0.00
      0.00
      0.00
      0.00
      -1.0

      {Ca_cyt}
      1.0
      1.00
      1.00
      -1.00
      -1.00
      1.00
      -1.0

      {CaPr}
      -1.0
      0.00
      0.00
      0.00
      0.00
      0.00
      1.0

      {CaER}
      0.0
      -0.25
      0.00
      0.00
      0.25
      -0.25
      0.0

      {CaM}
      0.0
      0.00
      -0.25
      0.25
      0.00
      0.00
      0.0

```python

```

      {Pr}
      {Ca_cyt}
      {CaPr}
      {CaER}
      {CaM}
      _0
      _1

      v11
      1.0
      1.0
      -1.0
      0.00
      0.00
      0.0
      0.0

      v3
      0.0
      1.0
      0.0
      -0.25
      0.00
      0.0
      0.0

      v7
      0.0
      0.0
      0.0
      0.25
      -0.25
      0.0
      0.0

      v9
      0.0
      0.0
      0.0
      -0.25
      0.25
      0.0
      0.0

      v5
      0.0
      0.0
      0.0
      0.00
      0.00
      0.0
      0.0

      v1
      0.0
      0.0
      0.0
      0.00
      0.00
      0.0
      0.0

      v12
      0.0
      0.0
      0.0
      0.00
      0.00
      0.0
      0.0

```python
mat_t.columns[-diff:]
```

    Index(['_0', '_1'], dtype='object')

```python
pd.DataFrame(upper,
    index=new_idx_mat_t,
    columns=mat_t.columns).drop(columns = mat_t.columns[-diff:]).T
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      {Pr}
      1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {Ca_cyt}
      1.0
      1.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaPr}
      -1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaER}
      0.0
      -0.25
      0.25
      -0.25
      0.0
      0.0
      0.0

      {CaM}
      0.0
      0.00
      -0.25
      0.25
      0.0
      0.0
      0.0

```python
k.drop(columns = mat_t.columns[-diff:])
```

      {Pr}
      {Ca_cyt}
      {CaPr}
      {CaER}
      {CaM}

      v11
      1.0
      1.0
      -1.0
      0.00
      0.00

      v3
      0.0
      1.0
      0.0
      -0.25
      0.00

      v7
      0.0
      0.0
      0.0
      0.25
      -0.25

      v9
      0.0
      0.0
      0.0
      -0.25
      0.25

      v5
      0.0
      0.0
      0.0
      0.00
      0.00

      v1
      0.0
      0.0
      0.0
      0.00
      0.00

      v12
      0.0
      0.0
      0.0
      0.00
      0.00

```python
rref_df
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      {Pr}
      1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {Ca_cyt}
      1.0
      1.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaPr}
      -1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaER}
      0.0
      -0.25
      0.25
      -0.25
      0.0
      0.0
      0.0

      {CaM}
      0.0
      0.00
      -0.25
      0.25
      0.0
      0.0
      0.0

      _0
      0.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      _1
      0.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

```python
rref_df.columns[-diff:]
```

    Index(['v1', 'v12'], dtype='object')

```python
-diff
```

    -2

```python
rref_df.drop(rref_df.T.columns[-diff:])
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      {Pr}
      1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {Ca_cyt}
      1.0
      1.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaPr}
      -1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaER}
      0.0
      -0.25
      0.25
      -0.25
      0.0
      0.0
      0.0

      {CaM}
      0.0
      0.00
      -0.25
      0.25
      0.0
      0.0
      0.0

```python

```

```python
list(range(-2, 0))
```

    [-2, -1]

```python
rref_df.drop([0, 1])
```

```python
print(idx_mat_t)
print(new_idx_mat_t)
```

    Index(['v1', 'v3', 'v7', 'v11', 'v5', 'v9', 'v12'], dtype='object')
    ['v11', 'v3', 'v7', 'v9', 'v5', 'v1', 'v12']

```python
perm_inverse
```

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

```python
m.som_stoichiometry_matrix.T
```

      {Pr}
      {Ca_cyt}
      {CaPr}
      {CaER}
      {CaM}

      v1
      0.0
      1.0
      0.0
      -0.25
      0.00

      v3
      0.0
      1.0
      0.0
      -0.25
      0.00

      v7
      0.0
      1.0
      0.0
      0.00
      -0.25

      v11
      1.0
      1.0
      -1.0
      0.00
      0.00

      v5
      0.0
      -1.0
      0.0
      0.25
      0.00

      v9
      0.0
      -1.0
      0.0
      0.00
      0.25

      v12
      -1.0
      -1.0
      1.0
      0.00
      0.00

```python
m.lower_inverse
```

```python

```

```python
sm = m.som_stoichiometry_matrix.T
print(sm.shape[0])
print(sm.shape[1])
sm
```

    7
    5

      {Pr}
      {Ca_cyt}
      {CaPr}
      {CaER}
      {CaM}

      v1
      0.0
      1.0
      0.0
      -0.25
      0.00

      v3
      0.0
      1.0
      0.0
      -0.25
      0.00

      v7
      0.0
      1.0
      0.0
      0.00
      -0.25

      v11
      1.0
      1.0
      -1.0
      0.00
      0.00

      v5
      0.0
      -1.0
      0.0
      0.25
      0.00

      v9
      0.0
      -1.0
      0.0
      0.00
      0.25

      v12
      -1.0
      -1.0
      1.0
      0.00
      0.00

```python
if sm.shape[0] > sm.shape[1]:
  diff = sm.shape[0] - sm.shape[1]
  for i in range(diff):
    sm["_" + str(i)] = np.zeros(sm.shape[0])
sm
```

      {Pr}
      {Ca_cyt}
      {CaPr}
      {CaER}
      {CaM}
      _0
      _1

      v1
      0.0
      1.0
      0.0
      -0.25
      0.00
      0.0
      0.0

      v3
      0.0
      1.0
      0.0
      -0.25
      0.00
      0.0
      0.0

      v7
      0.0
      1.0
      0.0
      0.00
      -0.25
      0.0
      0.0

      v11
      1.0
      1.0
      -1.0
      0.00
      0.00
      0.0
      0.0

      v5
      0.0
      -1.0
      0.0
      0.25
      0.00
      0.0
      0.0

      v9
      0.0
      -1.0
      0.0
      0.00
      0.25
      0.0
      0.0

      v12
      -1.0
      -1.0
      1.0
      0.00
      0.00
      0.0
      0.0

```python
m.decomposeMatrix(sm.T)
```

      v11
      v3
      v7
      v9
      v5
      v1
      v12

      {Pr}
      1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {Ca_cyt}
      1.0
      1.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaPr}
      -1.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      {CaER}
      0.0
      -0.25
      0.25
      -0.25
      0.0
      0.0
      0.0

      {CaM}
      0.0
      0.00
      -0.25
      0.25
      0.0
      0.0
      0.0

      _0
      0.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

      _1
      0.0
      0.00
      0.00
      0.00
      0.0
      0.0
      0.0

```python
m.lower_inverse
```

      {Ca_cyt}
      {Pr}
      {CaER}
      {CaPr}
      {CaM}
      _0
      _1

      0
      1.00
      0.0
      0.0
      0.00
      0.0
      0.0
      0.0

      1
      0.00
      1.0
      0.0
      0.00
      0.0
      0.0
      0.0

      2
      -0.25
      0.0
      1.0
      0.00
      0.0
      0.0
      0.0

      3
      0.00
      0.0
      0.0
      1.00
      0.0
      0.0
      0.0

      4
      0.00
      0.0
      -1.0
      -0.25
      1.0
      0.0
      0.0

      5
      0.00
      0.0
      0.0
      -0.00
      0.0
      1.0
      0.0

      6
      0.00
      0.0
      0.0
      -0.00
      0.0
      0.0
      1.0

```python
inv(m.lower_inverse)
```

    array([[ 1.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  , -0.  ],
           [ 0.  ,  1.  ,  0.  ,  0.  ,  0.  ,  0.  , -0.  ],
           [ 0.25,  0.  ,  1.  ,  0.  ,  0.  ,  0.  , -0.  ],
           [ 0.  ,  0.  ,  0.  ,  1.  ,  0.  ,  0.  , -0.  ],
           [ 0.25,  0.  ,  1.  ,  0.25,  1.  ,  0.  , -0.  ],
           [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  1.  , -0.  ],
           [ 0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  1.  ]])

```python
m.reactions_lu
```

    [reaction_6: R_smad_P_smad4_nuc -> smad4_nuc + R_smad_P_nuc; k6d * R_smad_P_smad4_nuc - k6a * smad4_nuc * R_smad_P_nuc,
     reaction_7: R_smad_P_nuc -> R_smad_nuc + Pi; Vmax7 * R_smad_P_nuc / (K7 + R_smad_P_nuc),
     reaction_2: R_smad_P_cyt + smad4_cyt -> R_smad_P_smad4_cyt; k2a * R_smad_P_cyt * smad4_cyt - k2d * R_smad_P_smad4_cyt]

```python
m.som_stoichiometry_matrix
```

      reaction_6
      reaction_7
      reaction_2

      {R_smad_P_nuc}
      1.0
      -1.0
      0.0

      {Pi}
      0.0
      1.0
      0.0

      {R_smad_P_cyt, R_smad_cyt, R_smad_nuc}
      0.0
      1.0
      -1.0

      {smad4_cyt, smad4_nuc}
      1.0
      0.0
      -1.0

      {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}
      -1.0
      0.0
      1.0

```python
m.decomposeMatrix(m.som_stoichiometry_matrix).T
```

      {R_smad_P_nuc}
      {Pi}
      {R_smad_P_cyt, R_smad_cyt, R_smad_nuc}
      {smad4_cyt, smad4_nuc}
      {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}

      reaction_6
      1.0
      0.0
      0.0
      1.0
      -1.0

      reaction_7
      0.0
      1.0
      1.0
      1.0
      -1.0

      reaction_2
      0.0
      0.0
      -1.0
      -1.0
      1.0

```python
m.reduced_som_reactions
```

    [reaction_6: {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc} -> {R_smad_P_nuc} + {smad4_cyt, smad4_nuc},
     reaction_7: {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc} -> {Pi} + {R_smad_P_cyt, R_smad_cyt, R_smad_nuc} + {smad4_cyt, smad4_nuc},
     reaction_2: {R_smad_P_cyt, R_smad_cyt, R_smad_nuc} + {smad4_cyt, smad4_nuc} -> {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}]

```python
m.processUnequalSOMReaction(m.reduced_som_reactions[0])
m.edges
```

    OutEdgeView([({R_smad_P_nuc}, {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}), ({Pi}, {R_smad_P_nuc}), ({Pi}, {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}), ({R_smad_P_cyt, R_smad_cyt, R_smad_nuc}, {R_smad_P_nuc}), ({R_smad_P_cyt, R_smad_cyt, R_smad_nuc}, {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}), ({smad4_cyt, smad4_nuc}, {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc})])

```python
reaction = m.reduced_som_reactions[0]
category = reaction.category
if (category!=cn.REACTION_n_1) or (category!=cn.REACTION_1_n):
  print("noo")
# else:
#   if category == cn.REACTION_n_1:
#     destination = [reaction.products[0].molecule]
#     source = [reactant.molecule for reactant in reaction.reactants]
#   elif category == cn.REACTION_1_n:
#     destination = [reaction.reactants[0].molecule]
#     source = [product.molecule for product in reaction.products]
#   arcs = itertools.product(source, destination)
#   for arc in arcs:
#     if arc[0] == arc[1]:
#       self.type_one_som_errors = self.type_one_som_errors.add(reaction.label)
#     else:
#       self.addArc(arc[0], arc[1], reactin.label)
```

    noo

```python
m.lower_inverse.dot(m.permuted_matrix.T)
```

      {R_smad_P_nuc}
      {Pi}
      {R_smad_P_cyt, R_smad_cyt, R_smad_nuc}
      {smad4_cyt, smad4_nuc}
      {R_smad_P_smad4_cyt, R_smad_P_smad4_nuc}

      0
      1.0
      0.0
      0.0
      1.0
      -1.0

      1
      -2.0
      1.0
      1.0
      -1.0
      1.0

      2
      0.0
      0.0
      -1.0
      -1.0
      1.0

```python
p, l, u = lu(m.stoichiometry_matrix.T)
p
```

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

```python
p.dot(p.T)
```

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

```python
u
```

    array([[ 1.,  0.,  0.,  0.,  0.,  0.,  0., -1.,  0.],
           [ 0.,  1.,  0., -1.,  0.,  0.,  1.,  0.,  0.],
           [ 0.,  0.,  1.,  0.,  0., -1.,  0., -1.,  0.],
           [ 0.,  0.,  0.,  1.,  0., -1.,  0., -1.,  0.],
           [ 0.,  0.,  0.,  0., -1.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  1., -1.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

```python
l.dot(u)
```

    array([[ 1.,  0.,  0.,  0.,  0.,  0.,  0., -1.,  0.],
           [ 0.,  1.,  0., -1.,  0.,  0.,  1.,  0.,  0.],
           [-1.,  0.,  1.,  0.,  0., -1.,  0.,  0.,  0.],
           [ 0.,  0., -1.,  1.,  0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0., -1.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  1., -1.,  0.,  0.],
           [ 0., -1.,  0.,  0.,  1.,  0.,  0.,  0.,  1.]])

```python
# this is the matrix for operation (without transpose)
reduce_operation = inv(l).dot(inv(p))
reduce_operation
```

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

```python
print(m.analyze())
m.rref_errors
```

    False

    []

```python
if [] or [] or []:
  print("none")
```

```python
rref_df = m.decomposeMatrix(m.stoichiometry_matrix)
reactions = m.convertMatrixToReactions(simple, rref_df)
```

```python
reactions
```

    [ReactionSummary(label='reaction6', reactants=[XI *  1.00], products=[X *  1.00], category='reaction_1_1'),
     ReactionSummary(label='reaction5', reactants=[M *  1.00], products=[MI *  1.00], category='reaction_1_1'),
     ReactionSummary(label='reaction4', reactants=[], products=[], category='reaction_redundant'),
     ReactionSummary(label='reaction7', reactants=[], products=[], category='reaction_redundant')]

```python
k = m.processUniUniReaction(reactions[0])
```

```python
type(reactions[0].reactants[0].molecule)
```

    SBMLLint.common.molecule.Molecule

```python
str(reactions[0].label)
```

    'reaction6'

```python
m2 = MESGraph(simple)
m2.analyze()
```

    Model analyzed...
    No error detected.

    ''

```python
s = StoichiometryMatrix(simple)
s.isConsistent()
```

    True

```python
list(m2.nodes)[1].reactions
```

    {reaction4: MI -> M; cell * MI * V1 * pow(K1 + MI, -1)}

```python
import networkx as nx
```

```python

```

```python

```


# Theoretical Foundations of Buffer Stock Saving

Generator: REMARK-make/REMARKs/BufferStockTheory.sh

[](https://mybinder.org/v2/gh/econ-ark/REMARK/master?filepath=REMARKs%2FBufferStockTheory%2FBufferStockTheory.ipynb)

This notebook uses the [Econ-ARK/HARK](https://github.com/econ-ark/hark) toolkit to describe the main results and reproduce the figures in the paper [Theoretical Foundations of Buffer Stock Saving](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory).

If you are not familiar with the HARK toolkit, you may wish to browse the ["Gentle Introduction to HARK"](https://mybinder.org/v2/gh/econ-ark/DemARK/master?filepath=Gentle-Intro-To-HARK.ipynb) before continuing (since you are viewing this document, you presumably know a bit about [Jupyter Notebooks](https://jupyter-notebook-beginner-guide.readthedocs.io/en/latest/)).

For instructions on how to install the [Econ-ARK/HARK](https://github.com/econ-ark/hark) toolkit on your computer, please refer to the [QUICK START GUIDE](https://github.com/econ-ark/HARK/blob/master/README.md).

The main HARK tool used here is $\texttt{ConsIndShockModel.py}$, in which agents have CRRA utility and face idiosyncratic shocks to permanent and transitory income.  For an introduction to this module, see the [ConsIndShockModel.ipynb](https://econ-ark.org/notebooks) notebook at the [Econ-ARK](https://econ-ark.org) website.

```python
# This cell does some setup and imports generic tools used to produce the figures

Generator=False # Is this notebook the master or is it generated?
# Import related generic python packages
import numpy as np
from time import clock
mystr = lambda number : "{:.4f}".format(number)

# This is a jupytext paired notebook that autogenerates BufferStockTheory.py
# which can be executed from a terminal command line via "ipython BufferStockTheory.py"
# But a terminal does not permit inline figures, so we need to test jupyter vs terminal
# Google "how can I check if code is executed in the ipython notebook"

from IPython import get_ipython # In case it was run from python instead of ipython
def in_ipynb():
    try:
        if str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>":
            return True
        else:
            return False
    except NameError:
        return False

# Determine whether to make the figures inline (for spyder or jupyter)
# vs whatever is the automatic setting that will apply if run from the terminal
if in_ipynb():
    # %matplotlib inline generates a syntax error when run from the shell
    # so do this instead
    get_ipython().run_line_magic('matplotlib', 'inline')
else:
    get_ipython().run_line_magic('matplotlib', 'auto')

# Import the plot-figure library matplotlib

import matplotlib.pyplot as plt

# In order to use LaTeX to manage all text layout in our figures, we import rc settings from matplotlib.
from matplotlib import rc
plt.rc('font', family='serif')

# LaTeX is huge and takes forever to install on mybinder
# so if it is not installed then do not use it
from distutils.spawn import find_executable
iflatexExists=False
if find_executable('latex'):
    iflatexExists=True

plt.rc('text', usetex= iflatexExists)
plt.rc('text', usetex= iflatexExists)

# The warnings package allows us to ignore some harmless but alarming warning messages
import warnings
warnings.filterwarnings("ignore")

# The tools for navigating the filesystem
import sys
import os

sys.path.insert(0, os.path.abspath('../../lib')) # REMARKs directory is two down from root

from HARK.utilities import plotFuncsDer, plotFuncs
from copy import copy, deepcopy

# Define (and create, if necessary) the figures directory "Figures"
if Generator:
    my_file_path = os.path.dirname(os.path.abspath("BufferStockTheory.ipynb")) # Find pathname to this file:
    Figures_HARK_dir = os.path.join(my_file_path,"Figures/") # LaTeX document assumes figures will be here
    Figures_HARK_dir = os.path.join(my_file_path,"/tmp/Figures/") # Uncomment to make figures outside of git path
    if not os.path.exists(Figures_HARK_dir):
        os.makedirs(Figures_HARK_dir)
```

## [The Problem](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-Problem)

The paper defines and calibrates a small set of parameters:

| Parameter | Description | Code | Value |
| :---: | ---         | ---  | :---: |
| $\newcommand{\PermGroFac}{\Gamma}\PermGroFac$ | Permanent Income Growth Factor | $\texttt{PermGroFac}$ | 1.03 |
| $\newcommand{\Rfree}{\mathrm{\mathsf{R}}}\Rfree$ | Interest Factor | $\texttt{Rfree}$ | 1.04 |
| $\newcommand{\DiscFac}{\beta}\DiscFac$ | Time Preference Factor | $\texttt{DiscFac}$ | 0.96 |
| $\newcommand{\CRRA}{\rho}\CRRA$ | Coeﬃcient of Relative Risk Aversion| $\texttt{CRRA}$ | 2 |
| $\newcommand{\UnempPrb}{\wp}\UnempPrb$ | Probability of Unemployment | $\texttt{UnempPrb}$ | 0.005 |
| $\newcommand{\IncUnemp}{\mu}\IncUnemp$ | Income when Unemployed | $\texttt{IncUnemp}$ | 0. |
| $\newcommand{\PermShkStd}{\sigma_\psi}\PermShkStd$ | Std Dev of Log Permanent Shock| $\texttt{PermShkStd}$ | 0.1 |
| $\newcommand{\TranShkStd}{\sigma_\theta}\TranShkStd$ | Std Dev of Log Transitory Shock| $\texttt{TranShkStd}$ | 0.1 |

For a microeconomic consumer with 'Market Resources' (net worth plus current income) $M_{t}$, end-of-period assets $A_{t}$ will be the amount remaining after consumption of $C_{t}$.
\begin{eqnarray}
A_{t}   &=&M_{t}-C_{t}  \label{eq:DBCparts} \\
%B_{t+1}   & = & A_{t} R \notag \\
\end{eqnarray}

The consumer's permanent noncapital income $P$ grows by a predictable factor $\PermGroFac$ and is subject to an unpredictable lognormally distributed multiplicative shock $\mathbb{E}_{t}[\psi_{t+1}]=1$,
\begin{eqnarray}
P_{t+1} & = & P_{t} \PermGroFac \psi_{t+1}
\end{eqnarray}

and actual income is permanent income multiplied by a logormal multiplicative transitory shock, $\mathbb{E}_{t}[\theta_{t+1}]=1$, so that next period's market resources are
\begin{eqnarray}
%M_{t+1} &=& B_{t+1} +P_{t+1}\theta_{t+1},  \notag
M_{t+1} &=& A_{t}R +P_{t+1}\theta_{t+1}.  \notag
\end{eqnarray}

When the consumer has a CRRA utility function $u(c)=\frac{c^{1-\rho}}{1-\rho}$, the paper shows that the problem can be written in terms of ratios of money variables to permanent income, e.g. $m_{t} \equiv M_{t}/P_{t}$, and the Bellman form of [the problem reduces to](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-Related-Problem):

\begin{eqnarray*}
v_t(m_t) &=& \max_{c_t}~~ u(c_t) + \beta~\mathbb{E}_{t} [(\Gamma\psi_{t+1})^{1-\rho} v_{t+1}(m_{t+1}) ] \\
& s.t. & \\
a_t &=& m_t - c_t \\
m_{t+1} &=& R/(\Gamma \psi_{t+1}) a_t + \theta_{t+1} \\
\end{eqnarray*}

```python
# Define a parameter dictionary with baseline parameter values

# Set the baseline parameter values
PermGroFac = 1.03
Rfree      = 1.04
DiscFac    = 0.96
CRRA       = 2.00
UnempPrb   = 0.005
IncUnemp   = 0.0
PermShkStd = 0.1
TranShkStd = 0.1
# Import default parameter values
import HARK.ConsumptionSaving.ConsumerParameters as Params

# Make a dictionary containing all parameters needed to solve the model
base_params = Params.init_idiosyncratic_shocks

# Set the parameters for the baseline results in the paper
# using the variable values defined in the cell above
base_params['PermGroFac'] = [PermGroFac]   # Permanent income growth factor
base_params['Rfree']      = Rfree          # Interest factor on assets
base_params['DiscFac']    = DiscFac        # Time Preference Factor
base_params['CRRA']       = CRRA           # Coefficient of relative risk aversion
base_params['UnempPrb']   = UnempPrb       # Probability of unemployment (e.g. Probability of Zero Income in the paper)
base_params['IncUnemp']   = IncUnemp       # Induces natural borrowing constraint
base_params['PermShkStd'] = [PermShkStd]   # Standard deviation of log permanent income shocks
base_params['TranShkStd'] = [TranShkStd]   # Standard deviation of log transitory income shocks

# Some technical settings that are not interesting for our purposes
base_params['LivPrb']       = [1.0]   # 100 percent probability of living to next period
base_params['CubicBool']    = True    # Use cubic spline interpolation
base_params['T_cycle']      = 1       # No 'seasonal' cycles
base_params['BoroCnstArt']  = None    # No artificial borrowing constraint
```

```python
from ConsIndShockModel import IndShockConsumerType
```

## Convergence of the Consumption Rules

[The paper's first figure](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Convergence-of-the-Consumption-Rules) depicts the successive consumption rules that apply in the last period of life $(c_{T}(m))$, the second-to-last period, and earlier periods under the baseline parameter values given above.

```python
# Create a buffer stock consumer instance by passing the dictionary to the class.
baseEx = IndShockConsumerType(**base_params)
baseEx.cycles = 100   # Make this type have a finite horizon (Set T = 100)

baseEx.solve()        # Solve the model
baseEx.unpackcFunc()  # Make the consumption function easily accessible
```

    This method only checks for the conditions for infinite horizon models with a 1 period cycle

```python
# Plot the different periods' consumption rules.

m1 = np.linspace(0,9.5,1000) # Set the plot range of m
m2 = np.linspace(0,6.5,500)
c_m  = baseEx.cFunc[0](m1)   # c_m can be used to define the limiting inﬁnite-horizon consumption rule here
c_t1 = baseEx.cFunc[-2](m1) # c_t1 defines the second-to-last period consumption rule
c_t5 = baseEx.cFunc[-6](m1) # c_t5 defines the T-5 period consumption rule
c_t10 = baseEx.cFunc[-11](m1)  # c_t10 defines the T-10 period consumption rule
c_t0 = m2                            # c_t0 defines the last period consumption rule
plt.figure(figsize = (12,9))
plt.plot(m1,c_m,color="black")
plt.plot(m1,c_t1,color="black")
plt.plot(m1,c_t5,color="black")
plt.plot(m1,c_t10,color="black")
plt.plot(m2,c_t0,color="black")
plt.xlim(0,11)
plt.ylim(0,7)
plt.text(7,6,r'$c_{T}(m) = 45$ degree line',fontsize = 22,fontweight='bold')
plt.text(9.6,5.3,r'$c_{T-1}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,2.6,r'$c_{T-5}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,2.1,r'$c_{T-10}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,1.7,r'$c(m)$',fontsize = 22,fontweight='bold')
plt.arrow(6.9,6.05,-0.6,0,head_width= 0.1,width=0.001,facecolor='black',length_includes_head='True')
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(0,7.05,"$c$",fontsize = 26)
plt.text(11.1,0,"$m$",fontsize = 26)
# Save the figures in several formats
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.svg'))
```

## Factors and Conditions

### The Finite Human Wealth Condition

Human wealth for a perfect foresight consumer is defined as the present discounted value of future income:

\begin{eqnarray}
H_{t} & = & \mathbb{E}[P_{t} + R^{-1} P_{t+1} + R^{2} P_{t+2} ... ] \\
      & = & P_{t}\mathbb{E}[P_{t} + (\Gamma/R) + (\Gamma/R)^{2} ... ]
\end{eqnarray}
which is an infinite number if $\Gamma/R \geq 1$.  We say that the 'Finite Human Wealth Condition' (FHWC) holds if
$0 \leq (\Gamma/R) \leq 1$.

### Absolute Patience and the AIC

The paper defines an object which it calls the Absolute Patience Factor, equal to the ratio of $C_{t+1}/C_{t}$ for a perfect foresight consumer.  The Old English character "Þ" is used for this object in the paper, but "Þ" cannot currently be rendered conveniently in Jupyter notebooks, so we will substitute $\Phi$ here:

\begin{equation}
\Phi = (R \beta)^{1/\rho}
\end{equation}

If $\Phi = 1$, a perfect foresight consumer will spend exactly the amount that can be sustained perpetually (given their current and future resources).  If $\Phi < 1$ (the consumer is 'absolutely impatient'; or, 'the absolute impatience condition holds'), the consumer is consuming more than the sustainable amount, so consumption will fall, and if the consumer is 'absolutely patient' with $\Phi > 1$ consumption will grow over time.

### Growth Patience and the GIC

For a [perfect foresight consumer](http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA), whether the ratio of consumption to the permanent component of income $P$ is rising, constant, or falling depends on the relative growth rates of consumption and permanent income, which is measured by the "Perfect Foresight Growth Patience Factor":

\begin{eqnarray}
\Phi_{\Gamma} & = & \Phi/\Gamma
\end{eqnarray}
and whether the ratio is falling or rising over time depends on whether $\Phi_{\Gamma}$ is below or above 1.

An analogous condition can be defined when there is uncertainty about permanent income.  Defining $\tilde{\Gamma} = (\mathbb{E}[\psi^{-1}])^{-1}\Gamma$, the 'Growth Impatience Condition' (GIC) is that
\begin{eqnarray}
  \Phi/\tilde{\Gamma} & < & 1
\end{eqnarray}

### The Finite Value of Autarky Condition (FVAC)

The paper [shows](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Autarky-Value) that a consumer who planned to spend his permanent income $\{ p_{t}, p_{t+1}, ...\} $ in every period would have value defined by

\begin{equation}
v_{t}^{\text{autarky}} = u(p_{t})\left(\frac{1}{1-\beta \Gamma^{1-\rho} \mathbb{E}[\psi^{1-\rho}]}\right)
\end{equation}

and defines the 'Finite Value of Autarky Condition' as the requirement that the denominator of this expression be a positive finite number:

\begin{equation}
\beta \Gamma^{1-\rho} \mathbb{E}[\psi^{1-\rho}] < 1
\end{equation}

### The Weak Return Impatience Condition (WRIC)

The 'Return Impatience Condition' $\Phi/R < 1$ has long been understood to be required for the perfect foresight model to have a nondegenerate solution (when $\rho=1$, this reduces to $\beta < R$).  If the RIC does not hold, the consumer is so patient that the optimal consumption function approaches zero as the horizon extends.

When the probability of unemployment is $\wp$, the paper articulates an analogous (but weaker) condition:

\begin{eqnarray}
 \wp^{1/\rho} \Phi/R & < & 1
\end{eqnarray}

# Key Results

## Nondegenerate Solution Requires FVAC and WRIC

The central result of the paper is that the conditions required for the model to have a nondegenerate solution ($0 < c(m) < \infty$ for feasible $m$) are that the Finite Value of Autarky (FVAC) and Weak Return Impatience Condition (WRAC) hold.

A [table](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Sufficient-Conditions-For-Nondegenerate-Solution) puts this result in the context of implications of other conditions and restrictions.

## Natural versus Artificial Borrowing Constraints

Defining $\chi(\wp)$ as the consumption function associated with any particular value of $\wp$, and defining $\hat{\chi}$ as the consumption function that would apply in the absence of the zero-income shocks but in the presence of an 'artificial' borrowing constraint requiring $a \geq 0$, a la Deaton (1991), the paper shows that

\begin{eqnarray}
\lim_{\wp \downarrow 0}~\chi(\wp) & = & \hat{\chi}
\end{eqnarray}

That is, as $\wp$ approaches zero the problem with uncertainty becomes identical to the problem that instead has constraints.  (See [Precautionary Saving and Liquidity Constraints](http://econ.jhu.edu/people/ccarroll/papers/LiqConstr) for a full treatment of the relationship between precautionary saving and liquidity constraints).

## $c(m)$ is Finite Even When Human Wealth Is Infinite

In the perfect foresight model, if $R < \Gamma$ the present discounted value of future labor income is infinite and so the limiting consumption function is $c(m) = \infty$ for all $m$.

The presence of uncertainty changes this: The limiting consumption function is finite for all values of $m$.

This is because uncertainty imposes a "natural borrowing constraint" that deters the consumer from borrowing against their unbounded future labor income.

## If the GIC Holds, $\exists$ a finite 'target' $m$

Section [There Is Exactly One Target $m$ Ratio, Which Is Stable]() shows that, under parameter values for which the limiting consumption function exists, if the GIC holds then there will be a value $\check{m}$ such that:

\begin{eqnarray}
\mathbb{E}[m_{t+1}] & > & m_{t}~\text{if $m_{t} < \check{m}$} \\
\mathbb{E}[m_{t+1}] & < & m_{t}~\text{if $m_{t} > \check{m}$} \\
\mathbb{E}[m_{t+1}] & = & m_{t}~\text{if $m_{t} = \check{m}$}
\end{eqnarray}

## Target Wealth is Infinite if the GIC Fails

The section [The GIC](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-GIC) depicts a solution when the **FVAC** (Finite Value of Autarky Condition) and **WRIC** hold (so that the model has a solution) but the **GIC** (Growth Impatience Condition) fails.  In this case the target wealth ratio is infinity.

The parameter values in this specific example are:

| Param | Description | Code | Value |
| :---: | ---         | ---  | :---: |
| $\Gamma$ | Permanent Income Growth Factor | $\texttt{PermGroFac}$ | 1.00 |
| $\mathrm{\mathsf{R}}$ | Interest Factor | $\texttt{Rfree}$ | 1.08 |

The figure is reproduced below.

```python
# Construct the "GIC fails" example.
GIC_fail_dictionary = dict(base_params)
GIC_fail_dictionary['Rfree']      = 1.08
GIC_fail_dictionary['PermGroFac'] = [1.00]

GICFailExample = IndShockConsumerType(
    cycles=0, # cycles=0 makes this an infinite horizon consumer
    **GIC_fail_dictionary)
```

    The given type violates the absolute impatience condition with the supplied parameter values; the AIF is 1.01823
    The given parameter values violate the growth impatience condition for this consumer type; the GIF is: 1.0088

The $\mathtt{IndShockConsumerType}$ tool automatically checks various parametric conditions, and will give a warning as well as the values of the factors if any conditions fail to be met.

We can also directly check the conditions, in which case results will be a little more verbose by default.

```python
# The checkConditions method does what it sounds like it would
GICFailExample.checkConditions(verbose=True)
```

    The given type violates the absolute impatience condition with the supplied parameter values; the AIF is 1.01823
        Therefore, the absolute amount of consumption is expected to grow over time
    The given parameter values violate the growth impatience condition for this consumer type; the GIF is: 1.0088
        Therefore, a target level of wealth does not exist.
    The weak return impatience factor value for the supplied parameter values satisfies the weak return impatience condition.
    The finite value of autarky factor value for the supplied parameter values satisfies the finite value of autarky condition.

    [!] For more information on the conditions, see Table 3 in "Theoretical Foundations of Buffer Stock Saving" at http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/

Next we define the function $\mathrm{\mathbb{E}}_{t}[\Delta m_{t+1}]$ that shows the ‘sustainable’ level of spending at which $m$ is expected to remain unchanged.

```python
# Calculate "Sustainable" consumption that leaves expected m unchanged
# In the perfect foresight case, this is just permanent income plus interest income
# A small adjustment is required to take account of the consequences of uncertainty
InvEpShInvAct = np.dot(GICFailExample.PermShkDstn[0][0], GICFailExample.PermShkDstn[0][1]**(-1))
InvInvEpShInvAct = (InvEpShInvAct) ** (-1)
PermGroFacAct = GICFailExample.PermGroFac[0] * InvInvEpShInvAct
ER = GICFailExample.Rfree / PermGroFacAct
Er = ER - 1
mSSfunc = lambda m : 1 + (m-1)*(Er/ER)
```

```python
# Plot GICFailExample consumption function against the sustainable level of consumption
GICFailExample.solve() # Above, we set up the problem but did not solve it
GICFailExample.unpackcFunc()  # Make the consumption function easily accessible for plotting
m = np.linspace(0,5,1000)
c_m = GICFailExample.cFunc[0](m)
E_m = mSSfunc(m)
plt.figure(figsize = (12,8))
plt.plot(m,c_m,color="black")
plt.plot(m,E_m,color="black")
plt.xlim(0,5.5)
plt.ylim(0,1.6)
plt.text(0,1.63,"$c$",fontsize = 26)
plt.text(5.55,0,"$m$",fontsize = 26)
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(1,0.6,"$c(m_{t})$",fontsize = 18)
plt.text(1.5,1.2,"$\mathrm{\mathsf{E}}_{t}[\Delta m_{t+1}] = 0$",fontsize = 18)
plt.arrow(0.98,0.62,-0.2,0,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.2,1.2,0.3,-0.05,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.svg'))

# This figure reproduces the figure shown in the paper.
# The gap between the two functions actually increases with $m$ in the limit.
```

As a foundation for the remaining figures, we define another instance of the class $\texttt{IndShockConsumerType}$, which has the same parameter values as the instance $\texttt{baseEx}$ defined previously but is solved to convergence (our definition of an infinite horizon agent type)

```python
# cycles=0 tells the solver to find the infinite horizon solution
baseEx_inf = IndShockConsumerType(cycles=0,**base_params)

baseEx_inf.solve()
baseEx_inf.unpackcFunc()
```

### Target $m$, Expected Consumption Growth, and Permanent Income Growth

The next figure is shown in  [Analysis of the Converged Consumption Function](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Analysis-of-the-Converged-Consumption-Function), which shows the expected consumption growth factor $\mathrm{\mathbb{E}}_{t}[c_{t+1}/c_{t}]$ for a consumer behaving according to the converged consumption rule.

The first step of the figure's construction is to calculate the t+1 period expected consumption. We define an auxiliary function to calculate the expectation of t+1 period consumption given t end-of-period assets.

```python
# Define a function to calculate expected consumption
def exp_consumption(a):
    '''
    Taking end-of-period assets as input, return expectation of next period's consumption
    Inputs:
       a: end-of-period assets
    Returns:
       expconsump: next period's expected consumption
    '''
    GrowFactp1 = baseEx_inf.PermGroFac[0]* baseEx_inf.PermShkDstn[0][1]
    Rnrmtp1 = baseEx_inf.Rfree / GrowFactp1
    # end-of-period assets plus normalized returns
    btp1 = Rnrmtp1*a
    # expand dims of btp1 and use broadcasted sum of a column and a row vector
    # to obtain a matrix of possible beginning-of-period assets next period
    mtp1 = np.expand_dims(btp1, axis=1) + baseEx_inf.TranShkDstn[0][1]
    part_expconsumption = GrowFactp1*baseEx_inf.cFunc[0](mtp1).T
    # finish expectation over permanent income shocks by right multiplying with
    # the weights
    part_expconsumption = np.dot(part_expconsumption, baseEx_inf.PermShkDstn[0][0])
    # finish expectation over transitory income shocks by right multiplying with
    # weights
    expconsumption = np.dot(part_expconsumption, baseEx_inf.TranShkDstn[0][0])
    # return expected consumption
    return expconsumption
```

```python
# Calculate the expected consumption growth factor
m1 = np.linspace(1,baseEx_inf.solution[0].mNrmSS,50) # m1 defines the plot range on the left of target m value (e.g. m <= target m)
c_m1 = baseEx_inf.cFunc[0](m1)
a1 = m1-c_m1
exp_consumption_l1 = []
for i in range(len(a1)):
    exp_consumption_tp1 = exp_consumption(a1[i])
    exp_consumption_l1.append(exp_consumption_tp1)

# growth1 defines the values of expected consumption growth factor when m is less than target m
growth1 = np.array(exp_consumption_l1)/c_m1

# m2 defines the plot range on the right of target m value (e.g. m >= target m)
m2 = np.linspace(baseEx_inf.solution[0].mNrmSS,1.9,50)

c_m2 = baseEx_inf.cFunc[0](m2)
a2 = m2-c_m2
exp_consumption_l2 = []
for i in range(len(a2)):
    exp_consumption_tp1 = exp_consumption(a2[i])
    exp_consumption_l2.append(exp_consumption_tp1)

# growth 2 defines the values of expected consumption growth factor when m is bigger than target m
growth2 = np.array(exp_consumption_l2)/c_m2
```

```python
# Define a function to construct the arrows on the consumption growth rate function
def arrowplot(axes, x, y, narrs=15, dspace=0.5, direc='neg',
              hl=0.01, hw=3, c='black'):
    '''
    The function is used to plot arrows given the data x and y.

    Input:
        narrs  :  Number of arrows that will be drawn along the curve

        dspace :  Shift the position of the arrows along the curve.
                  Should be between 0. and 1.

        direc  :  can be 'pos' or 'neg' to select direction of the arrows

        hl     :  length of the arrow head

        hw     :  width of the arrow head

        c      :  color of the edge and face of the arrow head
    '''

    # r is the distance spanned between pairs of points
    r = np.sqrt(np.diff(x)**2+np.diff(y)**2)
    r = np.insert(r, 0, 0.0)

    # rtot is a cumulative sum of r, it's used to save time
    rtot = np.cumsum(r)

    # based on narrs set the arrow spacing
    aspace = r.sum() / narrs

    if direc is 'neg':
        dspace = -1.*abs(dspace)
    else:
        dspace = abs(dspace)

    arrowData = [] # will hold tuples of x,y,theta for each arrow
    arrowPos = aspace*(dspace) # current point on walk along data
                                 # could set arrowPos to 0 if you want
                                 # an arrow at the beginning of the curve

    ndrawn = 0
    rcount = 1
    while arrowPos < r.sum() and ndrawn < narrs:
        x1,x2 = x[rcount-1],x[rcount]
        y1,y2 = y[rcount-1],y[rcount]
        da = arrowPos-rtot[rcount]
        theta = np.arctan2((x2-x1),(y2-y1))
        ax = np.sin(theta)*da+x1
        ay = np.cos(theta)*da+y1
        arrowData.append((ax,ay,theta))
        ndrawn += 1
        arrowPos+=aspace
        while arrowPos > rtot[rcount+1]:
            rcount+=1
            if arrowPos > rtot[-1]:
                break

    for ax,ay,theta in arrowData:
        # use aspace as a guide for size and length of things
        # scaling factors were chosen by experimenting a bit

        dx0 = np.sin(theta)*hl/2.0 + ax
        dy0 = np.cos(theta)*hl/2.0 + ay
        dx1 = -1.*np.sin(theta)*hl/2.0 + ax
        dy1 = -1.*np.cos(theta)*hl/2.0 + ay

        if direc is 'neg' :
            ax0 = dx0
            ay0 = dy0
            ax1 = dx1
            ay1 = dy1
        else:
            ax0 = dx1
            ay0 = dy1
            ax1 = dx0
            ay1 = dy0

        axes.annotate('', xy=(ax0, ay0), xycoords='data',
                xytext=(ax1, ay1), textcoords='data',
                arrowprops=dict( headwidth=hw, frac=1., ec=c, fc=c))
```

```python
# Plot consumption growth as a function of market resources
# Calculate Absolute Patience Factor Phi = lower bound of consumption growth factor
AbsPatientFac = (baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)

fig = plt.figure(figsize = (12,8))
ax = fig.add_subplot(111)
# Plot the Absolute Patience Factor line
ax.plot([0,1.9],[AbsPatientFac,AbsPatientFac],color="black")

# Plot the Permanent Income Growth Factor line
ax.plot([0,1.9],[baseEx_inf.PermGroFac[0],baseEx_inf.PermGroFac[0]],color="black")

# Plot the expected consumption growth factor on the left side of target m
ax.plot(m1,growth1,color="black")

# Plot the expected consumption growth factor on the right side of target m
ax.plot(m2,growth2,color="black")

# Plot the arrows
arrowplot(ax, m1,growth1)
arrowplot(ax, m2,growth2, direc='pos')

# Plot the target m
ax.plot([baseEx_inf.solution[0].mNrmSS,baseEx_inf.solution[0].mNrmSS],[0,1.4],color="black",linestyle="--")
ax.set_xlim(1,2.05)
ax.set_ylim(0.98,1.08)
ax.text(1,1.082,"Growth Rate",fontsize = 26,fontweight='bold')
ax.text(2.055,0.98,"$m_{t}$",fontsize = 26,fontweight='bold')
ax.text(1.9,1.01,"$\mathrm{\mathsf{E}}_{t}[c_{t+1}/c_{t}]$",fontsize = 22,fontweight='bold')
ax.text(baseEx_inf.solution[0].mNrmSS,0.975, r'$\check{m}$', fontsize = 26,fontweight='bold')
ax.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
ax.text(1.9,0.998,r'$\Phi = (\mathrm{\mathsf{R}}\beta)^{1/\rho}$',fontsize = 22,fontweight='bold')
ax.text(1.9,1.03, r'$\Gamma$',fontsize = 22,fontweight='bold')
if Generator:
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.png'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.jpg'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.pdf'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.svg'))
```

### The Consumption Function Bounds

The next figure is also shown in [Analysis of the Converged Consumption Function](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Analysis-of-the-Converged-Consumption-Function), and illustrates theoretical bounds for the consumption function.

We define two useful variables: lower bound of $\kappa$ (marginal propensity to consume) and limit of $h$ (Human wealth), along with some functions such as limiting perfect foresight consumption functions ($\bar{c}(m)$), $\bar{\bar c}(m)$ and $\underline{c}(m)$.

```python
# Define k_lower, h_inf and perfect foresight consumption function, upper bound of consumption function and lower
# bound of consumption function.
k_lower = 1.0-(baseEx_inf.Rfree**(-1.0))*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)
h_inf = (1.0/(1.0-baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree))
conFunc_PF = lambda m: (h_inf -1)* k_lower + k_lower*m
conFunc_upper = lambda m: (1 - baseEx_inf.UnempPrb ** (1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)*m
conFunc_lower = lambda m: (1 -(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree) * m
intersect_m = ((h_inf-1)* k_lower)/((1 - baseEx_inf.UnempPrb
            **(1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)-k_lower)
```

```python
# Plot the consumption function and its bounds
x1 = np.linspace(0,25,1000)
x3 = np.linspace(0,intersect_m,300)
x4 = np.linspace(intersect_m,25,700)
cfunc_m = baseEx_inf.cFunc[0](x1)
cfunc_PF_1 = conFunc_PF(x3)
cfunc_PF_2 = conFunc_PF(x4)
cfunc_upper_1 = conFunc_upper(x3)
cfunc_upper_2 = conFunc_upper(x4)
cfunc_lower = conFunc_lower(x1)
plt.figure(figsize = (12,8))
plt.plot(x1,cfunc_m, color="black")
plt.plot(x1,cfunc_lower, color="black",linewidth=2.5)
plt.plot(x3,cfunc_upper_1, color="black",linewidth=2.5)
plt.plot(x4,cfunc_PF_2 , color="black",linewidth=2.5)
plt.plot(x4,cfunc_upper_2 , color="black",linestyle="--")
plt.plot(x3,cfunc_PF_1 , color="black",linestyle="--")
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.xlim(0,25)
plt.ylim(0,1.12*conFunc_PF(25))
plt.text(0,1.12*conFunc_PF(25)+0.05,"$c$",fontsize = 22)
plt.text(25+0.1,0,"$m$",fontsize = 22)
plt.text(2.5,1,r'$c(m)$',fontsize = 22,fontweight='bold')
plt.text(6,5,r'$\overline{\overline c}(m)= \overline{\kappa}m = (1-\wp^{1/\rho}\Phi_{R})m$',fontsize = 22,fontweight='bold')
plt.text(2.2,3.8, r'$\overline{c}(m) = (m-1+h)\underline{\kappa}$',fontsize = 22,fontweight='bold')
plt.text(9,4.1,r'Upper Bound $ = $ Min $[\overline{\overline c}(m),\overline{c}(m)]$',fontsize = 22,fontweight='bold')
plt.text(7,0.7,r'$\underline{c}(m)= (1-\Phi_{R})m = \underline{\kappa}m$',fontsize = 22,fontweight='bold')
plt.arrow(2.45,1.05,-0.5,0.02,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.15,3.88,-0.5,0.1,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(8.95,4.15,-0.8,0.05,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(5.95,5.05,-0.4,0,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(14,0.70,0.5,-0.1,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.svg'))
```

### The Consumption Function and Target $m$

This figure shows the $\mathrm{\mathbb{E}}_{t}[\Delta m_{t+1}]$ and consumption function $c(m_{t})$, along with the intrsection of these two functions, which defines the target value of $m$

```python
# This just plots objects that have already been constructed
m1 = np.linspace(0,4,1000)
cfunc_m = baseEx_inf.cFunc[0](m1)
mSSfunc = lambda m:(baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree)+(1.0-baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree)*m
mss = mSSfunc(m1)
plt.figure(figsize = (12,8))
plt.plot(m1,cfunc_m, color="black")
plt.plot(m1,mss, color="black")
plt.xlim(0,3)
plt.ylim(0,1.45)
plt.plot([baseEx_inf.solution[0].mNrmSS, baseEx_inf.solution[0].mNrmSS],[0,2.5],color="black",linestyle="--")
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(0,1.47,r"$c$",fontsize = 26)
plt.text(3.02,0,r"$m$",fontsize = 26)
plt.text(2.3,0.95,r'$\mathrm{\mathsf{E}}[\Delta m_{t+1}] = 0$',fontsize = 22,fontweight='bold')
plt.text(2.3,1.1,r"$c(m_{t})$",fontsize = 22,fontweight='bold')
plt.text(baseEx_inf.solution[0].mNrmSS,-0.05, r"$\check{m}$",fontsize = 26)
plt.arrow(2.28,1.12,-0.1,0.03,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.28,0.97,-0.1,0.02,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.svg'))
```

### Upper and Lower Limits of the Marginal Propensity to Consume

The paper shows that as $m_{t}~\uparrow~\infty$ the consumption function in the presence of risk gets arbitrarily close to the perfect foresight consumption function.  Defining $\underline{\kappa}$ as the perfect foresight model's MPC, this implies that $\lim_{m_{t}~\uparrow~\infty} c^{\prime}(m) = \underline{\kappa}$.

The paper also derives an analytical limit $\bar{\kappa}$ for the MPC as $m$ approaches 0., its bounding value.  Strict concavity of the consumption function implies that the consumption function will be everywhere below a function $\bar{\kappa}m$, and strictly declining everywhere.  The last figure plots the MPC between these two limits.

```python
# The last figure shows the upper and lower limits of the MPC
plt.figure(figsize = (12,8))
# Set the plot range of m
m = np.linspace(0.001,8,1000)

# Use the HARK method derivative to get the derivative of cFunc, and the values are just the MPC
MPC = baseEx_inf.cFunc[0].derivative(m)

# Define the upper bound of MPC
MPCUpper = (1 - baseEx_inf.UnempPrb ** (1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)

# Define the lower bound of MPC
MPCLower = k_lower

plt.plot(m,MPC,color = 'black')
plt.plot([0,8],[MPCUpper,MPCUpper],color = 'black')
plt.plot([0,8],[MPCLower,MPCLower],color = 'black')
plt.xlim(0,8)
plt.ylim(0,1)
plt.text(1.5,0.6,r'$\kappa(m) \equiv c^{\prime}(m)$',fontsize = 26,fontweight='bold')
plt.text(6,0.87,r'$(1-\wp^{1/\rho}\Phi_{R})\equiv \overline{\kappa}$',fontsize = 26,fontweight='bold')
plt.text(0.5,0.07,r'$\underline{\kappa}\equiv(1-\Phi_{R})$',fontsize = 26,fontweight='bold')
plt.text(8.05,0,"$m$",fontsize = 26)
plt.arrow(1.45,0.61,-0.4,0,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(1.7,0.07,0.2,-0.01,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(5.95,0.875,-0.2,0.03,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.svg'))
```