Skip to content

fix: suppress divide-by-zero RuntimeWarning in BagOfWords idf computation - #89

Merged
petercorke merged 6 commits into
mainfrom
fix/bagofwords-idf-divide-by-zero
Aug 16, 2026
Merged

fix: suppress divide-by-zero RuntimeWarning in BagOfWords idf computation#89
petercorke merged 6 commits into
mainfrom
fix/bagofwords-idf-divide-by-zero

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

  • idf = np.log(N / ni) can legitimately divide by zero: after stopword removal (nstopwords > 0), a remaining word can end up with zero occurrences across every image (ni=0), giving idf=inf for that word.
  • This is harmless -- every downstream use of idf multiplies it by that same word's occurrence count, which is also always 0 there, so the 0*inf->nan result gets replaced with 0 before any vector is returned (already the case at both existing call sites). The equivalent divide-by-zero one call site down (v = nid / nd * idf) was already wrapped in np.errstate(divide="ignore", invalid="ignore") -- this one just wasn't.
  • Documented the inf possibility in __init__'s docstring.

Root-caused via RVC3-python's chap12.ipynb (BagOfWords(features, 2_000, nstopwords=50, seed=0)).

Test plan

  • Added a regression check: existing test_retrieve already exercised the nstopwords=50 path that triggers this; wrapped the relevant call in warnings.simplefilter("error", RuntimeWarning) so it fails loudly if this regresses
  • Verified against pre-fix code: fails with the exact original RuntimeWarning: divide by zero encountered in divide at BagOfWords.py:142; passes with the fix

…tion

idf = np.log(N / ni) can legitimately divide by zero: after stopword
removal (nstopwords > 0), a remaining word can end up with zero
occurrences across every image (ni=0), giving idf=inf for that word.
This is harmless -- every downstream use of idf multiplies it by that
same word's occurrence count, which is also always 0 there, so the
0*inf->nan result gets replaced with 0 before any vector is returned
(already the case at both existing call sites). The equivalent
divide-by-zero one call site down (v = nid / nd * idf) was already
wrapped in np.errstate(divide="ignore", invalid="ignore") -- this one
just wasn't.

Documented the inf possibility in __init__'s docstring, and added a
regression test (existing test_retrieve already exercised the
nstopwords=50 path that triggers this; wrapped it in
warnings.simplefilter("error", RuntimeWarning) so it fails loudly if
this regresses).

Root-caused via RVC3-python's chap12.ipynb (BagOfWords(features, 2_000,
nstopwords=50, seed=0)).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@petercorke
petercorke merged commit 615ccd2 into main Aug 16, 2026
31 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