{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e8a9db5a",
   "metadata": {},
   "source": [
    "# Laffer Curves  with Adaptive Expectations"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b8f0123",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "This lecture studies stationary and dynamic **Laffer curves** in the inflation tax rate in a non-linear version of the model studied in this  lecture [Money Financed Government Deficits and Price Levels](https://intro.quantecon.org/money_inflation.html).\n",
    "\n",
    "As in the lecture [Money Financed Government Deficits and Price Levels](https://intro.quantecon.org/money_inflation.html), this lecture uses the log-linear version of the demand function for money that  [[Cagan, 1956](https://intro.quantecon.org/zreferences.html#id112)] used in his classic paper in place of the linear demand function used in this  lecture [Money Financed Government Deficits and Price Levels](https://intro.quantecon.org/money_inflation.html).\n",
    "\n",
    "But now, instead of assuming  ‘‘rational expectations’’ in the form of ‘‘perfect foresight’’,\n",
    "we’ll adopt the ‘‘adaptive expectations’’ assumption used by  [[Cagan, 1956](https://intro.quantecon.org/zreferences.html#id112)] and [[Friedman, 1956](https://intro.quantecon.org/zreferences.html#id183)].\n",
    "\n",
    "This means that instead of assuming that expected inflation $ \\pi_t^* $ is described by the “perfect foresight” or “rational expectations” hypothesis\n",
    "\n",
    "$$\n",
    "\\pi_t^* = p_{t+1} - p_t\n",
    "$$\n",
    "\n",
    "that we adopted in lectures [Money Financed Government Deficits and Price Levels](https://intro.quantecon.org/money_inflation.html) and lectures [Inflation Rate Laffer Curves](https://intro.quantecon.org/money_inflation_nonlinear.html), we’ll now assume that $ \\pi_t^* $ is determined by the adaptive expectations hypothesis described in equation [(31.4)](#equation-eq-adaptex)  reported below.\n",
    "\n",
    "We shall discover that changing our hypothesis about expectations formation in this way will change some our findings and leave others intact.  In particular, we shall discover that\n",
    "\n",
    "- replacing rational expectations with adaptive expectations leaves the two stationary inflation rates unchanged, but that $ \\ldots $  \n",
    "- it reverses the perverse dynamics by making the **lower** stationary inflation rate the one to which the system typically converges  \n",
    "- a more plausible comparative dynamic outcome emerges in which now inflation can be **reduced** by running **lower**  government deficits  \n",
    "\n",
    "\n",
    "These more plausible comparative dynamics underlie the “old time religion” that states that\n",
    "“inflation is always and everywhere caused by government deficits”.\n",
    "\n",
    "These issues were studied by [[Bruno and Fischer, 1990](https://intro.quantecon.org/zreferences.html#id296)].\n",
    "\n",
    "Their purpose was to reverse  what they thought were counter intuitive\n",
    "predictions of their model under rational expectations (i.e., perfect foresight in this context)\n",
    "by dropping rational expectations and instead assuming that people form  expectations about future inflation rates according to the “adaptive expectations”  scheme [(31.4)](#equation-eq-adaptex) described below.\n",
    "\n",
    ">**Note**\n",
    ">\n",
    ">[[Marcet and Sargent, 1989](https://intro.quantecon.org/zreferences.html#id297)]  had studied another way of selecting stationary equilibrium that involved replacing rational expectations with a model of  learning via least squares regression.\\\\\n",
    "\n",
    "\n",
    "[[Marcet and Nicolini, 2003](https://intro.quantecon.org/zreferences.html#id295)] and  [[Sargent *et al.*, 2009](https://intro.quantecon.org/zreferences.html#id294)] extended that work and applied it to study recurrent high-inflation episodes in Latin America."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2345736",
   "metadata": {},
   "source": [
    "## The model\n",
    "\n",
    "Let\n",
    "\n",
    "- $ m_t $ be the log of the money supply at the beginning of time $ t $  \n",
    "- $ p_t $ be the log of the price level at time $ t $  \n",
    "- $ \\pi_t^* $ be the public’s expectation of the rate of inflation between $ t $ and $ t+1 $  \n",
    "\n",
    "\n",
    "The law of motion of the money supply is\n",
    "\n",
    "\n",
    "<a id='equation-eq-ada-msupply'></a>\n",
    "$$\n",
    "\\exp(m_{t+1}) - \\exp(m_t) = g \\exp(p_t) \\tag{31.1}\n",
    "$$\n",
    "\n",
    "where $ g $ is the part of government expenditures financed by printing money.\n",
    "\n",
    "Notice that equation [(31.1)](#equation-eq-ada-msupply) implies that\n",
    "\n",
    "\n",
    "<a id='equation-eq-ada-msupply2'></a>\n",
    "$$\n",
    "m_{t+1} = \\log[ \\exp(m_t) + g \\exp(p_t)] \\tag{31.2}\n",
    "$$\n",
    "\n",
    "The demand function for money is\n",
    "\n",
    "\n",
    "<a id='equation-eq-ada-mdemand'></a>\n",
    "$$\n",
    "m_{t+1} - p_t = -\\alpha \\pi_t^* \\tag{31.3}\n",
    "$$\n",
    "\n",
    "where $ \\alpha \\geq 0 $.\n",
    "\n",
    "Expectations of inflation are governed by\n",
    "\n",
    "\n",
    "<a id='equation-eq-adaptex'></a>\n",
    "$$\n",
    "\\pi_{t}^* = (1-\\delta) (p_t - p_{t-1}) + \\delta \\pi_{t-1}^* \\tag{31.4}\n",
    "$$\n",
    "\n",
    "where $ \\delta \\in (0,1) $"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a17a750",
   "metadata": {},
   "source": [
    "## Computing an equilibrium sequence\n",
    "\n",
    "Equation the expressions for $ m_{t+1} $ provided  by [(31.3)](#equation-eq-ada-mdemand) and [(31.2)](#equation-eq-ada-msupply2) and use equation [(31.4)](#equation-eq-adaptex) to eliminate $ \\pi_t^* $ to obtain\n",
    "the following equation for $ p_t $:\n",
    "\n",
    "\n",
    "<a id='equation-eq-pequation'></a>\n",
    "$$\n",
    "\\log[ \\exp(m_t) + g \\exp(p_t)] - p_t = -\\alpha [(1-\\delta) (p_t - p_{t-1}) + \\delta \\pi_{t-1}^*] \\tag{31.5}\n",
    "$$\n",
    "\n",
    "**Pseudo-code**\n",
    "\n",
    "Here is the pseudo-code for our algorithm.\n",
    "\n",
    "Starting at time $ 0 $ with initial conditions $ (m_0, \\pi_{-1}^*, p_{-1}) $, for each $ t \\geq 0 $\n",
    "deploy the following steps in order:\n",
    "\n",
    "- solve [(31.5)](#equation-eq-pequation) for $ p_t $  \n",
    "- solve equation [(31.4)](#equation-eq-adaptex) for $ \\pi_t^* $  \n",
    "- solve equation [(31.2)](#equation-eq-ada-msupply2) for $ m_{t+1} $  \n",
    "\n",
    "\n",
    "This completes the algorithm."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57247a6a",
   "metadata": {},
   "source": [
    "## Claims or conjectures\n",
    "\n",
    "It will turn out that\n",
    "\n",
    "- if they exist, limiting values $ \\overline \\pi $ and $ \\overline \\mu $ will be equal  \n",
    "- if  limiting values exist, there are two possible limiting values, one high, one low  \n",
    "- unlike the outcome in lecture [Inflation Rate Laffer Curves](https://intro.quantecon.org/money_inflation_nonlinear.html), for almost all initial log price levels and expected inflation rates $ p_0, \\pi_{t}^* $, the limiting $ \\overline \\pi = \\overline \\mu $ is  the **lower** steady state  value  \n",
    "- for each of the two possible limiting values $ \\bar \\pi $ ,there is a unique initial log price level $ p_0 $ that implies that $ \\pi_t = \\mu_t = \\bar \\mu $ for all  $ t \\geq 0 $  \n",
    "  - this unique initial log price level solves $ \\log(\\exp(m_0) + g \\exp(p_0)) - p_0 = - \\alpha \\bar \\pi $  \n",
    "  - the preceding equation for $ p_0 $ comes from $ m_1 - p_0 = -  \\alpha \\bar \\pi $  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fecafdaf",
   "metadata": {},
   "source": [
    "## Limiting values of inflation rate\n",
    "\n",
    "As in our earlier lecture [Inflation Rate Laffer Curves](https://intro.quantecon.org/money_inflation_nonlinear.html), we can compute the two prospective limiting values for $ \\bar \\pi $ by studying the steady-state Laffer curve.\n",
    "\n",
    "Thus, in a  **steady state**\n",
    "\n",
    "$$\n",
    "m_{t+1} - m_t = p_{t+1} - p_t =  x \\quad \\forall t ,\n",
    "$$\n",
    "\n",
    "where $ x > 0 $ is a common rate of growth of logarithms of the money supply and price level.\n",
    "\n",
    "A few lines of algebra yields the following equation that $ x $ satisfies\n",
    "\n",
    "\n",
    "<a id='equation-eq-ada-steadypi'></a>\n",
    "$$\n",
    "\\exp(-\\alpha x) - \\exp(-(1 + \\alpha) x) = g \\tag{31.6}\n",
    "$$\n",
    "\n",
    "where we require that\n",
    "\n",
    "\n",
    "<a id='equation-eq-ada-revmax'></a>\n",
    "$$\n",
    "g \\leq \\max_{x: x \\geq 0} \\exp(-\\alpha x) - \\exp(-(1 + \\alpha) x) , \\tag{31.7}\n",
    "$$\n",
    "\n",
    "so that it is feasible to finance $ g $ by printing money.\n",
    "\n",
    "The left side of [(31.6)](#equation-eq-ada-steadypi) is steady state revenue raised by printing money.\n",
    "\n",
    "The right side of [(31.6)](#equation-eq-ada-steadypi) is the quantity  of time $ t $ goods  that the government raises by printing money.\n",
    "\n",
    "Soon  we’ll plot  the left and right sides of equation [(31.6)](#equation-eq-ada-steadypi).\n",
    "\n",
    "But first we’ll write code that computes a steady-state\n",
    "$ \\bar \\pi $.\n",
    "\n",
    "Let’s start by importing some  libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15bc1c2a",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "from collections import namedtuple\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.ticker import MaxNLocator\n",
    "from matplotlib.cm import get_cmap\n",
    "from matplotlib.colors import to_rgba\n",
    "import matplotlib\n",
    "from scipy.optimize import root, fsolve"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61242e4e",
   "metadata": {},
   "source": [
    "Let’s create a `namedtuple` to store the parameters of the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1088762",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "LafferAdaptive = namedtuple('LafferAdaptive', \n",
    "                        [\"m0\",  # log of the money supply at t=0\n",
    "                         \"α\",   # sensitivity of money demand\n",
    "                         \"g\",   # government expenditure\n",
    "                         \"δ\"])\n",
    "\n",
    "# Create a Cagan Laffer model\n",
    "def create_model(α=0.5, m0=np.log(100), g=0.35, δ=0.9):\n",
    "    return LafferAdaptive(α=α, m0=m0, g=g, δ=δ)\n",
    "\n",
    "model = create_model()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb87741a",
   "metadata": {},
   "source": [
    "Now we write code that computes steady-state $ \\bar \\pi $s."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84984952",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "# Define formula for π_bar\n",
    "def solve_π(x, α, g):\n",
    "    return np.exp(-α * x) - np.exp(-(1 + α) * x) - g\n",
    "\n",
    "def solve_π_bar(model, x0):\n",
    "    π_bar = fsolve(solve_π, x0=x0, xtol=1e-10, args=(model.α, model.g))[0]\n",
    "    return π_bar\n",
    "\n",
    "# Solve for the two steady state of π\n",
    "π_l = solve_π_bar(model, x0=0.6)\n",
    "π_u = solve_π_bar(model, x0=3.0)\n",
    "print(f'The two steady state of π are: {π_l, π_u}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8647527a",
   "metadata": {},
   "source": [
    "We find two steady state $ \\bar \\pi $ values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9be353b5",
   "metadata": {},
   "source": [
    "## Steady-state Laffer curve\n",
    "\n",
    "The following figure plots the steady-state Laffer curve together with the two stationary inflation rates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d32bcc73",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "def compute_seign(x, α):\n",
    "    return np.exp(-α * x) - np.exp(-(1 + α) * x) \n",
    "\n",
    "def plot_laffer(model, πs):\n",
    "    α, g = model.α, model.g\n",
    "    \n",
    "    # Generate π values\n",
    "    x_values = np.linspace(0, 5, 1000)\n",
    "\n",
    "    # Compute corresponding seigniorage values for the function\n",
    "    y_values = compute_seign(x_values, α)\n",
    "\n",
    "    # Plot the function\n",
    "    plt.plot(x_values, y_values, \n",
    "            label=f'$exp((-{α})x) - exp(- (1- {α}) x)$')\n",
    "    for π, label in zip(πs, ['$\\pi_l$', '$\\pi_u$']):\n",
    "        plt.text(π, plt.gca().get_ylim()[0]*2, \n",
    "                 label, horizontalalignment='center',\n",
    "                 color='brown', size=10)\n",
    "        plt.axvline(π, color='brown', linestyle='--')\n",
    "    plt.axhline(g, color='red', linewidth=0.5, \n",
    "                linestyle='--', label='g')\n",
    "    plt.xlabel('$\\pi$')\n",
    "    plt.ylabel('seigniorage')\n",
    "    plt.legend()\n",
    "    plt.grid(True)\n",
    "    plt.show()\n",
    "\n",
    "# Steady state Laffer curve\n",
    "plot_laffer(model, (π_l, π_u))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cdb0041",
   "metadata": {},
   "source": [
    "## Associated initial price levels\n",
    "\n",
    "Now that we have our hands on the two possible steady states, we can compute two initial log price levels $ p_{-1} $, which as initial conditions, imply that $ \\pi_t = \\bar \\pi $ for all $ t \\geq 0 $.\n",
    "\n",
    "In particular, to initiate a fixed point of the dynamic Laffer curve dynamics, we set\n",
    "\n",
    "$$\n",
    "p_{-1} = m_0 + \\alpha \\pi^*\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ddaba3ad",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "def solve_p_init(model, π_star):\n",
    "    m0, α = model.m0, model.α\n",
    "    return m0 + α*π_star\n",
    "\n",
    "\n",
    "# Compute two initial price levels associated with π_l and π_u\n",
    "p_l, p_u = map(lambda π: solve_p_init(model, π), (π_l, π_u))\n",
    "print('Associated initial p_{-1}s', f'are: {p_l, p_u}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19d5ad5d",
   "metadata": {},
   "source": [
    "### Verification\n",
    "\n",
    "To start, let’s write some code to verify that if we initial  $ \\pi_{-1}^*,p_{-1} $ appropriately, the inflation rate $ \\pi_t $ will be constant for all $ t \\geq 0 $ (at either $ \\pi_u $ or $ \\pi_l $ depending on the initial condition)\n",
    "\n",
    "The following code verifies this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca237668",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "def solve_laffer_adapt(p_init, π_init, model, num_steps):\n",
    "    m0, α, δ, g = model.m0, model.α, model.δ, model.g\n",
    "    \n",
    "    m_seq = np.nan * np.ones(num_steps+1) \n",
    "    π_seq = np.nan * np.ones(num_steps) \n",
    "    p_seq = np.nan * np.ones(num_steps)\n",
    "    μ_seq = np.nan * np.ones(num_steps) \n",
    "    \n",
    "    m_seq[1] = m0\n",
    "    π_seq[0] = π_init\n",
    "    p_seq[0] = p_init\n",
    "        \n",
    "    for t in range(1, num_steps):\n",
    "        # Solve p_t\n",
    "        def p_t(pt):\n",
    "            return np.log(np.exp(m_seq[t]) + g * np.exp(pt)) \\\n",
    "                          - pt + α * ((1-δ)*(pt - p_seq[t-1]) + δ*π_seq[t-1])\n",
    "        \n",
    "        p_seq[t] = root(fun=p_t, x0=p_seq[t-1]).x[0]\n",
    "        \n",
    "        # Solve π_t\n",
    "        π_seq[t] = (1-δ) * (p_seq[t]-p_seq[t-1]) + δ*π_seq[t-1]\n",
    "        \n",
    "        # Solve m_t\n",
    "        m_seq[t+1] = np.log(np.exp(m_seq[t]) + g*np.exp(p_seq[t]))\n",
    "        \n",
    "        # Solve μ_t\n",
    "        μ_seq[t] = m_seq[t+1] - m_seq[t]\n",
    "    \n",
    "    return π_seq, μ_seq, m_seq, p_seq"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d194524a",
   "metadata": {},
   "source": [
    "Compute limiting values starting from $ p_{-1} $ associated with $ \\pi_l $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc3ac2d4",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p_l, π_l, model, 50)\n",
    "\n",
    "# Check steady state m_{t+1} - m_t and p_{t+1} - p_t \n",
    "print('m_{t+1} - m_t:', m_seq[-1] - m_seq[-2])\n",
    "print('p_{t+1} - p_t:', p_seq[-1] - p_seq[-2])\n",
    "\n",
    "# Check if exp(-αx) - exp(-(1 + α)x) = g\n",
    "eq_g = lambda x: np.exp(-model.α * x) - np.exp(-(1 + model.α) * x)\n",
    "\n",
    "print('eq_g == g:', np.isclose(eq_g(m_seq[-1] - m_seq[-2]), model.g))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0a966d1",
   "metadata": {},
   "source": [
    "Compute limiting values starting from $ p_{-1} $ associated with $ \\pi_u $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b1d973d",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p_u, π_u, model, 50)\n",
    "\n",
    "# Check steady state m_{t+1} - m_t and p_{t+1} - p_t \n",
    "print('m_{t+1} - m_t:', m_seq[-1] - m_seq[-2])\n",
    "print('p_{t+1} - p_t:', p_seq[-1] - p_seq[-2])\n",
    "\n",
    "# Check if exp(-αx) - exp(-(1 + α)x) = g\n",
    "eq_g = lambda x: np.exp(-model.α * x) - np.exp(-(1 + model.α) * x)\n",
    "\n",
    "print('eq_g == g:', np.isclose(eq_g(m_seq[-1] - m_seq[-2]), model.g))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f0adefd",
   "metadata": {},
   "source": [
    "## Slippery side of Laffer curve dynamics\n",
    "\n",
    "We are now equipped  to compute  time series starting from different $ p_{-1}, \\pi_{-1}^* $ settings, analogous to those in this lecture  [Money Financed Government Deficits and Price Levels](https://intro.quantecon.org/money_inflation.html) and this lecture  [Inflation Rate Laffer Curves](https://intro.quantecon.org/money_inflation_nonlinear.html).\n",
    "\n",
    "Now we’ll study how outcomes unfold when we start $ p_{-1}, \\pi_{-1}^* $ away from a stationary point of the dynamic Laffer curve, i.e., away from either $ \\pi_u $ or $ \\pi_l $.\n",
    "\n",
    "To construct a perturbation pair $ \\check p_{-1}, \\check \\pi_{-1}^* $we’ll implement the following pseudo code:\n",
    "\n",
    "- set $ \\check \\pi_{-1}^* $ not equal to one of the stationary points $ \\pi_u $ or $ \\pi_l $.  \n",
    "- set $ \\check p_{-1} = m_0 + \\alpha \\check \\pi_{-1}^* $  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fdca0ab8",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "def draw_iterations(π0s, model, line_params, π_bars, num_steps):\n",
    "    fig, axes = plt.subplots(4, 1, figsize=(8, 12), sharex=True)\n",
    "\n",
    "    for ax in axes[:2]:\n",
    "        ax.set_yscale('log')\n",
    "        \n",
    "    for i, π0 in enumerate(π0s):\n",
    "        p0 = model.m0 + model.α*π0\n",
    "        π_seq, μ_seq, m_seq, p_seq = solve_laffer_adapt(p0, π0, model, num_steps)\n",
    "\n",
    "        axes[0].plot(np.arange(num_steps), m_seq[1:], **line_params)\n",
    "        axes[1].plot(np.arange(-1, num_steps-1), p_seq, **line_params)\n",
    "        axes[2].plot(np.arange(-1, num_steps-1), π_seq, **line_params)\n",
    "        axes[3].plot(np.arange(num_steps), μ_seq, **line_params)\n",
    "            \n",
    "    axes[2].axhline(y=π_bars[0], color='grey', linestyle='--', lw=1.5, alpha=0.6)\n",
    "    axes[2].axhline(y=π_bars[1], color='grey', linestyle='--', lw=1.5, alpha=0.6)\n",
    "    axes[2].text(num_steps * 1.07, π_bars[0], '$\\pi_l$', verticalalignment='center', \n",
    "                     color='grey', size=10)\n",
    "    axes[2].text(num_steps * 1.07, π_bars[1], '$\\pi_u$', verticalalignment='center', \n",
    "                         color='grey', size=10)\n",
    "\n",
    "    axes[0].set_ylabel('$m_t$')\n",
    "    axes[1].set_ylabel('$p_t$')\n",
    "    axes[2].set_ylabel('$\\pi_t$')\n",
    "    axes[3].set_ylabel('$\\mu_t$')\n",
    "    axes[3].set_xlabel('timestep')\n",
    "    axes[3].xaxis.set_major_locator(MaxNLocator(integer=True))\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd3c3077",
   "metadata": {},
   "source": [
    "Let’s simulate the result generated by varying the initial $ \\pi_{-1} $ and corresponding $ p_{-1} $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc74962e",
   "metadata": {
    "hide-output": false
   },
   "outputs": [],
   "source": [
    "πs = np.linspace(π_l, π_u, 10)\n",
    "\n",
    "line_params = {'lw': 1.5, \n",
    "              'marker': 'o',\n",
    "              'markersize': 3}\n",
    "              \n",
    "π_bars = (π_l, π_u)\n",
    "draw_iterations(πs, model, line_params, π_bars, num_steps=80)"
   ]
  }
 ],
 "metadata": {
  "date": 1736815198.8830516,
  "filename": "laffer_adaptive.md",
  "kernelspec": {
   "display_name": "Python",
   "language": "python3",
   "name": "python3"
  },
  "title": "Laffer Curves  with Adaptive Expectations"
 },
 "nbformat": 4,
 "nbformat_minor": 5
}