Skip to content

neurospatial.environment._protocols

_protocols

Protocol definitions for Environment mixins.

This module defines Protocol classes that specify the interface mixins expect from the Environment class. Using Protocols allows mypy to understand the mixin pattern without requiring type: ignore comments or disabling error codes.

See: https://mypy.readthedocs.io/en/latest/more_types.html#mixin-classes

Classes

EnvironmentProtocol

Bases: Protocol

Protocol defining the interface that Environment provides to mixins.

This protocol specifies all attributes and methods that mixins may access from the Environment class. Mixins use self: EnvironmentProtocol instead of self: Environment to avoid "erased type" errors in mypy.

Attributes
n_bins property
n_bins: int

Number of spatial bins in the environment.

Returns:

Type Description
int

Total count of discrete spatial bins.

n_dims property
n_dims: int

Number of spatial dimensions.

Returns:

Type Description
int

Dimensionality of the environment (1, 2, or 3).

is_linearized_track property
is_linearized_track: bool

Whether this is a 1D (linearized) environment.

Returns:

Type Description
bool

True if environment supports linearization methods.

bin_sizes property
bin_sizes: NDArray[float64]

Size of bins along each dimension.

Returns:

Type Description
ndarray of shape (n_dims,)

Bin width in each spatial dimension.

layout_type property
layout_type: str | None

Type of layout engine used to create this environment.

Returns:

Type Description
str or None

Layout type name (e.g., 'RegularGrid', 'Hexagonal'), or None if not set.

layout_parameters property
layout_parameters: dict[str, Any] | None

Parameters used to create the layout.

Returns:

Type Description
dict or None

Dictionary of layout configuration parameters, or None if not set.

boundary_bins property
boundary_bins: NDArray[int_]

Indices of bins at the environment boundary.

Returns:

Type Description
ndarray of shape (n_boundary,)

Integer indices of boundary bins.

Functions
get_differential_operator
get_differential_operator() -> sparse.csc_matrix

Build (or fetch a cached) edge-oriented differential operator.

The differential operator D encodes the oriented edges of the connectivity graph: for each edge e = (i, j) with weight w_e, D[i, e] = -sqrt(w_e) and D[j, e] = +sqrt(w_e). This is not the graph Laplacian; the Laplacian is the derived quantity L = D @ D.T (shape (n_bins, n_bins)).

Returns:

Type Description
scipy.sparse.csc_matrix of shape (n_bins, n_edges)

Sparse differential operator matrix.

Notes

Method (not property) since v0.4: the underlying computation scales with the connectivity-graph size, so the call surface keeps that cost visible at the call site. The result is cached and invalidated by _state_version bumps.

See Also

neurospatial.ops.calculus.compute_differential_operator : The free function that builds D from an Environment.

Source code in src/neurospatial/environment/_protocols.py
def get_differential_operator(self) -> sparse.csc_matrix:
    """
    Build (or fetch a cached) edge-oriented differential operator.

    The differential operator ``D`` encodes the oriented edges of
    the connectivity graph: for each edge ``e = (i, j)`` with weight
    ``w_e``, ``D[i, e] = -sqrt(w_e)`` and ``D[j, e] = +sqrt(w_e)``.
    This is **not** the graph Laplacian; the Laplacian is the
    derived quantity ``L = D @ D.T`` (shape ``(n_bins, n_bins)``).

    Returns
    -------
    scipy.sparse.csc_matrix of shape (n_bins, n_edges)
        Sparse differential operator matrix.

    Notes
    -----
    Method (not property) since v0.4: the underlying computation
    scales with the connectivity-graph size, so the call surface
    keeps that cost visible at the call site. The result is cached
    and invalidated by ``_state_version`` bumps.

    See Also
    --------
    neurospatial.ops.calculus.compute_differential_operator :
        The free function that builds ``D`` from an Environment.
    """
    ...
bin_at
bin_at(points_nd: NDArray[float64]) -> NDArray[np.int_]

Find bin indices for given points.

Parameters:

Name Type Description Default
points_nd ndarray of shape (n_points, n_dims) or (n_dims,)

Spatial coordinates to query.

required

Returns:

Type Description
ndarray of shape (n_points,) or int

Bin index for each point. Returns -1 for points outside the environment.

Source code in src/neurospatial/environment/_protocols.py
def bin_at(self, points_nd: NDArray[np.float64]) -> NDArray[np.int_]:
    """
    Find bin indices for given points.

    Parameters
    ----------
    points_nd : ndarray of shape (n_points, n_dims) or (n_dims,)
        Spatial coordinates to query.

    Returns
    -------
    ndarray of shape (n_points,) or int
        Bin index for each point. Returns -1 for points outside
        the environment.
    """
    ...
bin_sequence
bin_sequence(times: NDArray[float64], positions: NDArray[float64], *, dedup: bool = True, outside_value: int | None = -1) -> NDArray[np.int32]

Convert a trajectory to a sequence of bin indices.

Parameters:

Name Type Description Default
times ndarray of shape (n_samples,)

Timestamps for each position sample.

required
positions ndarray of shape (n_samples, n_dims)

Spatial coordinates at each time point.

required
dedup bool

If True, remove consecutive duplicate bin indices.

True
outside_value int or None

Value to use for positions outside environment.

-1

Returns:

Type Description
ndarray

Bin indices. Use bin_sequence_with_runs to also obtain run-length boundaries.

Source code in src/neurospatial/environment/_protocols.py
def bin_sequence(
    self,
    times: NDArray[np.float64],
    positions: NDArray[np.float64],
    *,
    dedup: bool = True,
    outside_value: int | None = -1,
) -> NDArray[np.int32]:
    """
    Convert a trajectory to a sequence of bin indices.

    Parameters
    ----------
    times : ndarray of shape (n_samples,)
        Timestamps for each position sample.
    positions : ndarray of shape (n_samples, n_dims)
        Spatial coordinates at each time point.
    dedup : bool, default=True
        If True, remove consecutive duplicate bin indices.
    outside_value : int or None, default=-1
        Value to use for positions outside environment.

    Returns
    -------
    ndarray
        Bin indices. Use ``bin_sequence_with_runs`` to also obtain
        run-length boundaries.
    """
    ...
bin_sequence_with_runs
bin_sequence_with_runs(times: NDArray[float64], positions: NDArray[float64], *, outside_value: int | None = -1) -> Any

Convert a trajectory to a bin sequence plus per-run boundaries.

Returns a BinSequenceWithRuns dataclass with bins, run_starts, and run_lengths — all shape (n_runs,). For per-sample bins use bin_sequence(dedup=False) instead.

Source code in src/neurospatial/environment/_protocols.py
def bin_sequence_with_runs(
    self,
    times: NDArray[np.float64],
    positions: NDArray[np.float64],
    *,
    outside_value: int | None = -1,
) -> Any:
    """
    Convert a trajectory to a bin sequence plus per-run boundaries.

    Returns a ``BinSequenceWithRuns`` dataclass with ``bins``,
    ``run_starts``, and ``run_lengths`` — all shape ``(n_runs,)``.
    For per-sample bins use ``bin_sequence(dedup=False)`` instead.
    """
    ...
bins_in_region
bins_in_region(region_name: str) -> NDArray[np.int_]

Get bin indices within a named region.

Parameters:

Name Type Description Default
region_name str

Name of the region to query.

required

Returns:

Type Description
ndarray of shape (n_bins_in_region,)

Integer indices of bins within the region.

Source code in src/neurospatial/environment/_protocols.py
def bins_in_region(self, region_name: str) -> NDArray[np.int_]:
    """
    Get bin indices within a named region.

    Parameters
    ----------
    region_name : str
        Name of the region to query.

    Returns
    -------
    ndarray of shape (n_bins_in_region,)
        Integer indices of bins within the region.
    """
    ...
region_mask
region_mask(regions: object, *, include_boundary: bool = True) -> NDArray[np.bool_]

Get boolean mask for one or more regions.

Parameters:

Name Type Description Default
regions str, list[str], Region, or Regions

Region(s) to test against. Strings are looked up in self.regions.

required
include_boundary bool

Whether bins whose centers lie on a polygon region boundary count as inside (covers vs. contains).

True

Returns:

Type Description
ndarray of shape (n_bins,)

Boolean mask where True indicates the bin is in any of the named regions. Point regions select the bin containing the point.

Source code in src/neurospatial/environment/_protocols.py
def region_mask(
    self,
    regions: object,
    *,
    include_boundary: bool = True,
) -> NDArray[np.bool_]:
    """
    Get boolean mask for one or more regions.

    Parameters
    ----------
    regions : str, list[str], Region, or Regions
        Region(s) to test against. Strings are looked up in
        ``self.regions``.
    include_boundary : bool, default=True
        Whether bins whose centers lie on a polygon region boundary
        count as inside (covers vs. contains).

    Returns
    -------
    ndarray of shape (n_bins,)
        Boolean mask where True indicates the bin is in any of the
        named regions. Point regions select the bin containing the
        point.
    """
    ...
region_membership
region_membership(regions: Regions | None = None, *, include_boundary: bool = True) -> NDArray[np.bool_]

Get membership mask for all regions.

Parameters:

Name Type Description Default
regions Regions

Regions container to use. If None, uses self.regions.

None
include_boundary bool

Whether to include boundary bins in region membership.

True

Returns:

Type Description
ndarray of shape (n_bins, n_regions)

Boolean mask where entry [i, j] is True if bin i belongs to region j.

Source code in src/neurospatial/environment/_protocols.py
def region_membership(
    self,
    regions: Regions | None = None,
    *,
    include_boundary: bool = True,
) -> NDArray[np.bool_]:
    """
    Get membership mask for all regions.

    Parameters
    ----------
    regions : Regions, optional
        Regions container to use. If None, uses self.regions.
    include_boundary : bool, default=True
        Whether to include boundary bins in region membership.

    Returns
    -------
    ndarray of shape (n_bins, n_regions)
        Boolean mask where entry [i, j] is True if bin i belongs
        to region j.
    """
    ...
compute_kernel
compute_kernel(bandwidth: float, *, mode: Literal['transition', 'density'] = 'density', cache: bool = True) -> NDArray[np.float64]

Compute a smoothing kernel matrix.

Parameters:

Name Type Description Default
bandwidth float

Smoothing bandwidth in spatial units.

required
mode ('transition', 'density')

Kernel normalization mode.

'transition'
cache bool

Whether to cache the computed kernel.

True

Returns:

Type Description
ndarray of shape (n_bins, n_bins)

Kernel matrix for smoothing operations.

Source code in src/neurospatial/environment/_protocols.py
def compute_kernel(
    self,
    bandwidth: float,
    *,
    mode: Literal["transition", "density"] = "density",
    cache: bool = True,
) -> NDArray[np.float64]:
    """
    Compute a smoothing kernel matrix.

    Parameters
    ----------
    bandwidth : float
        Smoothing bandwidth in spatial units.
    mode : {'transition', 'density'}, default='density'
        Kernel normalization mode.
    cache : bool, default=True
        Whether to cache the computed kernel.

    Returns
    -------
    ndarray of shape (n_bins, n_bins)
        Kernel matrix for smoothing operations.
    """
    ...
smooth
smooth(field: NDArray[float64], bandwidth: float, *, mode: Literal['transition', 'density'] = 'density') -> NDArray[np.float64]

Apply graph-based smoothing to a spatial field.

Parameters:

Name Type Description Default
field ndarray of shape (n_bins,)

Spatial field values to smooth.

required
bandwidth float

Smoothing bandwidth in spatial units.

required
mode ('transition', 'density')

Kernel normalization mode.

'transition'

Returns:

Type Description
ndarray of shape (n_bins,)

Smoothed field values.

Source code in src/neurospatial/environment/_protocols.py
def smooth(
    self,
    field: NDArray[np.float64],
    bandwidth: float,
    *,
    mode: Literal["transition", "density"] = "density",
) -> NDArray[np.float64]:
    """
    Apply graph-based smoothing to a spatial field.

    Parameters
    ----------
    field : ndarray of shape (n_bins,)
        Spatial field values to smooth.
    bandwidth : float
        Smoothing bandwidth in spatial units.
    mode : {'transition', 'density'}, default='density'
        Kernel normalization mode.

    Returns
    -------
    ndarray of shape (n_bins,)
        Smoothed field values.
    """
    ...
occupancy
occupancy(times: NDArray[float64], positions: NDArray[float64], *, speed: NDArray[float64] | None = None, min_speed: float | None = None, max_gap: float | None = 0.5, bandwidth: float | None = None, time_allocation: Literal['start', 'linear'] = 'start', return_seconds: bool = True) -> NDArray[np.float64]

Compute spatial occupancy from position data.

Parameters:

Name Type Description Default
times ndarray of shape (n_samples,)

Timestamps for each position sample.

required
positions ndarray of shape (n_samples, n_dims)

Spatial coordinates at each time point.

required
speed ndarray of shape (n_samples,)

Speed at each time point for filtering.

None
min_speed float

Minimum speed threshold for inclusion.

None
max_gap float

Maximum time gap before splitting trajectory.

0.5
bandwidth float

Bandwidth for smoothing occupancy.

None
time_allocation ('start', 'linear')

How to allocate time between samples.

'start'
return_seconds bool

If True, return occupancy in seconds; else in samples.

True

Returns:

Type Description
ndarray of shape (n_bins,)

Time spent in each bin.

Source code in src/neurospatial/environment/_protocols.py
def occupancy(
    self,
    times: NDArray[np.float64],
    positions: NDArray[np.float64],
    *,
    speed: NDArray[np.float64] | None = None,
    min_speed: float | None = None,
    max_gap: float | None = 0.5,
    bandwidth: float | None = None,
    time_allocation: Literal["start", "linear"] = "start",
    return_seconds: bool = True,
) -> NDArray[np.float64]:
    """
    Compute spatial occupancy from position data.

    Parameters
    ----------
    times : ndarray of shape (n_samples,)
        Timestamps for each position sample.
    positions : ndarray of shape (n_samples, n_dims)
        Spatial coordinates at each time point.
    speed : ndarray of shape (n_samples,), optional
        Speed at each time point for filtering.
    min_speed : float, optional
        Minimum speed threshold for inclusion.
    max_gap : float, optional, default=0.5
        Maximum time gap before splitting trajectory.
    bandwidth : float, optional
        Bandwidth for smoothing occupancy.
    time_allocation : {'start', 'linear'}, default='start'
        How to allocate time between samples.
    return_seconds : bool, default=True
        If True, return occupancy in seconds; else in samples.

    Returns
    -------
    ndarray of shape (n_bins,)
        Time spent in each bin.
    """
    ...
distance_between
distance_between(point1: NDArray[float64], point2: NDArray[float64], edge_weight: str = 'distance') -> float

Compute graph distance between two points.

Parameters:

Name Type Description Default
point1 ndarray of shape (n_dims,)

First spatial coordinate.

required
point2 ndarray of shape (n_dims,)

Second spatial coordinate.

required
edge_weight str

Edge attribute to use for distance calculation.

'distance'

Returns:

Type Description
float

Shortest path distance between points.

Source code in src/neurospatial/environment/_protocols.py
def distance_between(
    self,
    point1: NDArray[np.float64],
    point2: NDArray[np.float64],
    edge_weight: str = "distance",
) -> float:
    """
    Compute graph distance between two points.

    Parameters
    ----------
    point1 : ndarray of shape (n_dims,)
        First spatial coordinate.
    point2 : ndarray of shape (n_dims,)
        Second spatial coordinate.
    edge_weight : str, default='distance'
        Edge attribute to use for distance calculation.

    Returns
    -------
    float
        Shortest path distance between points.
    """
    ...
point_to_bin_index
point_to_bin_index(points: NDArray[float64]) -> NDArray[np.int_]

Convert N-D points to flat bin indices.

Parameters:

Name Type Description Default
points ndarray of shape (n_points, n_dims) or (n_dims,)

Spatial coordinates to convert.

required

Returns:

Type Description
ndarray of shape (n_points,) or int

Flat bin index for each point.

Source code in src/neurospatial/environment/_protocols.py
def point_to_bin_index(self, points: NDArray[np.float64]) -> NDArray[np.int_]:
    """
    Convert N-D points to flat bin indices.

    Parameters
    ----------
    points : ndarray of shape (n_points, n_dims) or (n_dims,)
        Spatial coordinates to convert.

    Returns
    -------
    ndarray of shape (n_points,) or int
        Flat bin index for each point.
    """
    ...
to_linear
to_linear(nd_position: NDArray[float64]) -> NDArray[np.float64]

Convert N-D positions to 1D linear positions.

Only available for 1D environments (is_linearized_track=True).

Parameters:

Name Type Description Default
nd_position ndarray of shape (n_points, n_dims) or (n_dims,)

N-dimensional spatial coordinates.

required

Returns:

Type Description
ndarray of shape (n_points,) or float

Linear position along the track.

Source code in src/neurospatial/environment/_protocols.py
def to_linear(self, nd_position: NDArray[np.float64]) -> NDArray[np.float64]:
    """
    Convert N-D positions to 1D linear positions.

    Only available for 1D environments (is_linearized_track=True).

    Parameters
    ----------
    nd_position : ndarray of shape (n_points, n_dims) or (n_dims,)
        N-dimensional spatial coordinates.

    Returns
    -------
    ndarray of shape (n_points,) or float
        Linear position along the track.
    """
    ...
linear_to_nd
linear_to_nd(linear_position: NDArray[float64]) -> NDArray[np.float64]

Convert 1D linear positions to N-D coordinates.

Only available for 1D environments (is_linearized_track=True).

Parameters:

Name Type Description Default
linear_position ndarray of shape (n_points,) or float

Linear position along the track.

required

Returns:

Type Description
ndarray of shape (n_points, n_dims) or (n_dims,)

N-dimensional spatial coordinates.

Source code in src/neurospatial/environment/_protocols.py
def linear_to_nd(self, linear_position: NDArray[np.float64]) -> NDArray[np.float64]:
    """
    Convert 1D linear positions to N-D coordinates.

    Only available for 1D environments (is_linearized_track=True).

    Parameters
    ----------
    linear_position : ndarray of shape (n_points,) or float
        Linear position along the track.

    Returns
    -------
    ndarray of shape (n_points, n_dims) or (n_dims,)
        N-dimensional spatial coordinates.
    """
    ...
animate_fields
animate_fields(fields: Any, *, frame_times: NDArray[float64], backend: Literal['auto', 'napari', 'video', 'html', 'widget'] = 'auto', save_path: str | None = None, speed: float = 1.0, cmap: str = 'viridis', vmin: float | None = None, vmax: float | None = None, frame_labels: Any = None, overlay_trajectory: NDArray[float64] | None = None, title: str = 'Spatial Field Animation', dpi: int = 100, codec: str = 'h264', bitrate: int = 5000, n_workers: int | None = None, dry_run: bool = False, image_format: Literal['png', 'jpeg'] = 'png', max_html_frames: int = 500, contrast_limits: tuple[float, float] | None = None, show_colorbar: bool = False, colorbar_label: str = '', overlays: list[OverlayProtocol] | None = None, show_regions: bool | list[str] = False, region_alpha: float = 0.3, scale_bar: bool | Any = False, **kwargs: Any) -> Any

Animate spatial fields over time.

Parameters:

Name Type Description Default
fields ndarray of shape (n_frames, n_bins) or sequence

Spatial field values for each frame.

required
frame_times ndarray of shape (n_frames,)

Timestamps for each frame. Required.

required
backend ('auto', 'napari', 'video', 'html', 'widget')

Animation backend to use.

'auto'
save_path str

Path to save video file.

None
speed float

Playback speed multiplier relative to real time.

1.0
cmap str

Colormap name.

'viridis'
vmin float

Minimum value for color scaling.

None
vmax float

Maximum value for color scaling.

None
frame_labels sequence of str

Labels for each frame.

None
overlay_trajectory ndarray of shape (n_frames, n_dims)

Trajectory to overlay on animation.

None
title str

Animation title.

'Spatial Field Animation'
dpi int

Resolution for video output.

100
codec str

Video codec.

'h264'
bitrate int

Video bitrate in kbps.

5000
n_workers int

Number of parallel workers for rendering.

None
dry_run bool

If True, validate inputs without rendering.

False
image_format ('png', 'jpeg')

Format for frame images.

'png'
max_html_frames int

Maximum frames for HTML output.

500
contrast_limits tuple of (float, float)

Min/max values for contrast adjustment.

None
show_colorbar bool

Whether to display colorbar.

False
colorbar_label str

Label for colorbar.

''
overlays list of OverlayProtocol

Additional overlay objects.

None
show_regions bool or list of str

Whether to show region boundaries.

False
region_alpha float

Transparency for region overlays.

0.3
scale_bar bool or ScaleBarConfig

Whether to show scale bar.

False
**kwargs dict

Additional backend-specific arguments.

{}

Returns:

Type Description
Any

Backend-specific return value (viewer, path, or HTML).

Source code in src/neurospatial/environment/_protocols.py
def animate_fields(
    self,
    fields: Any,  # Sequence[NDArray[np.float64]] | NDArray[np.float64]
    *,
    frame_times: NDArray[np.float64],
    backend: Literal["auto", "napari", "video", "html", "widget"] = "auto",
    save_path: str | None = None,
    speed: float = 1.0,
    cmap: str = "viridis",
    vmin: float | None = None,
    vmax: float | None = None,
    frame_labels: Any = None,  # Sequence[str] | None
    overlay_trajectory: NDArray[np.float64] | None = None,
    title: str = "Spatial Field Animation",
    dpi: int = 100,
    codec: str = "h264",
    bitrate: int = 5000,
    n_workers: int | None = None,
    dry_run: bool = False,
    image_format: Literal["png", "jpeg"] = "png",
    max_html_frames: int = 500,
    contrast_limits: tuple[float, float] | None = None,
    show_colorbar: bool = False,
    colorbar_label: str = "",
    overlays: list[OverlayProtocol] | None = None,
    show_regions: bool | list[str] = False,
    region_alpha: float = 0.3,
    scale_bar: bool | Any = False,  # bool | ScaleBarConfig
    **kwargs: Any,
) -> Any:
    """
    Animate spatial fields over time.

    Parameters
    ----------
    fields : ndarray of shape (n_frames, n_bins) or sequence
        Spatial field values for each frame.
    frame_times : ndarray of shape (n_frames,)
        Timestamps for each frame. Required.
    backend : {'auto', 'napari', 'video', 'html', 'widget'}, default='auto'
        Animation backend to use.
    save_path : str, optional
        Path to save video file.
    speed : float, default=1.0
        Playback speed multiplier relative to real time.
    cmap : str, default='viridis'
        Colormap name.
    vmin : float, optional
        Minimum value for color scaling.
    vmax : float, optional
        Maximum value for color scaling.
    frame_labels : sequence of str, optional
        Labels for each frame.
    overlay_trajectory : ndarray of shape (n_frames, n_dims), optional
        Trajectory to overlay on animation.
    title : str, default='Spatial Field Animation'
        Animation title.
    dpi : int, default=100
        Resolution for video output.
    codec : str, default='h264'
        Video codec.
    bitrate : int, default=5000
        Video bitrate in kbps.
    n_workers : int, optional
        Number of parallel workers for rendering.
    dry_run : bool, default=False
        If True, validate inputs without rendering.
    image_format : {'png', 'jpeg'}, default='png'
        Format for frame images.
    max_html_frames : int, default=500
        Maximum frames for HTML output.
    contrast_limits : tuple of (float, float), optional
        Min/max values for contrast adjustment.
    show_colorbar : bool, default=False
        Whether to display colorbar.
    colorbar_label : str, default=''
        Label for colorbar.
    overlays : list of OverlayProtocol, optional
        Additional overlay objects.
    show_regions : bool or list of str, default=False
        Whether to show region boundaries.
    region_alpha : float, default=0.3
        Transparency for region overlays.
    scale_bar : bool or ScaleBarConfig, default=False
        Whether to show scale bar.
    **kwargs : dict
        Additional backend-specific arguments.

    Returns
    -------
    Any
        Backend-specific return value (viewer, path, or HTML).
    """
    ...