Skip to content

Keep the remaining caching in FrozenModel - #8385

Open
velochy wants to merge 4 commits into
pymc-devs:mainfrom
velochy:model-caching-cleanup
Open

Keep the remaining caching in FrozenModel#8385
velochy wants to merge 4 commits into
pymc-devs:mainfrom
velochy:model-caching-cleanup

Conversation

@velochy

@velochy velochy commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Description

Stacked on #8383. Branched off it because it changes _make_initial_point, which this PR moves; the commit belonging here is the last one. GitHub cannot target a fork branch as the base, hence the overlap.

Follow-up to the review on #8330, which asked for these two to get the same treatment compile_fn already got.

logp_dlogp_function and initial_point were still built around private methods on BaseModel that existed only to give FrozenModel's cache something to wrap, so every model carried a seam for a feature only frozen models use.

Both are now declared in BaseModel and implemented in each final:

  • Model builds its function on every call, as it did before any of this.
  • FrozenModel builds it through a cached method it owns (_value_grad_function, _initial_point_fn).

The part worth sharing — assembling the ValueGradFunction — moves to a module level _make_value_grad_function that both call, so what is duplicated is the argument handling rather than the logic.

make_initial_point_fns_per_chain follows the rename from _make_initial_point to _initial_point_fn, which both finals define, so sampling reuses the compiled initial-point function on a frozen model exactly as before.

Related Issue

Checklist

  • Pre-commit linting/style checks pass
  • Existing tests cover this; the two that assert on cache contents follow the rename

Type of change

  • Maintenance

velochy and others added 3 commits August 2, 2026 15:35
`pymc.util.hashable` and `HashableWrapper` back the memoization helpers,
and mishandle three kinds of values:

- Looking up a key compared the wrapped objects, which raises
  `ValueError: truth value of an array is ambiguous` for arrays and for
  the containers holding them. Keys are now compared by type and by the
  hash `hashable` computes, so a cache can be keyed on a dict of arrays.
- Sets fell through to being pickled whole, which is not stable across
  calls, so equal sets got different hashes and silently defeated the
  cache. They are now hashed by their elements, like lists and dicts.
- `In`/`Out` wrap a variable with compilation options but are hashed by
  identity, and callers rebuild them on every call, so a key holding one
  never matched. They are now hashed by what they hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repeated `pm.sample` on a frozen model still recompiled the initial-point
and trace functions, so the caching a frozen model advertises never fired
on the path that matters most. Both call sites now go through the model,
which the cache-key fixes make possible.

Repeated sampling of a frozen model now compiles nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`logp_dlogp_function` and `initial_point` were still built around private
methods in the base model that only existed to give the frozen model's
cache something to wrap, the same shape that `compile_fn` was cleaned up
from.

Declare both in the base model and implement them in each final instead.
`Model` builds its function every call, `FrozenModel` builds it through a
cached method it owns. The part worth sharing, assembling the
`ValueGradFunction`, moves to a module level helper both call, so the
duplication is the argument handling rather than the logic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.84%. Comparing base (9e94b48) to head (8e7ec5a).

Files with missing lines Patch % Lines
pymc/model/core.py 95.65% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #8385   +/-   ##
=======================================
  Coverage   91.84%   91.84%           
=======================================
  Files         128      128           
  Lines       21227    21253   +26     
=======================================
+ Hits        19495    19520   +25     
- Misses       1732     1733    +1     
Files with missing lines Coverage Δ
pymc/backends/base.py 88.20% <100.00%> (-0.06%) ⬇️
pymc/initial_point.py 99.20% <ø> (ø)
pymc/util.py 83.46% <100.00%> (+2.06%) ⬆️
pymc/model/core.py 93.09% <95.65%> (+0.05%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Both finals override all three, and the base model cannot be
instantiated, so the stubs are unreachable by construction and only show
up as uncovered lines. Same marker the stand-in ops in `logprob` use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@velochy

velochy commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

(comment drafted by AI)

codecov/patch passes (96.4% of the diff), and the lines it flagged are the three raise NotImplementedError stubs in BaseModel. They are unreachable by construction — Model and FrozenModel both override compile_fn, initial_point and logp_dlogp_function, and BaseModel cannot be instantiated — so they are the same stand-in pattern the logprob ops use, and they now carry the same # pragma: no cover.

The codecov/project figure was computed while two jobs were still uploading, so the -4.32% should settle once they land; every test job on the PR passes.

Why did the abstract methods never get any coverage? They kept telling everyone "not implemented" and their subclasses kept overriding them anyway. Classic middle-management. 📋

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