{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Beyeler et al. (2019): Focal percepts with the scoreboard model\n\nThis example shows how to apply the\n:py:class:`~pulse2percept.models.ScoreboardModel` to a\n:py:class:`~pulse2percept.implants.PRIMA75` implant.\n\nThe scoreboard model is a standard baseline model of retinal prosthesis\nstimulation, which assumes that electrical stimulation leads to the percept\nof focal dots of light, centered over the visual field location associated with\nthe stimulated retinal field location $(x_{stim}, y_{stim})$, whose\nspatial intensity decays with a Gaussian profile [Hayes2003]_, [Thompson2003]_:\n\n\\begin{align}I_{score}(x,y; \\rho) = \\exp \\Big(\n    -\\frac{(x-x_{stim})^2 + (y-y_{stim})^2}{2 \\rho^2} \\Big)\\end{align}\n\nwhere $\\rho$ is the spatial decay constant.\n\n.. important::\n\n    As pointed out by [Beyeler2019]_, the scoreboard model does not well\n    account for percepts generated by **epiretinal** implants, where\n    incidental stimulation of retinal nerve fiber bundles leads to elongated,\n    'streaky' percepts.\n\n    In that case, use :py:class:`~pulse2percept.models.AxonMapModel` instead.\n\nThe scoreboard model can be instantiated and run in three simple steps.\n\n## Creating the model\n\nThe first step is to instantiate the\n:py:class:`~pulse2percept.models.ScoreboardModel` class by calling its\nconstructor method.\n\nThe model simulates a patch of the visual field specified by ``xrange`` and\n``yrange`` (in degrees of visual angle), sampled at a step size of ``xystep``.\nThe grid that is created from these parameters is equivalent to calling NumPy's\n``linspace(xrange[0], xrange[1], num=xystep)``.\n\nBy default, this patch is quite large, spanning 30deg x 30deg.\nBecause PRIMA is rather small, we want to reduce the window size to 6deg x 6deg\nand sample it at 0.05deg resolution:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pulse2percept.models import ScoreboardModel\nmodel = ScoreboardModel(xrange=(-3, 3), yrange=(-3, 3), xystep=0.05)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Parameters you don't specify will take on default values. You can inspect\nall current model parameters as follows:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "print(model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "This reveals a number of other parameters to set, such as:\n\n* ``xrange``, ``yrange``: the extent of the visual field to be simulated,\n  specified as a range of x and y coordinates (in degrees of visual angle,\n  or dva). For example, we are currently sampling x values between -20 dva\n  and +20dva, and y values between -15 dva and +15 dva.\n* ``xystep``: The resolution (in dva) at which to sample the visual field.\n  For example, we are currently sampling at 0.25 dva in both x and y\n  direction.\n* ``thresh_percept``: You can also define a brightness threshold, below which\n  the predicted output brightness will be zero. It is currently set to\n  ``1/sqrt(e)``, because that will make the radius of the predicted percept\n  equal to ``rho``.\n\nTo change parameter values, either pass them directly to the constructor\nabove or set them by hand, like this:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "model.rho = 20"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Then build the model. This is a necessary step before you can actually use\nthe model to predict a percept, as it performs a number of expensive setup\ncomputations (e.g., building the spatial reference frame, calculating\nelectric potentials):\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "model.build()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\"><h4>Note</h4><p>You need to build a model only once. After that, you can apply any number\n    of stimuli -- or even apply the model to different implants -- without\n    having to rebuild (which takes time).</p></div>\n\n## Assigning a stimulus\nThe second step is to specify a visual prosthesis from the\n:py:mod:`~pulse2percept.implants` module.\n\nIn the following, we will create an\n:py:class:`~pulse2percept.implants.PRIMA75` implant. By default, the implant\nwill be centered over the fovea (at x=0, y=0) and aligned with the horizontal\nmeridian (rot=0):\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pulse2percept.implants import PRIMA75\nimplant = PRIMA75()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can visualize the implant and verify that we are simulating the correct\npatch of retina as follows:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "model.plot()\nimplant.plot()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The gray window indicates the extent of the grid that was created during\n``model.build()`` using the values specified by ``xrange``, ``yrange``, and\n``xystep``. As we can see, the window well-covers the implant that we want\nto simulate.\n\nThe easiest way to assign a stimulus to the implant is to pass a NumPy array\nthat specifies the current amplitude to be applied to every electrode in the\nimplant.\n\nFor example, the following sends 10 microamps to all 142 electrodes of the\nimplant:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\nimplant.stim = 10 * np.ones(142)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\"><h4>Note</h4><p>Some models can handle stimuli that have both a spatial and a temporal\n    component. The scoreboard model cannot.</p></div>\n\n## Predicting the percept\n\nThe third step is to apply the model to predict the percept resulting from\nthe specified stimulus. Note that this may take some time on your machine:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "percept = model.predict_percept(implant)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "The resulting percept is stored in a\n:py:class:`~pulse2percept.percepts.Percept` object, which is similar in\norganization to the :py:class:`~pulse2percept.stimuli.Stimulus` object:\nthe ``data`` container is a 3D NumPy array (Y, X, T) with labeled axes\n``xdva``, ``ydva``, and ``time``.\n\nThe percept can be plotted as follows:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "ax = percept.plot()\nax.set_title('Predicted percept')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\"><h4>Note</h4><p>Stimulation of the inferior retina leads to percepts appearing in the\n    upper visual field. This is why the percept (with axes showing the visual\n    field in degrees of visual angle) appears upside down with respect to\n    the earlier figure of the implant (with axes showing retinal coordinates\n    in microns).</p></div>\n\nBy default, the :py:meth:`~pulse2percept.percepts.Percept.plot` method uses\nMatplotlib's ``pcolor`` function.\nHowever, it can also be configured to use a hex grid (using Matplotlib's\n``hexbin`` function). Additional parameters can be passed to ``hexbin`` as\nkeyword arguments of :py:meth:`~pulse2percept.percepts.Percept.plot`:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "percept = model.predict_percept(implant)\npercept.plot(kind='hex', cmap='inferno')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Addressing individual electrodes\n\nAlternatively, we can address an individual electrode. To show the electrode\nnames, the implant can be plotted with ``annotate=True``:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "implant.plot(annotate=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "This makes it easier to pick an electrode; e.g., F7:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "implant.stim = {'F7': 10}\n\npercept = model.predict_percept(implant)\n\npercept.plot()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}