Skip to content

Compile UDFs under Numba's numpy error model - #588

Merged
eriknw merged 1 commit into
mainfrom
08-numba-numpy-error-model
Sep 18, 2026
Merged

eriknw merged 1 commit into
mainfrom
08-numba-numpy-error-model

Conversation

@eriknw

@eriknw eriknw commented Aug 4, 2026

Copy link
Copy Markdown
Member

A UDF that divides by zero silently corrupted the result. Numba's default
error model raises ZeroDivisionError inside the cfunc, where Numba prints
the traceback and returns without writing, so GraphBLAS keeps whatever the
output element already held. Measured at 6f1eb02:

def _idiv(x, y):
    return x // y
op = BinaryOp.register_anonymous(_idiv, "_probe_idiv")
op(Vector.from_coo([0, 1], [10, 20]) & Vector.from_coo([0, 1], [2, 0])).new()
-> [5, 5]        # numpy gives [5, 0]; the 5 is the previous element

The float case is the same shape: [0.5, 0.5] where numpy gives
[0.5, inf]. The only user-visible signal is an "Exception ignored"
traceback on stderr.

Pass error_model="numpy" at every numba.njit and numba.cfunc site for
user-defined ops. Setting it on the cfunc alone is not enough: _build
calls .compile(sig) on the Dispatcher first, a Dispatcher keeps one
compilation per signature, and whichever compile happens first fixes the
model. Reverting only the Dispatcher argument, leaving it on the cfunc,
reproduces [5, 5].

This changes what a dividing UDF returns, but the behaviour it replaces is
an unwritten element rather than an error, so there is no correct result
being taken away.


Stack created with GitHub Stacks CLI • Give Feedback 💬

@eriknw
eriknw marked this pull request as ready for review August 4, 2026 16:07
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch 2 times, most recently from 2d14098 to c4adb70 Compare August 5, 2026 00:06
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch 2 times, most recently from 6908f99 to b5525f1 Compare August 5, 2026 17:44
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from b5525f1 to e7721a6 Compare August 5, 2026 18:03
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch 2 times, most recently from d9bf85c to fb5254d Compare August 6, 2026 07:59
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from fb5254d to c877578 Compare August 6, 2026 15:39
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from c877578 to c24208c Compare August 6, 2026 15:41
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch 2 times, most recently from 0861b81 to d895629 Compare August 6, 2026 20:41
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from d895629 to ae5ca33 Compare August 7, 2026 02:48
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from ae5ca33 to 611eab0 Compare August 7, 2026 05:09
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from 611eab0 to 53eb255 Compare August 26, 2026 17:30
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from 53eb255 to 35c4009 Compare September 18, 2026 03:09
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from 35c4009 to d8dfc4a Compare September 18, 2026 03:46
Base automatically changed from 07-selectop-borrowed-handle-lifetime to main September 18, 2026 17:34
A UDF that divides by zero silently corrupted the result. Numba's default
error model raises ZeroDivisionError inside the cfunc, where Numba prints
the traceback and returns without writing, so GraphBLAS keeps whatever the
output element already held. Measured at 6f1eb02:

    def _idiv(x, y):
        return x // y
    op = BinaryOp.register_anonymous(_idiv, "_probe_idiv")
    op(Vector.from_coo([0, 1], [10, 20]) & Vector.from_coo([0, 1], [2, 0])).new()
    -> [5, 5]        # numpy gives [5, 0]; the 5 is the previous element

The float case is the same shape: `[0.5, 0.5]` where numpy gives
`[0.5, inf]`. The only user-visible signal is an "Exception ignored"
traceback on stderr.

Pass `error_model="numpy"` at every `numba.njit` and `numba.cfunc` site for
user-defined ops. Setting it on the cfunc alone is not enough: `_build`
calls `.compile(sig)` on the Dispatcher first, a Dispatcher keeps one
compilation per signature, and whichever compile happens first fixes the
model. Reverting only the Dispatcher argument, leaving it on the cfunc,
reproduces `[5, 5]`.

This changes what a dividing UDF returns, but the behaviour it replaces is
an unwritten element rather than an error, so there is no correct result
being taken away.
@eriknw
eriknw force-pushed the 08-numba-numpy-error-model branch from d8dfc4a to 7f9f3f3 Compare September 18, 2026 17:34
@eriknw

eriknw commented Sep 18, 2026

Copy link
Copy Markdown
Member Author

This was a good catch by my agents! The "numpy" error model is indeed what we want, not the "python" error model that tries and fails to raise an exception. Merging before the latest CI can complete (it was green before, and this let's us review 589 sooner, which fixes the segfault in CI)

@eriknw
eriknw merged commit f093e36 into main Sep 18, 2026
4 checks passed
@eriknw
eriknw deleted the 08-numba-numpy-error-model branch September 18, 2026 17:39
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