Skip to content

nearest takes ties="first" - #38

Merged
marco-mariotti merged 2 commits into
shared-string-rankerfrom
nearest-ties
Aug 13, 2026
Merged

nearest takes ties="first"#38
marco-mariotti merged 2 commits into
shared-string-rankerfrom
nearest-ties

Conversation

@marco-mariotti

Copy link
Copy Markdown
Member

Repo pyranges/ruranges · base shared-string-ranker · head nearest-ties · 5 files, +43 −7

Why

ruranges.numpy.nearest reports every neighbour at the winning distance. With
include_overlaps=True every interval a query overlaps sits at distance 0, so on dense
inputs that is one output row per overlapping pair rather than per query: 1.1 billion rows
against 7.7 million on 100 million hg38-like intervals.

What

ruranges.numpy.nearest(..., ties="first")   # one neighbour per distance
ruranges.numpy.nearest(..., ties="all")     # default, unchanged

Binds ruranges_core::nearest::nearest_with_ties. The default path is the same kernel call
it always was.

ties is parsed in the binding rather than by the kernel's FromStr, which panics. A
PanicException is not an Exception, so except Exception cannot catch it and the
interpreter is aborted — the trap direction fell into, and that pyranges1 had to add a
guard for. An unknown value raises ValueError naming the two options.

Dependencies

Requires ruranges-core 0.1.13 (pyranges/ruranges-core#6). Until that is published the
manifest carries the same development override this repo used for 0.1.12, with a comment
saying to drop it on release.

Version: wheel 0.1.7 → 0.1.8, crate 0.2.5 → 0.2.6.

Verification

Built with maturin develop --release and exercised end to end:

>>> nearest(..., ties="all")     idx1 [0, 0, 0, 1, 1, 1, 2]
>>> nearest(..., ties="first")   idx1 [0, 1, 2]
>>> nearest(..., ties="nope")    ValueError: ties must be 'all' or 'first'; got "nope"

The behaviour itself is tested in ruranges-core; the two libraries downstream of this
binding (pyranges1 1.4.3, polaranges 0.4.0) test the option through it, and
polaranges's randomized oracle checks all three implementations against each other with
ties varying.

marco-mariotti and others added 2 commits August 10, 2026 12:21
`ruranges.numpy.nearest(..., ties="first")` reports one neighbour per
distance instead of every neighbour at that distance, binding
`ruranges_core::nearest::nearest_with_ties`. The default, `"all"`, is
what nearest has always done, and the kernel builds the same output for
it as before.

With `include_overlaps=True` every interval a query overlaps sits at
distance 0, so "all tied neighbours" is "every overlapping interval":
on 100 million hg38-like intervals that is 1.1 billion rows against 7.7
million for one per query. The tied rows are never built, so the saving
is in memory as well as time.

`ties` is parsed in the binding rather than in the kernel's `FromStr`,
which panics. A PanicException is not an Exception, so `except
Exception` cannot catch it and the interpreter is aborted -- the same
trap `direction` fell into and pyranges1 had to guard against.

Requires ruranges-core 0.1.13, patched to the adjacent checkout until it
is published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@marco-mariotti
marco-mariotti merged commit 6538809 into shared-string-ranker Aug 13, 2026
14 of 15 checks passed
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.

1 participant