Discretize continuous N-dimensional spatial environments into bins/nodes with connectivity graphs
Project description
neurospatial
neurospatial is a Python library for discretizing continuous N-dimensional spatial environments into bins/nodes with connectivity graphs. It provides tools for spatial analysis, particularly for neuroscience applications involving place fields, position tracking, and spatial navigation.
Whether you're analyzing animal navigation data, modeling place cells, or working with any spatial discretization problem, neurospatial gives you flexible, powerful tools to represent and analyze spatial environments.
Key Features
Core Capabilities
- Multiple Layout Engines: Choose from regular grids, hexagonal tessellations, masked regions, polygon-bounded areas, triangular meshes, and 1D linearized tracks
- Automatic Bin Detection: Infer active bins from data samples with morphological operations (dilation, closing, hole filling)
- Connectivity Graphs: Built-in NetworkX graphs with mandatory node/edge metadata for spatial queries
- 1D Linearization: Transform complex 2D environments into 1D linearized coordinates for track-based analysis
- Region Support: Define and manage named regions of interest (ROIs) with immutable semantics
- Environment Composition: Merge multiple environments with automatic bridge inference
What neurospatial does that others don't
- generalizes analyses to 2D/3D and arbitrary shapes
- Geodesic distance computations (distances are not just Euclidean, they respect environment topology)
- Spatial kernels that respect connectivity graphs (smoothing is not just Gaussian, it respects environment topology)
- Interactive and static visualization of environments and spatial fields
- Comprehensive simulation subpackage for generating synthetic trajectories, neural activity, and spikes with ground truth
- unified analyses (the field reimplements many common neuroscience spatial analyses), this is designed to be a one-stop shop for spatial environment discretization and analysis so that the field is using consistent methods
- python-native with no matlab dependencies
- gpu acceleration
Spatial Analysis Operations
- Trajectory Analysis: Convert trajectories to bin sequences, compute empirical transition matrices with adjacency filtering
- Occupancy Mapping: Time-in-bin computation with speed filtering, gap handling, and optional kernel smoothing (including linear time allocation for accurate boundary handling)
- Field Smoothing: Diffusion kernel smoothing on graphs with volume correction for continuous fields
- Interpolation: Evaluate bin-valued fields at arbitrary points (nearest neighbor or bilinear/trilinear for grids)
- Distance Fields: Compute geodesic and Euclidean distances, k-hop neighborhoods, connected components
- Field Utilities: Normalize, clamp, combine fields; compute KL/JS divergence and cosine distance
- Environment Operations: Subset/crop environments by regions or polygons, rebin grids, copy with cache management
Field Animation
- Multi-Backend Animation: Visualize spatial fields over time with 4 specialized backends
- Napari: GPU-accelerated interactive viewer with lazy loading (100K+ frames)
- Video: Parallel MP4/WebM export with ffmpeg (unlimited frames)
- HTML: Standalone player with instant scrubbing (up to 500 frames)
- Jupyter Widget: Notebook integration with play/pause controls
- Auto-Selection: Intelligent backend selection based on file extension, dataset size, and environment
- Large-Scale Support: Memory-mapped arrays, LRU caching, frame subsampling for hour-long sessions
- Trajectory Overlays: Overlay animal trajectories on animated fields (Napari backend)
Installation
From PyPI
pip install neurospatial
Or with uv:
uv pip install neurospatial
For Development
# Clone the repository
git clone https://github.com/edeno/neurospatial.git
cd neurospatial
# Install with uv (recommended)
uv sync
# Or with pip
pip install -e ".[dev]"
Note: This project uses uv for package management. If you have uv installed, all commands should be prefixed with uv run (e.g., uv run pytest).
Tested Dependency Versions
neurospatial v0.4.0 has been tested with the following dependency versions:
| Package | Tested Version |
|---|---|
| Python | 3.13.5 |
| numpy | 2.3.4 |
| pandas | 2.3.3 |
| matplotlib | 3.10.7 |
| networkx | 3.5 |
| scipy | 1.16.3 |
| scikit-learn | 1.7.2 |
| shapely | 2.1.2 |
| track-linearization | 2.4.0 |
These versions represent the tested configuration. neurospatial likely works with a range of versions for each dependency, but these specific versions have full test coverage.
Optional Dependencies
For animation features, install optional dependencies:
# Napari backend (GPU-accelerated interactive viewer)
pip install "napari[all]>=0.4.18,<0.6"
# Jupyter widget backend (notebook integration)
pip install "ipywidgets>=8.0,<9.0"
# Video backend (requires system ffmpeg installation)
# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt-get install ffmpeg
# Windows (via chocolatey)
choco install ffmpeg
# Conda
conda install -c conda-forge ffmpeg
Note: HTML backend requires no additional dependencies. Video backend performance scales with CPU cores (use n_workers parameter for parallel rendering).
Quickstart
Here's a minimal example showing how to create an environment from spatial data:
import numpy as np
from neurospatial import Environment
# Generate some 2D position data (e.g., from animal tracking)
# Shape: (n_samples, 2) for x, y coordinates in centimeters
position_data = np.array([
[0.0, 0.0],
[5.0, 5.0],
[10.0, 10.0],
[15.0, 5.0],
[20.0, 0.0],
# ... more positions
])
# Create an environment with 2 cm bins
env = Environment.from_samples(
positions=position_data,
bin_size=2.0, # 2 cm bins
name="OpenField"
)
# Query the environment
print(f"Environment has {env.n_bins} bins")
print(f"Dimensions: {env.n_dims}D")
print(f"Extent: {env.dimension_ranges}")
# Map a point to its bin
point = np.array([[10.5, 10.2]])
bin_idx = env.bin_at(point)
print(f"Point {point[0]} is in bin {bin_idx[0]}")
# Find neighbors of a bin
neighbors = env.neighbors(bin_idx[0])
print(f"Bin {bin_idx[0]} has {len(neighbors)} neighbors")
# Visualize the environment
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
env.plot(ax=ax)
plt.show()
Your First Place Field
A neuroscientist's first task is usually: "I have an animal moving around and spikes from a neuron — show me where the cell fires." Here is the end-to-end pipeline using simulated data so you can run it right now without any setup:
import numpy as np
import matplotlib.pyplot as plt
from neurospatial import Environment
from neurospatial.encoding import compute_spatial_rate
from neurospatial.simulation import generate_population_spikes
from neurospatial.simulation.models import PlaceCellModel
from neurospatial.simulation.trajectory import simulate_trajectory_ou
# 1. Build a square open-field environment (60 × 60 cm, 2 cm bins).
xx, yy = np.meshgrid(np.linspace(0, 60, 31), np.linspace(0, 60, 31))
arena_corners = np.column_stack([xx.ravel(), yy.ravel()])
env = Environment.from_samples(arena_corners, bin_size=2.0)
env.units = "cm"
# 2. Simulate ten minutes of foraging.
positions_t, times = simulate_trajectory_ou(
env, duration=600.0, speed_units="cm", seed=42,
)
# 3. Build a place cell tuned to the middle of the arena.
cell = PlaceCellModel(env, center=np.array([30.0, 30.0]), width=8.0,
max_rate=15.0, seed=42)
# 4. Generate the spike train from the cell's firing model + trajectory.
spike_times = generate_population_spikes(
[cell], positions_t, times, seed=42, show_progress=False,
)[0]
# 5. Recover the place field from spikes + trajectory.
result = compute_spatial_rate(
env, spike_times, times, positions_t,
smoothing_method="diffusion_kde", bandwidth=5.0,
)
# 6. Plot.
fig, ax = plt.subplots()
result.plot(ax=ax)
ax.set_title("Place field recovered from simulated spikes")
plt.show()
The recovered field will be a Gaussian-like blob centered near (30, 30) — the same location we put the simulated cell. From here you can
swap in real spike times, change the trajectory, add more cells, or
detect place fields with detect_place_fields.
See example 11
for the full tutorial.
Core Concepts
Bins and Active Bins
neurospatial discretizes continuous space into bins (also called nodes). Each bin represents a region of space with a center coordinate and (optionally) a size.
Active bins are bins that contain actual data or are considered part of the environment. When creating an environment from data samples, neurospatial can automatically infer which bins should be active based on:
- Data occupancy (bins with enough samples)
- Morphological operations (filling gaps, connecting nearby regions)
- Explicit masks or polygons
This is essential for neuroscience applications where you want to focus on visited areas while excluding walls, obstacles, or unvisited regions.
Connectivity Graphs
Each environment includes a connectivity graph (NetworkX Graph) defining which bins are neighbors. This graph powers spatial queries like:
- Finding shortest paths between locations
- Computing geodesic (manifold) distances
- Determining local neighborhoods
The connectivity graph includes mandatory metadata on all nodes and edges (positions, distances, vectors, indices) for robust spatial operations.
Layout Engines
Layout engines define how space is discretized. Available engines include:
- RegularGridLayout: Standard rectangular/cuboid grids
- HexagonalLayout: Hexagonal tessellations (more uniform neighbor distances)
- GraphLayout: 1D linearized tracks for maze/track experiments
- MaskedGridLayout: Grids with arbitrary active/inactive regions
- ImageMaskLayout: Binary image-based layouts
- ShapelyPolygonLayout: Polygon-bounded grids
- TriangularMeshLayout: Triangular tessellations
You typically don't interact with layout engines directly; instead, use the Environment factory methods which select the appropriate engine for you.
Common Use Cases
1. Analyzing Animal Position Data
import numpy as np
from neurospatial import Environment
# In a real analysis, replace these three lines with your own loader:
# times = load_timestamps() # shape (n_timepoints,) in seconds
# position = load_tracking() # shape (n_timepoints, 2)
# speeds = load_speeds() # shape (n_timepoints,)
# Here we synthesize a 1 Hz, 60 s random walk in a 100x100 cm arena so the
# block is runnable end-to-end.
rng = np.random.default_rng(0)
times = np.linspace(0.0, 60.0, 60)
position = np.cumsum(rng.normal(0, 3.0, size=(60, 2)), axis=0) + 50.0
speeds = np.linalg.norm(np.diff(position, axis=0, prepend=position[:1]), axis=1) / np.gradient(times)
# Create environment with 5 cm bins, auto-detect active areas
env = Environment.from_samples(
positions=position,
bin_size=5.0, # cm
infer_active_bins=True,
dilate=True, # Expand active region
fill_holes=True, # Fill small gaps
name="Experiment1_OpenField"
)
# Compute occupancy with speed filtering
occupancy = env.occupancy(
times=times,
positions=position,
speed=speeds,
min_speed=2.5, # cm/s - filter slow periods
bandwidth=10.0 # cm - smooth the occupancy map
)
# Analyze movement patterns
transitions = env.transitions(times=times, positions=position, normalize=True)
bin_sequence = env.bin_sequence(times=times, positions=position, dedup=True)
2. Creating Masked Environments
from shapely.geometry import Polygon
# Define a circular arena (80 cm diameter)
theta = np.linspace(0, 2*np.pi, 100)
boundary = np.column_stack([40 * np.cos(theta), 40 * np.sin(theta)])
polygon = Polygon(boundary)
# Create environment bounded by polygon
env = Environment.from_polygon(
polygon=polygon,
bin_size=2.5, # cm
name="CircularArena"
)
3. Linearizing Track Mazes
import networkx as nx
# Define track graph (e.g., plus maze)
graph = nx.Graph()
graph.add_node(0, pos=(0.0, 0.0)) # center
graph.add_node(1, pos=(0.0, 50.0)) # north arm
graph.add_node(2, pos=(50.0, 0.0)) # east arm
graph.add_node(3, pos=(0.0, -50.0)) # south arm
graph.add_node(4, pos=(-50.0, 0.0)) # west arm
graph.add_edge(0, 1, edge_id=0, distance=50.0)
graph.add_edge(0, 2, edge_id=1, distance=50.0)
graph.add_edge(0, 3, edge_id=2, distance=50.0)
graph.add_edge(0, 4, edge_id=3, distance=50.0)
# Create 1D linearized environment
env = Environment.from_graph(
graph=graph,
edge_order=[(4, 0), (0, 1), (0, 2), (0, 3)], # traversal order
edge_spacing=0.0, # no gaps between edges
bin_size=2.0, # cm
name="PlusMaze"
)
# Convert 2D positions to 1D linearized coordinates
position_2d = np.array([[25.0, 0.0]]) # halfway down east arm
position_1d = env.to_linear(position_2d)
# And back
position_2d_reconstructed = env.linear_to_nd(position_1d)
4. Defining Regions of Interest
from shapely.geometry import Point
# Create environment
env = Environment.from_samples(position_data, bin_size=3.0)
# Define reward zones as circular regions (buffered points)
reward1_polygon = Point(10.0, 10.0).buffer(5.0) # 5 cm radius circle
reward2_polygon = Point(30.0, 30.0).buffer(5.0)
env.regions.add("RewardZone1", polygon=reward1_polygon)
env.regions.add("RewardZone2", polygon=reward2_polygon)
# Or add a point region
env.regions.add("StartLocation", point=(0.0, 0.0))
# Access region information
print(f"Number of regions: {len(env.regions)}")
print(f"Region names: {env.regions.list_names()}")
# Get region statistics
area = env.regions.area("RewardZone1")
center = env.regions.region_center("RewardZone1")
print(f"RewardZone1 area: {area:.2f}, center: {center}")
Simulation
neurospatial includes a comprehensive simulation subpackage for generating synthetic spatial data, neural activity, and spike trains. This is essential for testing analysis pipelines, validating algorithms against ground truth, and creating educational examples.
Quick Example
import numpy as np
from neurospatial import Environment
from neurospatial.simulation import (
simulate_trajectory_ou,
PlaceCellModel,
generate_poisson_spikes,
)
# Sample positions used to infer the active region (e.g., a 100x100 cm arena).
# In real use, replace this with your own tracking data.
arena_data = np.random.default_rng(0).uniform(0, 100, size=(2000, 2))
# Create environment
env = Environment.from_samples(arena_data, bin_size=2.0)
env.units = "cm" # Required for trajectory simulation
# Generate realistic trajectory using Ornstein-Uhlenbeck process.
# speed_units must match env.units exactly (no auto-conversion in v0.4).
positions, times = simulate_trajectory_ou(
env,
duration=120.0, # seconds
speed_units="cm",
speed_mean=8.0, # cm/s
coherence_time=0.7, # smoothness parameter
seed=42
)
# Create place cell with known ground truth
place_cell = PlaceCellModel(
env,
center=[50.0, 75.0], # field center in cm
width=10.0, # field width (Gaussian std)
max_rate=25.0 # peak firing rate in Hz
)
# Generate spikes
firing_rates = place_cell.firing_rate(positions, times)
spike_times = generate_poisson_spikes(firing_rates, times, seed=42)
# Validate with neurospatial analysis
from neurospatial.encoding import compute_spatial_rate
result = compute_spatial_rate(env, spike_times, times, positions)
detected_field = result.firing_rate
# Compare detected field to ground truth
true_center = place_cell.ground_truth['center']
print(f"True field center: {true_center}")
print(f"Detected peak: {env.bin_centers[detected_field.argmax()]}")
Available Features
-
Trajectory Simulation
- Ornstein-Uhlenbeck process for realistic exploration
- Structured trajectories (laps, alternation tasks)
- Boundary handling (reflect, periodic, stop)
-
Neural Models
- Place cells (Gaussian fields, direction-selective, speed-gated)
- Boundary/border cells (distance-tuned)
- Grid cells (hexagonal patterns)
- All models expose
.ground_truthfor validation
-
Spike Generation
- Inhomogeneous Poisson process
- Refractory period constraints
- Population spike generation with progress tracking
-
High-Level API
- Pre-configured sessions:
open_field_session(),linear_track_session(), etc. - Automated validation:
validate_simulation()compares detected vs true parameters - One-call workflow:
simulate_session()handles trajectory + models + spikes
- Pre-configured sessions:
Learn More
See the comprehensive tutorial: Simulation Workflows Notebook for complete examples including:
- Quick start with pre-configured sessions
- Low-level API for custom workflows
- All cell types (place, boundary, grid)
- Validation and visualization
- Performance tips and customization
Animation
Visualize how spatial fields evolve over time with multi-backend animation support.
Quick Example
import numpy as np
from neurospatial import Environment
from neurospatial.animation import subsample_frames
from neurospatial.encoding import compute_spatial_rate
# Assumes you already have:
# positions: shape (n_samples, 2) animal trajectory
# times: shape (n_samples,) timestamps in seconds
# spikes: list of length 30, each entry is a 1-D array of spike
# times for one cell (see "Your First Place Field" above
# for an end-to-end simulation that produces these arrays)
env = Environment.from_samples(positions, bin_size=2.5)
fields = [
compute_spatial_rate(env, spikes[i], times, positions).firing_rate
for i in range(30)
]
# frame_times is required: one timestamp per field (seconds)
frame_times = np.arange(len(fields)) / 30.0 # 30 Hz
# Interactive Napari viewer (best for exploration)
env.animate_fields(fields, frame_times=frame_times, backend="napari")
# Video export with parallel rendering (best for presentations)
env.clear_cache() # Required for parallel rendering
env.animate_fields(
fields, frame_times=frame_times,
save_path="animation.mp4", n_workers=4,
)
# HTML standalone player (best for sharing)
env.animate_fields(fields, frame_times=frame_times, save_path="animation.html")
# Jupyter widget (best for notebooks)
env.animate_fields(fields, frame_times=frame_times, backend="widget")
Backend Selection Guide
| Backend | Best For | Max Frames | Output |
|---|---|---|---|
| Napari | Large datasets (100K+), interactive exploration | Unlimited* | Live viewer |
| Video | Presentations, publications | Unlimited | .mp4, .webm |
| HTML | Sharing, web embedding | 500 | .html |
| Widget | Jupyter notebooks | ~1000 | Interactive widget |
* Limited only by disk space (lazy loading with LRU cache)
Large-Scale Datasets
For sessions with 100K+ frames (e.g., 1-hour recording at 250 Hz):
import numpy as np
from neurospatial.animation import subsample_frames
# Use memory-mapped arrays (doesn't load into RAM)
fields = np.memmap('fields.dat', dtype='float32', mode='w+',
shape=(900_000, env.n_bins))
# Napari lazy-loads from disk (no data loading); frame_times is required
frame_times = np.arange(len(fields)) / 250.0 # 250 Hz acquisition
env.animate_fields(fields, frame_times=frame_times, backend="napari")
# Or subsample for video export (250 Hz → 30 fps)
subsampled = subsample_frames(fields, source_fps=250, target_fps=30)
sub_times = np.arange(len(subsampled)) / 30.0
env.clear_cache()
env.animate_fields(
subsampled, frame_times=sub_times,
save_path="replay.mp4", n_workers=4,
)
Learn More
- Animation User Guide: Complete documentation with troubleshooting
- Animation Examples Notebook: Working examples for all backends
- Common use cases: place field dynamics, theta sequences, remapping, population activity
Documentation
- Documentation Home: Complete documentation site
- Getting Started: Installation and quickstart guide
- User Guide: Detailed feature documentation
- API Reference: Auto-generated API documentation
- Examples: Jupyter notebooks with real-world use cases
- Glossary: Spatial-coding vocabulary reference
- Contributing: Guidelines for contributors
- CLAUDE.md: Development guide for Claude Code users
- GitHub Issues: Bug reports and feature requests
Project Structure
neurospatial/
├── src/neurospatial/
│ ├── environment/ # Main Environment class (modular package)
│ │ ├── core.py # Core dataclass with state and properties
│ │ ├── factories.py # Factory classmethods (from_samples, from_graph, etc.)
│ │ ├── queries.py # Spatial query methods
│ │ ├── trajectory.py # Trajectory analysis (occupancy, transitions)
│ │ ├── transforms.py # Rebin/subset operations
│ │ ├── fields.py # Spatial field operations (smooth, interpolate)
│ │ ├── metrics.py # Environment metrics and properties
│ │ ├── serialization.py # Save/load methods
│ │ ├── regions.py # Region operations
│ │ ├── visualization.py # Plotting methods (includes animate_fields)
│ │ └── decorators.py # check_fitted decorator
│ ├── animation/ # Field animation
│ │ ├── core.py # Main dispatcher and subsample_frames utility
│ │ ├── rendering.py # Rendering utilities (colormap, RGB conversion)
│ │ ├── _parallel.py # Parallel frame rendering for video backend
│ │ └── backends/ # Backend implementations
│ │ ├── napari_backend.py # GPU-accelerated interactive viewer
│ │ ├── video_backend.py # Parallel MP4/WebM export with ffmpeg
│ │ ├── html_backend.py # Standalone HTML player
│ │ └── widget_backend.py # Jupyter widget integration
│ ├── composite.py # CompositeEnvironment for multi-env merging
│ ├── alignment.py # Probability distribution transforms
│ ├── transforms.py # 2D affine transformations
│ ├── layout/
│ │ ├── base.py # LayoutEngine protocol
│ │ ├── factories.py # Layout factory functions
│ │ └── engines/ # Concrete layout implementations
│ └── regions/
│ ├── core.py # Region and Regions classes
│ └── serialization.py # JSON I/O for regions
└── tests/ # Comprehensive test suite (1,185+ tests)
Requirements
- Python 3.10+
- numpy >= 1.24.0
- pandas >= 2.0.0
- matplotlib >= 3.7.0
- networkx >= 3.0
- scipy >= 1.10.0
- scikit-learn >= 1.2.0
- shapely >= 2.0.0
- track-linearization >= 2.4.0
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run the test suite (
uv run pytest) - Run code quality checks (
uv run ruff check . && uv run ruff format .) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See CLAUDE.md for detailed development guidelines.
Citation
If you use neurospatial in your research, please cite:
@software{neurospatial2026,
author = {Denovellis, Eric},
title = {neurospatial: Spatial environment discretization for neuroscience},
year = {2026},
url = {https://github.com/edeno/neurospatial},
version = {0.4.0}
}
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built with NetworkX for graph operations
- Uses Shapely for geometric operations
- Leverages track-linearization for 1D linearization
Contact
Eric Denovellis
- Email: eric.denovellis@ucsf.edu
- GitHub: @edeno
- Issues: GitHub Issues
Status: Alpha - API may change. Contributions and feedback welcome!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neurospatial-0.5.0.tar.gz.
File metadata
- Download URL: neurospatial-0.5.0.tar.gz
- Upload date:
- Size: 25.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ade0ca92739e1cb728e3d40711402b55a82f603fa1150d3b21fa67af14a8ffca
|
|
| MD5 |
5357a1a4eec614cb293416db72610561
|
|
| BLAKE2b-256 |
7d4202b7b433853dc703bb0ab2fbbbf33ac614492256c46cddd5c4ecb788abf3
|
Provenance
The following attestation bundles were made for neurospatial-0.5.0.tar.gz:
Publisher:
publish.yml on edeno/neurospatial
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
neurospatial-0.5.0.tar.gz -
Subject digest:
ade0ca92739e1cb728e3d40711402b55a82f603fa1150d3b21fa67af14a8ffca - Sigstore transparency entry: 1713907032
- Sigstore integration time:
-
Permalink:
edeno/neurospatial@9552a270c993f6db8943bdc8030940b1ae42cc42 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/edeno
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9552a270c993f6db8943bdc8030940b1ae42cc42 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file neurospatial-0.5.0-py3-none-any.whl.
File metadata
- Download URL: neurospatial-0.5.0-py3-none-any.whl
- Upload date:
- Size: 1.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb62f3eee31c99d60f4eefee18d154ca99ac1478b854cc3a92fe9fe5a3657abc
|
|
| MD5 |
5821727c9c9815c36c6d7b57f74ed077
|
|
| BLAKE2b-256 |
a0133f8f919f68990994b033d0ce9a32685776300a718a3f4a5c7f914767bd9b
|
Provenance
The following attestation bundles were made for neurospatial-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on edeno/neurospatial
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
neurospatial-0.5.0-py3-none-any.whl -
Subject digest:
bb62f3eee31c99d60f4eefee18d154ca99ac1478b854cc3a92fe9fe5a3657abc - Sigstore transparency entry: 1713907093
- Sigstore integration time:
-
Permalink:
edeno/neurospatial@9552a270c993f6db8943bdc8030940b1ae42cc42 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/edeno
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9552a270c993f6db8943bdc8030940b1ae42cc42 -
Trigger Event:
workflow_dispatch
-
Statement type: