Skip to content

Validate the bin count in FalsificationResult.plot, delegate the rest (#284) - #454

Open
anevolbap wants to merge 3 commits into
pymc-labs:mainfrom
anevolbap:fix/284-plot-bins-validation
Open

Validate the bin count in FalsificationResult.plot, delegate the rest (#284)#454
anevolbap wants to merge 3 commits into
pymc-labs:mainfrom
anevolbap:fix/284-plot-bins-validation

Conversation

@anevolbap

Copy link
Copy Markdown

Closes #284.

plot() guarded bins with a bare bins < 1. A float like 2.5 passed that check and failed later inside matplotlib, which is the reported symptom. The same expression also raises TypeError: '<' not supported between instances of 'str' and 'int' for a binning strategy name, and likewise for a list or tuple of bin edges, so neither ever reached matplotlib even though Axes.hist accepts both.

The fix checks only what the bin count owns:

  • a non-positive integer raises ValueError, as before
  • a type Axes.hist cannot take at all (float, bool, set) raises ValueError
  • a strategy name or an edge sequence passes through, and matplotlib validates it

Delegating the last case keeps matplotlib's more precise reporting: bins="nonsense" gives 'nonsense' is not a valid estimator for bins, which re-implementing the rules here would lose.

The annotation widens to int | str | Sequence[float] | np.ndarray | None to match what the parameter accepts.

Tests cover the rejected types, the non-positive counts, the accepted counts including np.int64, the strategy names and edge sequences that previously raised TypeError, and matplotlib's estimator error. Also adds the three-variable ~~ block case from the issue.

One follow-up question: refute.py:395 guards its own bins with the same bare bins < 1, and interpret.py:214 declares bins: int | None but passes it to Axes.hist unvalidated. Worth a separate issue, or out of scope?

… rest (pymc-labs#284)

The `bins < 1` guard let a float like 2.5 through to a matplotlib
TypeError, which is what pymc-labs#284 reports. The same guard also raised
`TypeError: '<' not supported` on a strategy name or an edge sequence,
so neither ever reached matplotlib even though it accepts both.

Check only what the bin count owns: reject a non-positive integer, and
reject types matplotlib cannot take at all. Strategy names and edge
sequences now pass through and matplotlib validates them, which it does
more precisely ("'nonsense' is not a valid estimator for `bins`").

Widen the annotation to match what the parameter actually accepts. Add
falsification edge-case tests for the bins cases and for a
three-variable ~~ block.
@anevolbap
anevolbap force-pushed the fix/284-plot-bins-validation branch from 2e0daec to 00f5c35 Compare August 13, 2026 18:11

@drbenvincent drbenvincent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Standards

Clean, focused change. The validation logic is well-scoped: pathmc owns the integer bin-count contract, matplotlib owns strategy names and edge sequences. The explicit bool guard is the right call (True subclasses int), and accepting np.integer is a nice touch beyond plain int.

Docstring and type annotation now match actual behaviour. Error messages name the problem and suggest the fix. Tests are parametrized and readable; the shared result fixture avoids repetition without over-abstracting.

One tiny nit (non-blocking): test_invalid_bins_type_rejected matches "bins must be a positive integer", which is a substring of the longer type-error message rather than the exact wording. Fine for now, but a tighter match on the full type-error string would make the test more precise.

Lint passes locally (make lint).

Spec

Closes #284 completely:

  • plot(bins=2.5) → clear ValueError before matplotlib
  • bins=0 / bins=-1 still rejected; None / 10 still work
  • Three-variable ~~ block test pinned explicitly
  • Bonus coverage: strategy names, edge sequences, np.int64, matplotlib's estimator error — all sensible extensions of the same bug surface

The issue's suggested fix (not isinstance(bins, int)) would have rejected valid np.int64 inputs; this implementation is strictly better.

Follow-up

Agree with the PR description: the same bare bins < 1 pattern in refute.py and the unvalidated bins passthrough in interpret.py are worth a separate issue rather than expanding this PR.

Summary: 1 minor nit (Standards), 0 blockers (Spec). LGTM.

anevolbap and others added 2 commits August 16, 2026 19:32
Both plot(bins=...) tests matched "bins must be a positive integer",
which is a prefix of the type error and the range error alike, so
neither test pinned which one actually fired.

Anchor each pattern to its own message: the range test on the full
string including the parametrized value, the type test on the invariant
prefix up to the variable repr.
@anevolbap

Copy link
Copy Markdown
Author

Filed the follow-up as #460, implemented in #461 (draft, stacked on this branch).

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.

Polish: validate non-integer plot(bins) and add a few falsification edge-case tests

2 participants