.. _users-release-notes:

=============
Release Notes
=============

v0.11.0 Foundations (unreleased)
================================

Highlights
----------

* **Backwards-incompatible API overhaul.** Retinal and cortical implants,
  models, and topography now live under explicit `retina` and `cortex`
  namespaces. Models are constructed with an implant, build automatically, and
  take the stimulus directly in `predict_percept`. Implant placement moved
  from device constructors to model parameters, so named implant geometry now
  remains device-local (:pull:`862`, :pull:`879`, :pull:`880`, :pull:`884`,
  :pull:`887`).

  A typical workflow changes from:

  .. code-block:: python

      # v0.10
      implant = p2p.implants.ArgusII(x=1000, y=-500, rot=15)
      model = p2p.models.AxonMapModel()
      model.build()
      percept = model.predict_percept(implant, stim)

  to:

  .. code-block:: python

      # v0.11
      implant = p2p.implants.retina.ArgusII()
      model = p2p.models.retina.AxonMapModel(
          implant,
          implant_position=(1000, -500),
          implant_rotation=15,
      )
      percept = model.predict_percept(stim)

* Added :py:mod:`pulse2percept.vision` for composing scenes, scotomas,
  residual vision, and prosthetic percepts in visual-field coordinates,
  including binocular scenes (:pull:`854`, :pull:`871`, :pull:`883`,
  :pull:`890`, :pull:`899`, :pull:`901`).

* Added a photovoltaic stimulation pipeline for PRIMA-style arrays, from image
  encoding through irradiance-based stimulation to the new
  :py:class:`~pulse2percept.models.retina.Ho2018Model`
  (:pull:`868`, :pull:`891`, :pull:`903`).


API changes and improvements
----------------------------

Stimuli and encoding
~~~~~~~~~~~~~~~~~~~~

* The encoder API was cleaned up: :py:class:`~pulse2percept.stimuli.Encoder` is
  now the general base class, and only an
  :py:class:`~pulse2percept.stimuli.ImplantEncoder` can be installed on
  ``Implant.encoder``.
  ``AmplitudeEncoder(amp_range=(0, 50)).encode(image, implant=implant)``
  becomes ``AmplitudeEncoder(implant, amp_range=(0, 50)).encode(image)``,
  and ``StimulusEncoder`` was renamed to
  :py:class:`~pulse2percept.stimuli.PulseEncoder` (:pull:`925`, :pull:`926`).

* Added :py:class:`~pulse2percept.stimuli.TraceEncoder`, a model-aware encoder
  that stimulates one electrode at a time along a letter or trajectory drawn
  in the visual field (:pull:`926`).

* The stimuli API was simplified: bundled media moved to ``stimuli.samples``,
  visual psychophysics stimuli to ``stimuli.psychophysics``, and several legacy
  stimulus classes and module paths were deprecated or removed (:pull:`889`).

* :py:class:`~pulse2percept.stimuli.ImageStimulus` and
  :py:class:`~pulse2percept.stimuli.VideoStimulus` now provide
  ``crop_square()`` for centered square crops (:pull:`916`).


Implants
~~~~~~~~

* Implant classes were reorganized by anatomical target. Retinal and cortical
  devices moved to ``implants.retina`` and ``implants.cortex``; generic implant
  machinery remains at the package root. Retinal implants carry ``eye`` and
  cortical implants may carry ``hemisphere`` metadata (:pull:`887`).

* ``ProsthesisSystem`` was renamed
  :py:class:`~pulse2percept.implants.Implant`; the old name is deprecated until
  v0.12.0. ``RectangleImplant`` was removed in favor of
  :py:class:`~pulse2percept.implants.GridImplant` (:pull:`859`, :pull:`876`).

* Implant and electrode APIs were standardized with clearer parameter names,
  consistent grid terminology, ``plot3d()``, and normal Python container
  behavior (:pull:`880`).

* The PRIMA implant family was updated to reflect published device designs,
  including :py:class:`~pulse2percept.implants.retina.PRIMAPivotal`,
  :py:class:`~pulse2percept.implants.retina.Lorach2015Array`,
  :py:class:`~pulse2percept.implants.retina.Ho2019FlatArray`, and
  :py:class:`~pulse2percept.implants.retina.Huang2021Array`
  (:pull:`865`, :pull:`891`).

* :py:class:`~pulse2percept.implants.EnsembleImplant` now supports placement
  from physical coordinates or visual-field maps and enforces a single
  anatomical target (:pull:`884`, :pull:`887`, :pull:`912`).

* Implants now expose ``placement``, ``technology``, and ``family`` metadata
  and support per-electrode thresholds (:pull:`865`, :pull:`869`).


Models
~~~~~~

* Models were reorganized by anatomical target. Generic base and temporal models
  remain in :py:mod:`pulse2percept.models`; retinal and cortical models moved to
  ``models.retina`` and ``models.cortex``. The generic
  :py:class:`~pulse2percept.models.SpatialModel` is now anatomy-neutral, while
  retinal and cortical models enforce compatible implant targets
  (:pull:`887`, :pull:`895`, :pull:`912`).

* Model construction and placement were simplified. Models now bind their
  implant, build on demand, and take stimuli directly in ``predict_percept``.
  ``implant_position``, ``implant_rotation``, and ``implant_depth`` place
  device-local electrode coordinates in tissue; ``model.plot(show_implant=True)``
  shows the resulting placement (:pull:`862`, :pull:`879`, :pull:`884`,
  :pull:`901`).

* New :py:class:`~pulse2percept.models.retina.Ho2018Model` predicts the
  spatiotemporal network-mediated response to photovoltaic subretinal
  stimulation described by [Ho2018]_ (:pull:`903`).

* New :py:class:`~pulse2percept.models.retina.BiphasicScoreboardModel` applies
  the [Granley2021]_ pulse-dependent brightness and size relationships without
  axonal streaks. Retinal scoreboard and axon-map models also support the new
  encoding workflows (:pull:`868`, :pull:`869`, :pull:`886`, :pull:`914`).

* ``location_noise`` now models fixed electrode-specific uncertainty in
  phosphene location. The old generic ``noise`` parameter and ``find_threshold``
  model API were removed (:pull:`862`, :pull:`881`, :pull:`885`).


Topography
~~~~~~~~~~

* Visual-field maps were reorganized by anatomical target: retinal maps moved
  to ``topography.retina`` and cortical maps to ``topography.cortex``. Generic
  map classes remain at the package root (:pull:`887`).

* Added :py:class:`~pulse2percept.topography.retina.Montesano2020Map`, a
  two-dimensional retinal ganglion-cell displacement model based on
  [Montesano2020]_, with eye-specific and inverse mappings (:pull:`897`).

* :py:class:`~pulse2percept.topography.retina.Watson2014DisplaceMap` now
  supports both eyes and is deprecated in favor of ``Montesano2020Map``
  (:pull:`895`).


Scene and plotting
~~~~~~~~~~~~~~~~~~

* Added :py:class:`~pulse2percept.vision.Scene` and
  :py:class:`~pulse2percept.vision.BinocularScene` for composing visual input,
  scotomas, residual vision, and prosthetic percepts in visual-field
  coordinates (:pull:`871`, :pull:`884`, :pull:`890`, :pull:`893`,
  :pull:`899`, :pull:`900`, :pull:`901`, :pull:`902`, :pull:`923`).

* Added :py:class:`~pulse2percept.vision.Gaze` for sparse, timestamped
  fixation events, each held until the next timestamp, given as positions
  plus ``time`` or as ``(x, y, time)`` rows. Accepted wherever ``Scene`` and
  ``predict_percept`` take a gaze array (:pull:`917`, :pull:`923`).

* Added :py:mod:`pulse2percept.plotting` for stimulus/percept figures and
  animations. :py:mod:`pulse2percept.viz` is deprecated until v0.12.0
  (:issue:`872`).


Bug fixes
---------

* Fixed centering of single-row and single-column hexagonal
  :py:class:`~pulse2percept.implants.ElectrodeGrid` instances
  (:pull:`859`).

* Corrected PRIMA-family pixel dimensions and layouts (:pull:`865`).

* :py:class:`~pulse2percept.models.retina.BiphasicAxonMapSpatial` now respects
  `n_gray` and preserves the full stimulus in percept metadata
  (:pull:`869`).

* :py:class:`~pulse2percept.models.cortex.DynaphosModel` phosphenes are half as
  wide: the Gaussian standard deviation is a quarter of the phosphene diameter,
  matching the reference implementation's ``radius_to_sigma = 0.5``
  (:pull:`894`).

* Cortical maps and models no longer place electrodes outside their mapped
  region. :py:class:`~pulse2percept.topography.cortex.Polimeni2006Map`
  ``to_dva`` returns NaN outside each region instead of wrapping into the
  other hemifield; :py:class:`~pulse2percept.models.cortex.DynaphosModel`
  simulates V1 only and raises for driven electrodes outside it; and
  multi-region ``location_noise`` displaces each electrode through its home
  region only. Predictions for such electrodes change (:pull:`926`).


v0.10.0 Encoders (2026-08-23)
=============================

Highlights:

* New stimulus encoders support amplitude and frequency modulation of images and
  videos, with implant-specific raster strategies for multiplexed stimulation
  (:pull:`810`, :pull:`820`, :pull:`833`)

* Stimuli now retain structured pulse and encoder representations and generate
  waveform samples lazily, substantially reducing construction time and memory
  use (:pull:`842`)

* New :py:mod:`~pulse2percept.units` support for dimension-checked physical
  quantities throughout the public API (:pull:`828`)

* Major performance improvements for stimulus handling, spatial models, temporal
  models, and large-array simulations (:pull:`800`, :pull:`805`, :pull:`808`,
  :pull:`821`, :pull:`850`)

* :py:meth:`~pulse2percept.percepts.Percept.play` and
  :py:meth:`~pulse2percept.stimuli.VideoStimulus.play` are substantially faster,
  and percept playback now supports irregular time axes (:pull:`809`,
  :pull:`834`)

* New :py:class:`~pulse2percept.models.AlphaTemporal` and much faster
  :py:class:`~pulse2percept.models.FadingTemporal` temporal models
  (:pull:`849`)

* New :py:meth:`~pulse2percept.percepts.Percept.load` reads percepts from image
  and video files (:pull:`835`)

* Python 3.14 is supported. Python 3.11 and NumPy 2 are now required
  (:pull:`790`)


API changes:

* ``Stimulus.data``, ``time``, ``electrodes``, and pulse parameters are now
  read-only. New :py:meth:`~pulse2percept.stimuli.Stimulus.pad` and
  :py:meth:`~pulse2percept.stimuli.Stimulus.shift` methods provide explicit
  time-axis transformations (:pull:`837`, :pull:`842`)

* Image and video stimuli now use grid-style electrode names such as ``'A1'``
  and ``'C12_G'`` instead of integer pixel indices (:pull:`805`)

* ``ProsthesisSystem`` (now
  :py:class:`~pulse2percept.implants.Implant`) supports stimulus
  encoders, raster strategies, and maximum-current limits. Dimensionless
  image/video stimuli can be encoded automatically when assigned to an implant
  (:pull:`810`, :pull:`833`)

* Temporal models gained a ``reduce`` parameter for summarizing automatically
  selected output intervals. :py:class:`~pulse2percept.models.FadingTemporal`
  now responds only to cathodic current (:pull:`818`)

* :py:class:`~pulse2percept.models.retina.BiphasicAxonMapModel` now distinguishes
  physical current from threshold-relative amplitude via the new ``xTh`` unit
  (:pull:`848`)

* :py:class:`~pulse2percept.models.retina.BiphasicAxonMapSpatial` can now be composed
  with temporal models using a space-time-separable approximation (:pull:`847`)

* Model parameter ``xystep`` was renamed to ``step`` and ``axlambda`` to
  ``lam``; the old names are deprecated until v0.11 (:pull:`824`, :pull:`830`)

* Axon-map and cortical scoreboard models can smooth predictions across
  visual-field meridians (:pull:`838`)

* Multi-electrode stimuli now plot as electrode-by-time heatmaps by default;
  percept playback and saving gained ``vmin``/``vmax`` controls
  (:pull:`835`, :pull:`841`)


Bug fixes:

* Fixed stimulus timing, metadata propagation, pulse scheduling, and structured
  stimulus handling across implants and models (:pull:`804`, :pull:`810`,
  :pull:`818`, :pull:`825`, :pull:`846`)

* Fixed several image and video issues, including argument forwarding,
  centering, single-frame playback, irregular timing, and efficient partial
  video loading (:pull:`815`, :pull:`822`, :pull:`834`, :pull:`844`)

* Fixed several Neuropythy and cortical-map issues involving shapes, NaNs,
  scalar inputs, mesh vertices, subject IDs, and coordinate units
  (:pull:`826`, :pull:`836`, :pull:`845`)

* Various smaller correctness fixes (:pull:`814`)

v0.9.1 (2026-08-06)
===================

Highlights:

*  Python 3.13 support; the minimum supported Python is now 3.10 (:pull:`649`)
*  NumPy 2.x support: wheels import under both NumPy 1.x and 2.x, so installing
   pulse2percept no longer downgrades NumPy in environments that ship it, such
   as Google Colab (:pull:`635`, :pull:`736`)
*  Removed the jax engine and ``predict_percept_batched`` from
   :py:class:`~pulse2percept.models.retina.BiphasicAxonMapModel`; the ``engine``
   argument of the effect models and the ``pad`` argument of
   :py:meth:`~pulse2percept.models.retina.AxonMapSpatial.calc_axon_sensitivity` are
   deprecated, and will be removed in v0.10.0 (:pull:`788`)
*  Removed the ``model_selection`` module (:pull:`685`) and support for the
   joblib and dask parallel backends (:pull:`686`); the ``engine`` and
   ``scheduler`` model parameters are deprecated, and will be removed in
   v0.10.0 (:pull:`788`)
*  ``n_jobs`` is now an alias for ``n_threads``: either name sets the number
   of OpenMP threads, and ``None`` or ``-1`` uses every core (:pull:`788`)
*  More robust dataset downloads, with updated OSF endpoints (:pull:`754`)
*  Smarter ``reshape_stim`` for implants (:pull:`680`)
*  Installation is now tested inside the real Google Colab runtime (:pull:`777`)
*  Various bug fixes (:pull:`682`, :pull:`700`, :pull:`732`, :pull:`776`)

v0.9.0 Cortex (2025-02-17)
==========================

Highlights:

*  Cortical implants: :py:class:`~pulse2percept.implants.cortex.Cortivis`
   [Fernandez2017]_ (:pull:`525`),
   :py:class:`~pulse2percept.implants.cortex.ICVP` [Troyk2003]_ (:pull:`542`),
   :py:class:`~pulse2percept.implants.cortex.Neuralink` [Musk2019]_
   (:pull:`597`)
*  Cortical models: :py:class:`~pulse2percept.models.cortex.ScoreboardModel`
   (:pull:`533`), :py:class:`~pulse2percept.models.cortex.DynaphosModel`
   [vanderGrinten2023]_ (:pull:`547`)
*  Cortical maps: :py:class:`~pulse2percept.topography.cortex.Polimeni2006Map`
   (:pull:`509`), :py:class:`~pulse2percept.topography.cortex.NeuropythyMap` 
   (:pull:`597`)
*  Other new implants: :py:class:`~pulse2percept.implants.retina.IMIE` [Xu2021]_
   (:pull:`492`), :py:class:`~pulse2percept.implants.EnsembleImplant` 
   (:pull:`537`), ``RectangleImplant`` (:pull:`631`)
*  New datasets: :py:class:`~pulse2percept.datasets.fetch_han2021` 
   [Han2021]_ (:pull:`494`)
*  Torch and CUDA support (:pull:`633`)
*  Python 3.11 and 3.12 support
*  Various bug fixes

v0.8.0 Retina (2022-05-05)
==========================

Highlights:

*  New implants: :py:class:`~pulse2percept.implants.retina.BVT44` [Petoe2021]_
   (:pull:`465`)
*  New models: :py:class:`~pulse2percept.models.retina.BiphasicAxonMapModel`
   [Granley2021]_ (:pull:`398`) and
   :py:class:`~pulse2percept.models.retina.Thompson2003Model` [Thompson2003]_
   (:pull:`448`)
*  New datasets: :py:func:`~pulse2percept.datasets.load_greenwald2009`
   [Greenwald2009]_ (:pull:`459`) and
   :py:func:`~pulse2percept.datasets.load_perezfornos2012`
   [PerezFornos2012]_ (:pull:`457`)
*  New stimuli: :py:class:`~pulse2percept.stimuli.BarStimulus`,
   :py:class:`~pulse2percept.stimuli.GratingStimulus` (:pull:`310`)
*  Python 3.10 support (:pull:`479`)
*  Various bug fixes

v0.7.1 (2021-06-21)
===================

Highlights:

*  Add :py:class:`~pulse2percept.models.FadingTemporal`, a generic phosphene fading model (:pull:`378`)
*  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`)
*  Improve documentation and usability of various :py:class:`~pulse2percept.models.retina.AxonMapModel` methods (:pull:`370`)

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

Highlights:

*  New implants: :py:class:`~pulse2percept.implants.retina.PRIMA`, 
   :py:class:`~pulse2percept.implants.retina.PRIMA75`,
   :py:class:`~pulse2percept.implants.retina.PRIMA55`, 
   :py:class:`~pulse2percept.implants.retina.PRIMA40` (:pull:`188`)
*  New electrodes: :py:class:`~pulse2percept.implants.SquareElectrode`,
   :py:class:`~pulse2percept.implants.HexElectrode`,
   :py:class:`~pulse2percept.implants.retina.PhotovoltaicPixel` (:pull:`188`, 
   :pull:`193`)
*  New stimuli: :py:class:`~pulse2percept.stimuli.ImageStimulus` and
   :py:class:`~pulse2percept.stimuli.VideoStimulus` (:pull:`196`, :pull:`220`,
   :pull:`221`, :pull:`356`), :py:class:`~pulse2percept.stimuli.BarStimulus`
   and :py:class:`~pulse2percept.stimuli.GratingStimulus` (:pull:`323`)
*  New datasets: :py:class:`~pulse2percept.datasets.load_nanduri2012`
   (:pull:`250`)
*  New model selection subpackage (:pull:`311`)
*  100x speedup of building :py:class:`~pulse2percept.models.retina.AxonMapModel` (:pull:`331`)
*  OpenMP support (:pull:`260`)
*  Python 3.9 support (:pull:`348`)
*  Various usability upgrades
*  Various bug fixes

v0.6.0 API (2020-05-05)
=======================

Highlights:

*   New API (:pull:`96`, :pull:`174`, :pull:`178`)
*   New implants: ``BVA24``, since renamed
    :py:class:`~pulse2percept.implants.retina.BVT24` (:pull:`161`)
*   New models: :py:class:`~pulse2percept.models.retina.ScoreboardModel` (:pull:`96`),
    :py:class:`~pulse2percept.models.retina.AxonMapModel` (:pull:`96`),
    :py:class:`~pulse2percept.models.retina.Nanduri2012Model` (:pull:`168`),
    :py:class:`~pulse2percept.models.retina.Horsager2009Model` (:pull:`180`)
*   New stimuli: :py:class:`~pulse2percept.stimuli.BiphasicPulseTrain`,
    :py:class:`~pulse2percept.stimuli.AsymmetricBiphasicPulse`,
    :py:class:`~pulse2percept.stimuli.AsymmetricBiphasicPulseTrain`
    (:pull:`178`)
*   New :py:mod:`~pulse2percept.percepts` subpackage (:pull:`174`)
*   New :py:mod:`~pulse2percept.datasets` subpackage (:pull:`167`)
*   New build process: Compile code and run tests via ``Makefile``
    (:pull:`96`)
*   Documentation now includes a tutorial, user guide, developer's guide, and
    a gallery
*   Various bug fixes

v0.5.2 (2020-02-25)
===================

Bug fix:

*   ``pulse2percept.retina.Nanduri2012``: improved Cython implementation

v0.5.1 (2020-02-05)
===================

Bug fixes:

*   ``pulse2percept.retina.Nanduri2012``: allow switch between FFT/Cython
*   ``pulse2percept.retina.Horsager2009``: respect ``use_jit`` option
*   ``pulse2percept.utils.center_vector``: "cannot determine Numba type"

v0.5.0 Community (2019-11-29)
=============================

*   New :py:mod:`pulse2percept.viz` module (:pull:`84`)
*   Support for the :py:class:`~pulse2percept.implants.retina.AlphaIMS` implant
    (:pull:`87`)
*   Automated wheelhouse build (:pull:`130`)
*   New contribution guidelines (:pull:`92`)
*   New issue templates (:pull:`93`)
*   New code of conduct (:pull:`95`)
*   Host documentation on
    `pulse2percept.readthedocs.io <https://pulse2percept.readthedocs.io>`_.

v0.4.3 Cython (2018-05-21)
==========================

Highlights:

*   Cython integration:

    * The model described in Nanduri et al. (2012) now uses a finite difference
      method implemented in Cython as opposed to FFT-based convolutions
      (:pull:`83`)

    * Single-core benchmarks show a 200x speedup over a pure-Python
      implementation.


v0.3.0 Baby Steps (2018-02-20)
==============================

*   New, faster axon map calculation
*   Better plotting
*   Support for left/right eye
