Skip to content

Vectorize line-source weight computation (2)#82

Open
cattabiani wants to merge 28 commits into
mainfrom
katta/vectorize-line-source-weights
Open

Vectorize line-source weight computation (2)#82
cattabiani wants to merge 28 commits into
mainfrom
katta/vectorize-line-source-weights

Conversation

@cattabiani

@cattabiani cattabiani commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Replaces per-segment and per-electrode Python loops with vectorized numpy operations.

fix: #79

Changes

  • _precompute_segment_geometry: extracts segment arrays from positions DataFrame once
  • _get_coeffs_line_source: vectorized h/l/r2 + np.where for sign cases (no loop)
  • _get_coeffs_line_source_batch: multi-electrode broadcasting with chunking (50 at a time, ~4 GB peak)
  • get_weights: batches consecutive LINE_SOURCE electrodes sharing the same sigma
  • Progress logging on rank 0 behind verbose=True

Testing

  • All unit, integration, and MPI integration tests pass
  • New test: scalar vs vectorized comparison on 10 segments × 5 electrodes (rtol=1e-10)
  • Public API unchanged

cattabiani added 23 commits June 4, 2026 14:25
Adapt all example simulation configs to the updated libsonata API:
- Remove electrodes_file from the run section
- Add electrodes_file to each LFP report block
- Remove variable_name from LFP reports (no longer allowed)
- Pick the node manager with cells instead of asserting exactly one
- Use alternate_morphology_formats (neurolucida-asc, h5v1) when available
Replace per-segment Python loop in _get_coeffs_line_source and per-electrode
loop in get_weights with fully vectorized numpy operations.

- Add _precompute_segment_geometry helper for array extraction
- Vectorize h/l/r2 computation with np.where for sign cases
- Add _get_coeffs_line_source_batch for multi-electrode broadcasting
- Batch consecutive LINE_SOURCE electrodes in get_weights
- Chunk by 50 electrodes to bound memory at ~4 GB peak
- Add verbose progress logging (rank 0 only)
- Add scalar-vs-vectorized comparison test (rtol=1e-10)
neurodamus-models 3.0+ removed CMakeLists.txt. Replace the cmake-based
build with nrnivmodl, following the approach used in neurodamus's own CI.

Also bump NEURODAMUS_COMMIT to 4.2.3.
tests/mechanisms/neocortex/ was added after the 4.2.3 tag, so cloning
at that tag fails. Clone at main (default) since these are just standard
.mod files not version-sensitive.
neurodamus-models/mod/ contains files (InhPoissonStim, vecevent) that
duplicate mechanisms already in neurodamus core data/mod/. Matching
neurodamus's own CI approach: only combine core mods + neocortex test
mechanisms. No need to separately copy neurodamus-models/mod/.
SonataReportHelper.mod and SonataReports.mod require bbp/sonata/reports.h
from libsonatareport, which is not installed. These mods are not needed
for BlueRecording tests (the old CI used -DNEURODAMUS_ENABLE_REPORTING=OFF).
Keep SonataReport mods in the build (needed for neurodamus checks)
but disable the reporting lib dependency via compiler flag.
This branch has the electrodes_file fix (no longer reads from
Run object) and includes tests/mechanisms/neocortex/. Will be
pinned to a tag once the next neurodamus release lands.
Adds electrodes_line_source.csv (3 LineSource electrodes) and a
reference weights file for the rat_s1_forelimb_l56_10cells circuit.
The reference was generated from the pre-vectorization code.

Also includes the circuit.py change to use nd._sonata_circuits
for morphology path resolution.
neurodamus already resolves alternate morphology formats in its
circuit config parsing. Use MorphologyPath directly instead of
duplicating the logic.
Move all coefficient computation functions (line-source, point-source,
reciprocity, CSD) and SegmentGeometry into bluerecording/physics.py.
weights.py keeps orchestration, H5 I/O, and electrode parsing.

Backward-compatible aliases are re-exported from weights.py so
existing test imports continue to work.
Tests now import physics functions from bluerecording.physics
with their proper public names.
…_coeffs_batch

- Add _point_source_coeffs_batch as the shared DRY helper
- Add get_coeffs_point_source_batch for multi-electrode point source
- Line-source batch now reuses _point_source_coeffs_batch for soma segments
- get_weights batches consecutive PointSource electrodes (same as LineSource)
…utive

get_weights now groups all electrodes by type and sigma upfront,
batches each group in one shot, then places results back in
original electrode order. Removes the old consecutive-walking
_collect_and_compute_* helpers.

This handles interleaved electrode types correctly (e.g.
LineSource, PointSource, LineSource all get batched properly).
Verifies that get_weights correctly groups electrodes by (type, sigma),
computes batches, and reassembles results in original order. Compares
against per-electrode individual computation.
@cattabiani cattabiani marked this pull request as draft June 18, 2026 14:47
@cattabiani cattabiani self-assigned this Jun 18, 2026
Batch functions now accept sigma as a per-electrode array. get_weights
groups only by type (LineSource vs PointSource), passes the sigma array
for that group, and places results back in original order.

No need to sub-group by sigma value anymore.
@cattabiani cattabiani changed the title Vectorize line-source weight computation Vectorize line-source weight computation (2) Jun 25, 2026
@cattabiani cattabiani marked this pull request as ready for review June 25, 2026 07:10
@cattabiani cattabiani requested a review from mgeplf June 25, 2026 07:10
@cattabiani cattabiani requested a review from WeinaJi June 25, 2026 07:10
@cattabiani cattabiani changed the base branch from katta/per-report-electrodes-file to main June 29, 2026 08:28

@mgeplf mgeplf left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only made it so far today, I'm very unfamiliar with this code, so not sure how useful I can be

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not move the tests of physics.py out to a test_physics.py?

Comment thread bluerecording/weights.py

Dispatches to the appropriate coefficient function based on each
electrode's type and returns the concatenated result.
Groups electrodes by (type, sigma), computes each group as a batch,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how much does computes each group as a batch actually save? how many electrodes do we expect? there seems to be a lot of complexity to batch and unbatch them, and I'm not convinced we want that complexity - however, having some numbers could convince me

Comment thread bluerecording/weights.py
electrode_type = electrode.type
other_indices.append(idx)

# --- Batch compute LINE_SOURCE ---

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these comments, as usual, don't tell me anything that the if line_source_indices doesn't already

Comment thread bluerecording/weights.py

elif electrode_type is ElectrodeType.DIPOLE_RECIPROCITY:
center = mid_positions.mean(axis=1)
coeffs = get_coeffs_dipole_reciprocity(mid_positions, path_to_fields[reciprocity_idx], center)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why doesn't get_coeffs_dipole_reciprocity calculate the center?



def test_get_coeffs_line_source():
def testget_coeffs_line_source():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why no underscore between test and get?

columns = weights.columns
node_ids = np.unique(cols[:, 0])

from bluerecording.weights import _get_segment_midpts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be imported at the top?

Comment thread bluerecording/physics.py

start_pos: np.ndarray # (N_line_segments, 3) — segment start positions (µm)
end_pos: np.ndarray # (N_line_segments, 3) — segment end positions (µm)
seg_lengths: np.ndarray # (N_line_segments,) — lengths in meters

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this in meters when everything else is in um?

Comment thread bluerecording/physics.py

def precompute_segment_geometry(positions: pd.DataFrame) -> SegmentGeometry:
"""Precompute segment geometry arrays. Use ``SegmentGeometry.from_positions()`` instead."""
return SegmentGeometry.from_positions(positions)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary? I couldn't find the tests for SegmentGeometry::from_positions since there is this wrapper

Comment thread bluerecording/physics.py
positions: DataFrame of segment boundary positions (µm), shape
``(3, N_columns)`` with MultiIndex columns ``(gid, section_id)``.
"""
n_cols = len(positions.columns)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this df arranged where the number of columns is unbounded? I find it very strange; it makes the manipulations much harder, imo, as opposed the "traditional" layout.

Comment thread bluerecording/physics.py
) -> float:
"""Compute the line-source coefficient for a single segment.

All positions are in µm and are converted to m internally.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

converted to m internally why? if the natural scale is micro, seems strange to change it

Not sure why this internal detail is described in the docstring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants