Skip to content

Commit

Permalink
Release 0.7.1 (#388)
Browse files Browse the repository at this point in the history
* Increment version number

* [FIX] Axon map in Argus plotters (#366)

* fix axon map in Argus plotters

* fix typo

* fix more typos

* [FIX] Axon map plot for left eyes (#367)

* [DOC] Update release notes

* [FIX] Show warning when Scoreboard / AxonMap models are used with implant.z > 0 (#368)

* [ENH] Speed up AxonMap build (#369)

* replace np.insert with np.concatenate

* Update release notes

* [FIX] Axon map usability (#370)

* Raise ValueError when lambda is too small

* Add note in the docs about resetting parameters

* Fix axon map docs, refactor axon methods

* remove axon map legacy build

* Update release notes

* [ENH] Update Nanduri2012 data (#376)

* updated nanduri dataset

* Updated / added test cases

* Add docstring for new column

* Fixed docstring for new column

* [ENH] Speed up ElectrodeArray.plot() (#375)

* speed up implant.plot

* Update release notes

* update test

* [ENH] Add FadingTemporal model (#378)

* Add FadingTemporal model

* Fix docs and release notes

* [FIX] Percept plotting and animation (#379)

* fix plot method

* remove get_interval, use utils.unique instead

* Fix Matplotlib deprecation error

* [FIX] BVT24 electrode naming convention (#380)

* [ENH] Various performance enhancements (#382)

* speed up stim.compress()

* Speed up alphabetic electrode numbering

* fix typo

* fix contiguous error

* add new cython submodule

* refactor Nanduri Cython

* Refactor Horsager Cython

* Refactor Beyeler Cython

* add cumpow function

* [ENH] Various implant upgrades (#383)

* Update earray indexing

* fix is_charge_balanced

* add easy conversion from ImageStimulus, VideoStimulus to implant.stim

* Activate/deactivate electrodes

* [DOC] Fix CircleElectrodeArray

* [FIX] BarStimulus, GratingStimulus docs (#384)

* [ENH] Various stimulus upgrades (#385)

* Speed up VideoStimulus init

* Fix rgb2gray for a video that's already grayscale

* Speed up Stimulus init

* replace np.isclose with math.isclose for speedup

* Switch monotonic ValueError to warning

* Add video.trim()

* [FIX] Order of display items in implant plots (#386)

* add constants; fix np.bool deprecation warning

* adjust alpha of electrode plots

* Don't expose constants at the module level

* [FIX] plot_sinusoidal example

* [FIX] Add docstrings for constants

* Prepare Release 0.7.1 (#387)

* Prepare Release 0.7.1

* Add MANIFEST.in

Co-authored-by: Jacob Granley <jgranley@ucsb.edu>
  • Loading branch information
mbeyeler and jgranley committed Jun 22, 2021
1 parent 63b9a5e commit 280178a
Show file tree
Hide file tree
Showing 78 changed files with 2,072 additions and 829 deletions.
3 changes: 3 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,3 @@
include AUTHORS LICENSE CONTRIBUTING* CODE_OF_CONDUCT* Makefile* MANIFEST.in setup* README.*
recursive-include doc *
recursive-include examples *
24 changes: 23 additions & 1 deletion doc/topics/implants.rst
Expand Up @@ -51,6 +51,28 @@ A mathematical model is then used to compute the neural stimulus response and
predict the resulting visual percept
(see :ref:`Computational Models <topics-models>`).

Understanding the coordinate system
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The easiest way to understand the coordinate system is to look at the
organization of the optic fiber layer:

.. ipython:: python
from pulse2percept.models import AxonMapModel
AxonMapModel(eye='RE').plot()
Here you can see that:

* the coordinate system is centered on the fovea
* in a right eye, positive :math:`x` values correspond to the nasal retina
* in a right eye, positive :math:`y` values correspond to the superior retina

Positive :math:`z` values move an electrode away from the retina into the
vitreous humor (:math:`z` is sometimes called electrode-retina distance).
Analogously, negative :math:`z` values move an electrode through the different
retinal layers towards the outer retina.

Understanding the ProsthesisSystem class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -91,7 +113,7 @@ prosthesis system is a Python dictionary:
.. ipython:: python
from pulse2percept.implants import ArgusI
for name, electrode in ArgusI().items():
for name, electrode in ArgusI().electrodes.items():
print(name, electrode)
Expand Down
1 change: 1 addition & 0 deletions doc/topics/models.rst
Expand Up @@ -21,6 +21,7 @@ pulse2percept provides the following computational models:
================ ========================= ===================
Reference Model Type
---------------- ------------------------- -------------------
generic `FadingTemporal` temporal
[Horsager2009]_ `Horsager2009Model` temporal
[Horsager2009]_ `Horsager2009Temporal` temporal
[Nanduri2012]_ `Nanduri2012Model` spatial + temporal
Expand Down
35 changes: 32 additions & 3 deletions doc/users/release_notes.rst
Expand Up @@ -9,8 +9,37 @@ Release Notes
**pulse2percept 0.6 was the last version to support Python <= 3.5.**
pulse2percept 0.7+ requires Python 3.6+.

v0.7.0 (2021, planned)
----------------------
v0.7.1 (2021-06-21)
-------------------

New features
~~~~~~~~~~~~

* Add :py:class:`~pulse2percept.models.FadingTemporal`, a generic phosphene fading model (:pull:`378`)

Maintenance
~~~~~~~~~~~

* Various implant usability and speed upgrades (:pull:`375`, :pull:`382`, :pull:`383`, :pull:`386`)
* Various stimulus usability and speed upgrades (:pull:`382`, :pull:`383`, :pull:`384`, :pull:`385`)
* Refactor common Cython functions and move them into a ``utils._fast_math`` submodule (:pull:`382`)
* Further speed up the :py:class:`~pulse2percept.models.AxonMapModel` build process (:pull:`369`)
* Improve documentation and usability of various :py:class:`~pulse2percept.models.AxonMapModel` methods (:pull:`370`)
* Disallow lambda<10 for :py:class:`~pulse2percept.models.AxonMapModel` (:pull:`370`)
* Show a warning when :py:class:`~pulse2percept.models.ScoreboardModel` or
:py:class:`~pulse2percept.models.AxonMapModel` is used with a nonzero electrode-retina distance (:pull:`368`)

Bug fixes
~~~~~~~~~

* Fix naming convention for :py:class:`~pulse2percept.implants.BVT24` electrodes (:pull:`380`)
* Fix issues with plotting and animating :py:class:`~pulse2percept.percepts.Percept` (:pull:`379`)
* Fix inconsistencies and missing parameters in the [Nanduri2012]_ dataset (:pull:`376`)
* Fix :py:meth:`pulse2percept.models.AxonMapModel.plot` for left eyes (:pull:`367`)
* Fix axon map visualization in :py:meth:`~pulse2percept.viz.plot_argus_phosphenes` (:pull:`366`)

v0.7.0 (2021-04-04)
-------------------

Highlights
~~~~~~~~~~
Expand Down Expand Up @@ -68,7 +97,7 @@ Backward-incompatible changes
Deprecations
^^^^^^^^^^^^

* ``plot_axon_map``: Use :py:meth`pulse2percept.models.AxonMapModel.plot`
* ``plot_axon_map``: Use :py:meth:`pulse2percept.models.AxonMapModel.plot`
* ``plot_implant_on_axon_map``: Use
:py:meth:`pulse2percept.implants.ProsthesisSystem.plot` on top of
:py:meth`pulse2percept.models.AxonMapModel.plot`
Expand Down
4 changes: 3 additions & 1 deletion examples/implants/plot_custom_electrode_array.py
Expand Up @@ -78,7 +78,7 @@ def __init__(self, n_electrodes, radius, x_center, y_center):
"""
# The job of the constructor is to create the electrodes. We start
# with an empty collection:
self.electrodes = coll.OrderedDict()
self._electrodes = coll.OrderedDict()
# We then generate a number `n_electrodes` of electrodes, arranged on
# the circumference of a circle:
for n in range(n_electrodes):
Expand All @@ -97,6 +97,7 @@ def __init__(self, n_electrodes, radius, x_center, y_center):
# To use the new class, we need to specify all input arguments and pass them
# to the constructor:


n_electrodes = 10
radius = 1000 # radius in microns
x_center = 0 # x-coordinate of circle center (microns)
Expand Down Expand Up @@ -158,6 +159,7 @@ def remove(self, name):
# including its constructor. So the following line will create the same
# electrode array as above:


flex_earray = FlexibleCircleElectrodeArray(
n_electrodes, radius, x_center, y_center)
print(flex_earray)
Expand Down
8 changes: 4 additions & 4 deletions examples/implants/plot_electrode_grid.py
Expand Up @@ -40,6 +40,9 @@
"""
# sphinx_gallery_thumbnail_number = 3
from pulse2percept.models import AxonMapModel
from numpy import pi
from pulse2percept.implants import DiskElectrode
from pulse2percept.implants import ElectrodeGrid

grid = ElectrodeGrid((2, 3), 500)
Expand Down Expand Up @@ -69,15 +72,14 @@
##############################################################################
# We can iterate over all electrodes as if we were dealing with a dictionary:

for name, electrode in grid.items():
for name, electrode in grid.electrodes.items():
print(name, electrode)

##############################################################################
# To make a grid of :py:class:`~pulse2percept.implants.DiskElectrode` objects,
# we need to explicitly specify the electrode type (``etype``) and the radius
# to use (``r``):

from pulse2percept.implants import DiskElectrode

# 11x13 grid, 100-um disk electrodes spaced 500um apart:
disk_grid = ElectrodeGrid((11, 13), 500, etype=DiskElectrode, r=100)
Expand Down Expand Up @@ -111,7 +113,6 @@
# z=150um away from the retinal surface, and rotates it clockwise by 45 degrees
# (note the minus sign):

from numpy import pi
offset_grid = ElectrodeGrid((11, 13), 500, type='hex', x=-600, y=200, z=150,
rot=-45, etype=DiskElectrode, r=100)

Expand All @@ -123,7 +124,6 @@
#
# We can also plot the grid on top of a map of retinal nerve fiber bundles:

from pulse2percept.models import AxonMapModel

AxonMapModel().plot()
offset_grid.plot()
Expand Up @@ -39,14 +39,16 @@
:py:class:`~pulse2percept.models.AxonMapModel` class by calling its
constructor method.
The two most important parameters to set are ``rho`` and ``axlambda`` from
the equation above (here set to 200 micrometers and 500 micrometers,
the equation above (here set to 150 micrometers and 500 micrometers,
respectively):
"""
# sphinx_gallery_thumbnail_number = 2

import numpy as np
from pulse2percept.implants import ArgusII
from pulse2percept.models import AxonMapModel
model = AxonMapModel(rho=200, axlambda=500)
model = AxonMapModel(rho=150, axlambda=500)

##############################################################################
# Parameters you don't specify will take on default values. You can inspect
Expand Down Expand Up @@ -111,12 +113,16 @@
model.build()

##############################################################################
# .. note::
# .. important ::
#
# You need to build a model only once. After that, you can apply any number
# of stimuli -- or even apply the model to different implants -- without
# having to rebuild (which takes time).
#
# However, if you change important model parameters outside the constructor
# (e.g., by directly setting ``model.axlambda = 100``), you will have to
# call ``model.build()`` again for your changes to take effect.
#
# Assigning a stimulus
# --------------------
# The second step is to specify a visual prosthesis from the
Expand All @@ -127,7 +133,6 @@
# will be centered over the fovea (at x=0, y=0) and aligned with the horizontal
# meridian (rot=0):

from pulse2percept.implants import ArgusII
implant = ArgusII()

##############################################################################
Expand All @@ -149,7 +154,6 @@
# For example, the following sends 1 microamp to all 60 electrodes of the
# implant:

import numpy as np
implant.stim = np.ones(60)

##############################################################################
Expand Down Expand Up @@ -199,3 +203,22 @@
# (upper) visual field, a counterclockwise orientation on the retina is
# equivalent to a clockwise orientation of the percept in visual field
# coordinates.

##############################################################################
# You can also use the axon map model to imitate
# :py:class:`~pulse2percept.models.ScoreboardModel` by setting lambda to a small
# value.
# However, you may have to increase the number of axons and number of segments
# per axon to get a smooth percept out:

model = AxonMapModel(rho=200, axlambda=10, n_axons=3000, n_ax_segments=3000)
model.build()
percept = model.predict_percept(implant)
ax = percept.plot()
ax.set_title('Predicted percept')

##############################################################################
# This is of course not very computationally efficient, because the model is
# still performing all the axon map calculations.
# In this case, you might be better off using
# :py:class:`~pulse2percept.models.ScoreboardModel`.
File renamed without changes.
7 changes: 4 additions & 3 deletions examples/stimuli/plot_sinusoidal.py
Expand Up @@ -16,11 +16,12 @@
"""
# sphinx_gallery_thumbnail_number = 4

from pulse2percept.utils.constants import DT
from pulse2percept.stimuli import Stimulus
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0, 2 * np.pi, 100)
x = np.sin(t)
Expand All @@ -47,7 +48,6 @@
# We can turn this signal into a :py:class:`~pulse2percept.stimuli.Stimulus`
# object as follows:

from pulse2percept.stimuli import Stimulus, DT

stim = Stimulus(10 * data.reshape((1, -1)), time=t)
stim.plot()
Expand Down Expand Up @@ -92,5 +92,6 @@ def __init__(self, amp, freq, phase, stim_dur, n_levels=5, dt=DT):
##############################################################################
# Then we can create a new pulse as follows:


sine = SinusoidalPulse(26, 0.25, -np.pi / 4, 20)
sine.plot()
8 changes: 4 additions & 4 deletions pulse2percept/datasets/beyeler2019.py
Expand Up @@ -123,7 +123,7 @@ def fetch_beyeler2019(subjects=None, electrodes=None, data_path=None,
# Images need special treatment:
# - Direct assign confuses Pandas, need a loop
# - Save as black/white boolean
df['image'] = [img.astype(np.bool) for img in f[key]]
df['image'] = [img.astype(bool) for img in f[key]]
else:
df[col] = f[key]
dfs.append(df)
Expand Down Expand Up @@ -154,18 +154,18 @@ def fetch_beyeler2019(subjects=None, electrodes=None, data_path=None,
pass

# Select subset of data:
idx = np.ones_like(df.index, dtype=np.bool)
idx = np.ones_like(df.index, dtype=bool)
if subjects is not None:
if isinstance(subjects, str):
subjects = [subjects]
idx_subject = np.zeros_like(df.index, dtype=np.bool)
idx_subject = np.zeros_like(df.index, dtype=bool)
for subject in subjects:
idx_subject |= df.subject == subject
idx &= idx_subject
if electrodes is not None:
if isinstance(electrodes, str):
electrodes = [electrodes]
idx_electrode = np.zeros_like(df.index, dtype=np.bool)
idx_electrode = np.zeros_like(df.index, dtype=bool)
for electrode in electrodes:
idx_electrode |= df.electrode == electrode
idx &= idx_electrode
Expand Down

0 comments on commit 280178a

Please sign in to comment.