Repository navigation
refactor(polytime): keep the raw-import table in an environment extension - #2207
Conversation
…sion #2191 cached the table of imported modules whose import closure declares a raw-family constant in a process-global `IO.Ref` keyed by the import list. Store it instead as the state of a persistent environment extension whose `addImportedFn` creates a `Thunk` over the import header. The scan runs once per file on first use, never in files that do not call the tactics, and proofs elaborated in parallel share one computation instead of each repeating it. Replace the two `Name.toString` prefix tests with one structural predicate, `isRawFamilyName`, used by both the import scan and `rawTransitionPremise`. It classifies every imported constant of the library identically and makes the one-time scan about 40% cheaper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG
|
|
edemaine
left a comment
There was a problem hiding this comment.
APPROVE — reviewer-codex-edemaine, independent non-author review of dc648c3ba0a10fd80653039398dd944ddeac34bf.
Read the complete one-file diff, classification callers, existing memo-scope/raw-boundary tests and pinned Lean extension/Thunk implementation. The empty-entry extension captures only the fixed import header; each imported environment initializes its own lazy table, and forcing the shared thunk memoizes the scan. Current-module declarations and unknown metadata retain conservative exact checks. No local proofs, evidence or changing rule classifications leak into this import cache. The structural name test correctly recognizes the raw namespace component and descendants; filtering/search logic is unchanged.
Hygiene/simp and whitespace checks pass. The author reports the focused/397-client builds and a full imported-name equivalence comparison; I did not repeat those builds, that exhaustive comparison or the timing experiment. At one CI inspection, Guide/title passed and library CI was running. No mathematical, resource or abstract change. Agent review, not human approval; required final validation remains before integration.
Change
#2191 removed a fixed cost of about 0.4 s from every
polytime/pptcall. The cost came from rebuilding the table of imported modules whose import closure declares a raw-family (Turing.MultiTapeTM.ComputableTidily…/ComputesTidily…) constant. #2191 kept the table in a process-globalIO.Refkeyed by the import list. This PR keeps the speed-up and changes how the table is stored. Search is unchanged.rawImportClosureExtis a persistent extension that stores no entries. ItsaddImportedFncreates aThunkover the import header. The table belongs to the environment it was computed from, so there is no global state and no key comparison. The scan runs once per file, on the first call that needs it, and never in files that do not call the tactics. Forcing aThunkis thread-safe, so proofs elaborated in parallel share one scan. With perf(polytime): compute the raw-import classification once per file #2191'sIO.Ref, every theorem body that started before the first scan finished ran its own scan. In a file of fivetheorem … := by polytime, each call took about 0.38 s.isRawFamilyNamematches the name component afterTuring.MultiTapeTM. It replaces two copies of aName.toStringprefix test, one in the import scan and one inrawTransitionPremise. It allocates nothing and makes the one-time scan about 40% cheaper.When #862 deletes the tidy calculus,
isRawFamilyName,rawImportClosureandrawImportClosureExtgo with the rest of the classification memo.Options tried
Complexity.Tactic.PolyTime's imports (343k constants) from about 400 ms to about 230 ms, or about 160 ms with an extraName.isPrefixOfguard. The tactic's meta code is interpreted, though, so the per-name loop still dominates. Every call then costs about 240 ms, against 28 ms with a cached table.PolyTimeTacticwas 56% slower.IO.Ref. Not needed.Timings
lake env leanon the same base (#2191's headfc5455819, prebuilt imports), so the only difference isPolyTime.lean. Each figure is the median of 3 runs withLEAN_NUM_THREADS=4. Per-call figures come fromset_option profiler trueon the trivial goalComputableInPolyTime (fun n : ℕ => n + 1) encUnary encUnary. The machine was shared (load 4–8), so differences under about 1 s are noise.IO.Ref)polytime, first call in a filepolytime, later callstheorems, wall / CPUCrypto.Protocols.MAC.CarterWegman, wall / CPUComplexityTests.PolyTimeTactic, wall / CPUThe five-theorem row shows the parallel case. Under (b), the calls wait for one scan instead of each running their own.
Search behaviour
The table only decides which rules skip the exact premise check (
hasRawPremise). A module with no raw-family constant in its import closure cannot state a raw premise. I checked the new test against the old one over every constant imported byComplexitytogether withComplexityTests.PolyTimeTactic: 677,252 names in 9,548 modules. The two tests agree on every name, 545 of which are raw-family, and the resulting per-module tables are identical (1,374 modules can see the raw family). Rule classifications, certificate filtering and search are therefore unchanged.Validation
--wfail --iofail:ComplexityTests.PolyTime*,PPT*,ProgramCertificateDiagnosticsandMachines.Turing.MultiTape.Internal.PolyTimeBoundary.PPTMemoScopecovers the memo's scoping andPolyTimeBoundarycovers the raw/plain boundary that this table serves.scripts/check.sh --quickagainstorigin/mainfe49fb4ebrebuilt and checked all 397 affected targets (PolyTime.lean's clients acrossComplexity,ComplexityTestsandComplexityAbstracts), followed bylake build Complexity ComplexityTests ComplexityAbstracts --wfail --iofail. Both pass.check_hygiene.py --check-simp,check_tidy_surface.pyandcheck_changed_files.py --base origin/mainpass.Contributes to #622; follows up #2191.
AI assistance
Written and measured by
worker-claude-015dlg8k-tacticcache(Claude Code, Opus) forcoordinator-claude-015dlg8k. Needs independent review.🤖 Generated with Claude Code
https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG