pulse2percept.implants.rasters

Raster, SequentialRaster, CheckerboardRaster, CustomRaster

Classes

CheckerboardRaster(n_groups[, balance, ...])

Split electrodes into groups that are spread as far apart as possible

CustomRaster(groups[, group_dur])

Assign electrodes to raster groups by name

Raster([group_dur])

Abstract base class for raster patterns.

SequentialRaster(n_groups[, interleave, ...])

Split electrodes into groups that fire one after another

class pulse2percept.implants.rasters.Raster(group_dur=None)[source]

Abstract base class for raster patterns.

A raster partitions electrodes into groups that take turns stimulating. Different groups must not be active at the same time. Raster timing is applied by PulseEncoder.

Assigning a raster to implant.raster binds it to that implant. Subclasses implement groups() and may override bind() when the grouping depends on electrode geometry.

Added in version 0.10.0.

Parameters:

group_dur (float, optional) – Duration of one group’s slot (ms). If None, groups divide the shortest pulse period evenly. An explicit value fixes the raster sweep to n_groups * group_dur.

property implant

The implant this raster is bound to, or None

Set by assigning the raster to raster (see bind()).

bind(implant)[source]

Bind the raster to an implant or electrode array.

Geometry-dependent rasters may override this method to recompute their grouping.

Parameters:

implant (Implant or ) – ElectrodeArray Implant or electrode array to bind.

Returns:

self

Return type:

Raster

abstract property n_groups

Number of raster groups

abstractmethod groups(electrodes)[source]

Assign each electrode to a raster group

Parameters:

electrodes (array_like) – Electrode names, in the order they appear in the stimulus.

Returns:

group – The group each electrode belongs to, in 0..n_groups-1.

Return type:

(n_electrodes,) int array

members(electrodes, group)[source]

Return the electrodes in one raster group.

Parameters:
  • electrodes (array_like) – Electrode names.

  • group (int) – Group index in 0..n_groups-1.

Returns:

members – Electrode names belonging to group.

Return type:

array

plot(implant=None, annotate=None, ax=None, cmap='viridis', autoscale=True)[source]

Plot electrodes colored by raster group.

Parameters:
  • implant (Implant, optional) – Implant to draw. If None, use the bound implant.

  • annotate (bool, optional) – Write group indices on electrodes. If None, annotate arrays with at most 120 electrodes.

  • ax (matplotlib.axes.Axes, optional) – Axes to draw on.

  • cmap (str, optional) – Matplotlib colormap.

  • autoscale (bool, optional) – Fit the axes to the implant.

Returns:

ax – The axes drawn on.

Return type:

matplotlib.axes.Axes

slot_dur(period)[source]

Duration (ms) of one group’s slot

Parameters:

period (float) – The pulse period (ms) a sweep has to fit into, so that every group gets its turn before the first one comes round again.

Returns:

slot_dur – group_dur if one was given, else the period split evenly between the groups.

Return type:

float

offsets(electrodes, period)[source]

How far behind group 0 each electrode’s slot begins

Parameters:
  • electrodes (array_like) – Electrode names, in the order they appear in the stimulus.

  • period (float) – The pulse period (ms) a sweep has to fit into.

Returns:

offset – Time (ms) between the start of a sweep and the start of this electrode’s slot.

Return type:

(n_electrodes,) float array

class pulse2percept.implants.rasters.SequentialRaster(n_groups, interleave=False, group_dur=None)[source]

Split electrodes into groups that fire one after another

Electrodes are assigned to groups by their position in the stimulus, which for an ElectrodeGrid runs row by row. So on a 6x10 array such as ArgusII, SequentialRaster(6) puts each row in its own group – a line raster.

Added in version 0.10.0.

Parameters:
  • n_groups (int) – Number of groups to split the electrodes into.

  • interleave (bool, optional) – If False (the default), each group is a contiguous block of electrodes. If True, groups are interleaved, so that consecutive electrodes end up in different groups. Interleaving spreads each group’s current further across the array.

  • group_dur (float, optional) – See Raster.

Examples

A line raster for Argus II, one row of ten electrodes at a time:

>>> from pulse2percept.implants import SequentialRaster
>>> from pulse2percept.implants.retina import ArgusII
>>> implant = ArgusII()
>>> implant.raster = SequentialRaster(6)
property n_groups

Number of raster groups

groups(electrodes)[source]

Assign each electrode to a raster group

bind(implant)[source]

Bind the raster to an implant or electrode array.

Geometry-dependent rasters may override this method to recompute their grouping.

Parameters:

implant (Implant or ) – ElectrodeArray Implant or electrode array to bind.

Returns:

self

Return type:

Raster

property implant

The implant this raster is bound to, or None

Set by assigning the raster to raster (see bind()).

members(electrodes, group)[source]

Return the electrodes in one raster group.

Parameters:
  • electrodes (array_like) – Electrode names.

  • group (int) – Group index in 0..n_groups-1.

Returns:

members – Electrode names belonging to group.

Return type:

array

offsets(electrodes, period)[source]

How far behind group 0 each electrode’s slot begins

Parameters:
  • electrodes (array_like) – Electrode names, in the order they appear in the stimulus.

  • period (float) – The pulse period (ms) a sweep has to fit into.

Returns:

offset – Time (ms) between the start of a sweep and the start of this electrode’s slot.

Return type:

(n_electrodes,) float array

plot(implant=None, annotate=None, ax=None, cmap='viridis', autoscale=True)[source]

Plot electrodes colored by raster group.

Parameters:
  • implant (Implant, optional) – Implant to draw. If None, use the bound implant.

  • annotate (bool, optional) – Write group indices on electrodes. If None, annotate arrays with at most 120 electrodes.

  • ax (matplotlib.axes.Axes, optional) – Axes to draw on.

  • cmap (str, optional) – Matplotlib colormap.

  • autoscale (bool, optional) – Fit the axes to the implant.

Returns:

ax – The axes drawn on.

Return type:

matplotlib.axes.Axes

slot_dur(period)[source]

Duration (ms) of one group’s slot

Parameters:

period (float) – The pulse period (ms) a sweep has to fit into, so that every group gets its turn before the first one comes round again.

Returns:

slot_dur – group_dur if one was given, else the period split evenly between the groups.

Return type:

float

class pulse2percept.implants.rasters.CheckerboardRaster(n_groups, balance=0.05, group_dur=None)[source]

Split electrodes into groups that are spread as far apart as possible

Implements a generalized form of the checkerboard raster pattern tested in [Kasowski2025], which found that scattering raster groups over the whole array beat horizontal, vertical, and random rasters at letter recognition and motion discrimination, and matched not rastering at all.

Mathematically speaking, a raster group is a coset of a sublattice of the electrode grid. Within a group, electrodes sit as far from one another as the electrode count allows. Each group is a coarser copy of the grid. Between groups, the order is chosen so the pattern doubles back rather than marching on (to reduce apparent motion). For example: Five groups on a square grid come out one over, two over, one back, two over, so that the percept steps right, down, left, down, and back rather than sliding across the array.

The grid does not have to be rectangular: hexagonal grids, rotated grids, grids with unequal row and column spacing, and grids with electrodes trimmed off are all handled, since the pattern is derived from where the electrodes actually are. Arrays whose electrodes do not lie on a grid at all raise NotImplementedError.

The pattern depends on where the electrodes are, so it is worked out when the raster is bound to an implant – which assigning it to raster does. Until then the raster knows how many groups it will have but not which electrode goes in which, and groups(), min_spacing and plot() all raise. Binding it to a second implant recomputes the pattern for that one.

Note

Not every n_groups fits a given grid, and one that does not raises a ValueError (naming counts that do) when the raster is bound.

Both halves of the pattern are searched for at binding time, and the order the groups fire in is settled exactly only up to eight groups; beyond that a heuristic stands in for it, and the search grows with the group count.

It is worth checking min_spacing on the ones that do fit, because a count can be accepted and still leave neighbors in the same group. The standard example is two groups on a hex grid, which degenerates to a line raster. In other words, implants like PRIMA cannot be two-colored; they want 3, 4, or 7 raster groups instead.

Added in version 0.10.0.

Parameters:
  • n_groups (int) – Number of groups to split the electrodes into.

  • balance (float, optional) – How much bigger the largest group may be than an even split would make it, as a fraction of it. The largest group is what sets the current the stimulator has to source, so this is the price being paid; what it buys is spacing, because the patterns that spread furthest do not always land evenly on a grid whose edges have been trimmed. Pass 0 to add no imbalance beyond the rounding an uneven electrode count forces anyway – 378 electrodes in 5 groups are 76, 76, 75, 75, 76 at balance=0, never 76 apiece – and take whatever spacing comes with it.

  • group_dur (float, optional) – See Raster.

Examples

Five groups of twelve on Argus II, as in [Kasowski2025]:

>>> from pulse2percept.implants import CheckerboardRaster
>>> from pulse2percept.implants.retina import ArgusII
>>> implant = ArgusII()
>>> implant.raster = CheckerboardRaster(5)
>>> implant.raster.n_groups
5

No two electrodes of a group are closer than sqrt(5) pitches, where a line raster would have them adjacent:

>>> round(implant.raster.min_spacing / 575, 3)  # 575 um pitch
2.236

The pattern is easiest to check by eye (plot()), and the electrodes that fire together are members():

>>> implant.raster.members(implant.electrode_names, 0)[:4].tolist()
['A1', 'A6', 'B3', 'B8']
bind(implant)[source]

Work the checkerboard out for this implant’s electrode grid

See bind(). Called for you when the raster is assigned to raster.

property n_groups

Number of raster groups

property min_spacing

Distance (um) between the closest two electrodes of a group

How much the checkerboard bought over a line raster, which leaves neighboring electrodes in the same group and so would report the electrode pitch. Measured between electrodes the implant actually has, so a small or trimmed array can come out better spaced than the pattern it was cut from. Infinite when no group holds more than one electrode, since then no two electrodes ever fire together. None until the raster is bound to an implant, since there is no grid to measure yet.

groups(electrodes)[source]

Assign each electrode to a raster group

property implant

The implant this raster is bound to, or None

Set by assigning the raster to raster (see bind()).

members(electrodes, group)[source]

Return the electrodes in one raster group.

Parameters:
  • electrodes (array_like) – Electrode names.

  • group (int) – Group index in 0..n_groups-1.

Returns:

members – Electrode names belonging to group.

Return type:

array

offsets(electrodes, period)[source]

How far behind group 0 each electrode’s slot begins

Parameters:
  • electrodes (array_like) – Electrode names, in the order they appear in the stimulus.

  • period (float) – The pulse period (ms) a sweep has to fit into.

Returns:

offset – Time (ms) between the start of a sweep and the start of this electrode’s slot.

Return type:

(n_electrodes,) float array

plot(implant=None, annotate=None, ax=None, cmap='viridis', autoscale=True)[source]

Plot electrodes colored by raster group.

Parameters:
  • implant (Implant, optional) – Implant to draw. If None, use the bound implant.

  • annotate (bool, optional) – Write group indices on electrodes. If None, annotate arrays with at most 120 electrodes.

  • ax (matplotlib.axes.Axes, optional) – Axes to draw on.

  • cmap (str, optional) – Matplotlib colormap.

  • autoscale (bool, optional) – Fit the axes to the implant.

Returns:

ax – The axes drawn on.

Return type:

matplotlib.axes.Axes

slot_dur(period)[source]

Duration (ms) of one group’s slot

Parameters:

period (float) – The pulse period (ms) a sweep has to fit into, so that every group gets its turn before the first one comes round again.

Returns:

slot_dur – group_dur if one was given, else the period split evenly between the groups.

Return type:

float

class pulse2percept.implants.rasters.CustomRaster(groups, group_dur=None)[source]

Assign electrodes to raster groups by name

Added in version 0.10.0.

Parameters:
  • groups (list of lists, or dict) – Either a list whose i-th element holds the names of the electrodes in group i, or a dict mapping each electrode name onto its group index. Every electrode in the stimulus must be accounted for, and no electrode may appear in two groups.

  • group_dur (float, optional) – See Raster.

Examples

Fire the four corners of Argus II before everything else. Every other electrode has to be given a group too, or the current limit that the raster exists to respect could be violated without anyone noticing:

>>> from pulse2percept.implants import CustomRaster
>>> from pulse2percept.implants.retina import ArgusII
>>> corners = ['A1', 'A10', 'F1', 'F10']
>>> rest = [e for e in ArgusII().electrode_names if e not in corners]
>>> raster = CustomRaster([corners, rest])
>>> raster.n_groups
2
property n_groups

Number of raster groups

groups(electrodes)[source]

Assign each electrode to a raster group

bind(implant)[source]

Bind the raster to an implant or electrode array.

Geometry-dependent rasters may override this method to recompute their grouping.

Parameters:

implant (Implant or ) – ElectrodeArray Implant or electrode array to bind.

Returns:

self

Return type:

Raster

property implant

The implant this raster is bound to, or None

Set by assigning the raster to raster (see bind()).

members(electrodes, group)[source]

Return the electrodes in one raster group.

Parameters:
  • electrodes (array_like) – Electrode names.

  • group (int) – Group index in 0..n_groups-1.

Returns:

members – Electrode names belonging to group.

Return type:

array

offsets(electrodes, period)[source]

How far behind group 0 each electrode’s slot begins

Parameters:
  • electrodes (array_like) – Electrode names, in the order they appear in the stimulus.

  • period (float) – The pulse period (ms) a sweep has to fit into.

Returns:

offset – Time (ms) between the start of a sweep and the start of this electrode’s slot.

Return type:

(n_electrodes,) float array

plot(implant=None, annotate=None, ax=None, cmap='viridis', autoscale=True)[source]

Plot electrodes colored by raster group.

Parameters:
  • implant (Implant, optional) – Implant to draw. If None, use the bound implant.

  • annotate (bool, optional) – Write group indices on electrodes. If None, annotate arrays with at most 120 electrodes.

  • ax (matplotlib.axes.Axes, optional) – Axes to draw on.

  • cmap (str, optional) – Matplotlib colormap.

  • autoscale (bool, optional) – Fit the axes to the implant.

Returns:

ax – The axes drawn on.

Return type:

matplotlib.axes.Axes

slot_dur(period)[source]

Duration (ms) of one group’s slot

Parameters:

period (float) – The pulse period (ms) a sweep has to fit into, so that every group gets its turn before the first one comes round again.

Returns:

slot_dur – group_dur if one was given, else the period split evenly between the groups.

Return type:

float