Skip to content

Return an empty array from segment-wise functions for an empty atom array - #1032

Open
musharna wants to merge 2 commits into
biotite-dev:mainfrom
musharna:fix-empty-segment-wise
Open

musharna wants to merge 2 commits into
biotite-dev:mainfrom
musharna:fix-empty-segment-wise

Conversation

@musharna

@musharna musharna commented Nov 9, 2026

Copy link
Copy Markdown

apply_residue_wise(), apply_chain_wise() and apply_segment_wise() return None for an empty AtomArray, although they are typed to return an ndarray:

empty = atom_array[:0]
struc.apply_residue_wise(empty, empty.coord, np.mean, axis=0)  # None

Since #889, get_residue_starts(empty, add_exclusive_stop=True) is [0], so the loop in apply_segment_wise() never runs and the initial None is returned. The comment saying the loop runs at least once no longer holds, so I removed it.

With no segments function is never called, so the shape and dtype of its return value are unknown. This PR uses the dtype of data and its shape without axis: (0,) without axis, (0, 3) for coordinates with axis=0. Happy to change that if you'd rather have something else, e.g. always float.

A related empty-input case: get_residue_positions(empty, []) and get_residue_starts_for(empty, []) raise IndexError. np.asarray([]) is a float array, and with 0 >= 0 the lookup-table branch indexes with it. Empty indices now return an empty int array.

Tests: the new empty-input tests fail on main (None, IndexError) and pass with the change. I couldn't build main here (no Rust toolchain), so I ran tests/structure against the 2.0.0 wheel with the patched segments.py swapped in (the file is identical to main before this change): no new failures. ruff check, ruff format --diff, numpydoc lint and pyright at the lock-file versions are clean.

AI assistance: drafted with Claude Opus 5.5 (claude-opus-5-5); I ran the checks above.

@padix-key padix-key left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi thanks for fixing this.

Comment thread tests/structure/test_chains.py Outdated
]


def test_apply_chain_wise_empty(array):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is basically A duplicate of the corresponding residue test. I would remove it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed in 64502c2.

@codspeed

codspeed Bot commented Nov 11, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 39.88%

1 warning

⚠️ 19 benchmarks with a significant change ran on different hardware

Their base and head results were measured on machines with a different CPU, core count or memory, so the changes might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

❌ 19 regressed benchmarks
✅ 92 untouched benchmarks
⏩ 14 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ benchmark_find_rotatable_bonds 26.7 ms 176.9 ms -84.88%
❌ benchmark_hbond 10.1 ms 29.4 ms -65.58%
❌ benchmark_superimpose[superimpose] 643.7 µs 1,730.1 µs -62.8%
❌ benchmark_similar_kmers 197.3 µs 498.1 µs -60.39%
❌ benchmark_get_assembly 835.9 ms 1,594.8 ms -47.58%
❌ benchmark_score_threshold_rule[11*11*1*1***111] 2 s 3.5 s -43.28%
❌ benchmark_score_threshold_rule[None] 2 s 3.5 s -43.23%
❌ benchmark_superimpose[superimpose_homologs] 33.6 ms 56.5 ms -40.6%
❌ benchmark_superimpose_multi_model[superimpose_homologs] 44.8 ms 67.4 ms -33.53%
❌ benchmark_structural_alphabet_methods[to_3di] 14.1 ms 21.2 ms -33.3%
❌ benchmark_superimpose[superimpose_structural_homologs] 35.8 ms 51.9 ms -31.04%
❌ benchmark_match_kmer_selection[KmerTable-None] 526.4 µs 644.8 µs -18.37%
❌ benchmark_match[KmerTable-None] 623.9 µs 744.4 µs -16.19%
❌ benchmark_set_structure[bcif-True] 32.8 ms 39 ms -15.89%
❌ benchmark_match_kmer_selection[BucketKmerTable(10000)-11*11*1*1***111] 652.7 µs 772.3 µs -15.5%
❌ benchmark_match_kmer_selection[BucketKmerTable(10000)-None] 653.5 µs 768.5 µs -14.96%
❌ benchmark_set_structure[bcif-False] 15.1 ms 17.7 ms -14.41%
❌ benchmark_match[BucketKmerTable(10000)-None] 760.6 µs 876.7 µs -13.24%
❌ benchmark_match[BucketKmerTable(10000)-11*11*1*1***111] 838.9 µs 956.5 µs -12.3%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing musharna:fix-empty-segment-wise (1daa745) with main (2effe8f)2

Open in CodSpeed

Footnotes

  1. 14 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (f7eb9c6) during the generation of this report, so 2effe8f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

This branch was previously deployed

1 inactive (outdated) deployment
publish — 1daa745e Deployed Nov 11, 2026 by musharna via Upload package to GitHub Releases & PyPI #1512
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