Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c7d6737
[wip] preserved baseline: stable_members mitigations + new failing test
May 17, 2026
93f597e
[fix] Option A: expand dataclass-instance args in @qd.func calls from…
May 17, 2026
c25f49c
[test] nested dataclasses + chained @qd.func calls from data_oriented…
May 17, 2026
8f64016
[Perf] Prune unused @qd.data_oriented ndarrays via existing pruning m…
hughperkins May 17, 2026
aa9a88f
[Fix] Fastcache hasher: skip QuadrantsCallable/BoundQuadrantsCallable…
hughperkins May 17, 2026
fd8c440
[Perf] Don't over-mark ndarrays during @qd.func dataclass-arg expansion
hughperkins May 17, 2026
e3a3d88
[Perf] TemplateMapper.lookup: only walk template-slot args, cache per…
hughperkins May 18, 2026
067a471
[Fix] Walker robustness: cycle-safe + Pydantic-metaclass-safe is_data…
hughperkins May 18, 2026
cc1e380
[Style] Apply pre-commit (black + ruff): import order, single-line co…
hughperkins May 18, 2026
34f8532
[Fix] stable_members: tolerate opaque members in fastcache hasher + c…
hughperkins May 18, 2026
5e54902
[Fix] stable_members fastcache: only tolerate truly-opaque members, f…
hughperkins May 18, 2026
55ecf95
[Fix] Metaclass-safe is_dataclass for walker over user objects
hughperkins May 18, 2026
6d9c307
[Style] pre-commit: import formatting
hughperkins May 18, 2026
49ffb3b
[Fix] Fastcache: skip opaque-typed members silently by default
hughperkins May 18, 2026
7757907
[Doc] Fastcache: opaque-member silencing is the default; clarify stab…
hughperkins May 18, 2026
fb38fec
Revert "[Doc] Fastcache: opaque-member silencing is the default; clar…
hughperkins May 18, 2026
7cabaa0
Revert "[Fix] Fastcache: skip opaque-typed members silently by default"
hughperkins May 18, 2026
b5b360a
[Fix] Fastcache: replace PARAM_INVALID / silent-skip with qualname fa…
hughperkins May 18, 2026
dce1305
[Refactor] Fastcache: two-level cache + pruning-driven narrow args walk
hughperkins May 18, 2026
984ac40
[Doc] Fastcache: pruning-driven semantics; stable_members is launch-p…
hughperkins May 18, 2026
12fb215
[Fix] Fastcache: full pruning coverage for data_oriented; remove qual…
hughperkins May 18, 2026
356394e
[Test] Pin pruning-driven fastcache behaviour for @qd.data_oriented args
hughperkins May 18, 2026
45129bc
[Doc] data_oriented(stable_members=...) docstring: correct the failur…
hughperkins May 18, 2026
1f25d9c
[Fix] record_after_call: propagate chain paths through Attribute args
hughperkins May 18, 2026
5fc9b4c
[Fix] Track @qd.func params in fn_param_names for chain-path seeding
hughperkins May 18, 2026
710ee47
[Fix] Fastcache: prune _predeclare_struct_ndarrays by flat-name on ca…
hughperkins May 18, 2026
090f1a8
[CI] Fix linters, pyright, MockContext test, deleted-comment, line-wrap
hughperkins May 18, 2026
be4b030
[Refactor] Move fold_*_into_pruning from Kernel to Pruning
hughperkins May 19, 2026
75c08f6
[Style] Reflow 3 docstring paragraphs to 120c (Check line wrapping)
hughperkins May 19, 2026
29dd841
[Style] Reflow 3 more comment/docstring lines to 120c
hughperkins May 19, 2026
4bd2d10
[Style] Reflow 3 more comment lines to 120c
hughperkins May 19, 2026
aef1a26
[Style] Manually reflow underwrapped prose to 120c
hughperkins May 19, 2026
197d150
[Style] Reflow more underwrapped prose to 120c (round 2)
hughperkins May 19, 2026
173b051
Merge remote-tracking branch 'origin/hp/data-oriented-ndarray-fix' in…
hughperkins May 19, 2026
a47a5ab
[Doc] fastcache.md: restore prose phrasing in unsupported-type + arg-…
hughperkins May 19, 2026
5debfe4
[Doc] fastcache.md: drop redundant 'every child is subject to pruning…
hughperkins May 19, 2026
4e714c7
[Doc] fastcache.md: revert @qd.data_oriented child-rule bullets to or…
hughperkins May 19, 2026
39602c6
[Doc] fastcache.md: tighten recognised-but-unsupported sentence
hughperkins May 19, 2026
bd37c94
[Doc] fastcache.md: restore nested-dataclass + qd.field bullets in da…
hughperkins May 19, 2026
59ce5ff
[Style] args_hasher: restore original 'field offset' comments on Scal…
hughperkins May 19, 2026
a63b834
[Docs] src_hasher: remove pre-refactor background paragraph from modu…
hughperkins May 19, 2026
f6c68d8
[Docs] src_hasher: correct safety-implication paragraph
hughperkins May 19, 2026
ae36b11
[Fix] Per-instance ndarray-path cache for @qd.data_oriented args
hughperkins May 19, 2026
c61d32c
[Test] Strengthen polymorphism + add cache-hit predeclare ndarray test
hughperkins May 19, 2026
706f9b5
[Test] Add bug reproducer: needs_grad not folded into fastcache args_…
hughperkins May 19, 2026
4398af7
[Fix] Fold needs_grad into fastcache narrow args_hash for ndarray leaves
hughperkins May 19, 2026
8a7ead4
[Lint] Reorder imports in needs_grad reproducer test
hughperkins May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/source/user_guide/compound_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ state.step()

### Fastcache

`@qd.kernel(fastcache=True)` is supported on methods of `@qd.data_oriented` classes, but is disabled for fields; see [Advanced — compound-type cache keying](fastcache.md#compound-type-cache-keying) for more information.
`@qd.kernel(fastcache=True)` is supported on methods of `@qd.data_oriented` classes. Cache keying is *pruning-driven*: only the members the kernel actually reads contribute to the cache key. Opaque metadata members (e.g. UUIDs, Pydantic config objects, back-pointers to parent solvers) are skipped by the args-hasher's narrow walk as long as the kernel doesn't read them — they cannot affect compiled code and cannot cause spurious cache misses. See [Pruning-driven argument hashing](fastcache.md#pruning-driven-argument-hashing) for the full keying rules.

If the kernel *does* read a member of an unrecognised type, fastcache is disabled for the call with a `[FASTCACHE][UNKNOWN_TYPE]` diagnostic — there is no qualname-fallback. `qd.field` / `MatrixField` members at a kernel-read path likewise disable fastcache (recognised-but-unsupported tensor-like types).

### Under the hood

Expand Down
39 changes: 35 additions & 4 deletions docs/source/user_guide/fastcache.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,16 @@ Fastcache supports the following parameter types:
| `torch.Tensor` | Yes | dtype, ndim |
| `numpy.ndarray` | Yes | dtype, ndim |
| `dataclasses.dataclass` | Yes | member types recursively; member values if annotated with `FIELD_METADATA_CACHE_VALUE` (see [Advanced — compound-type cache keying](#compound-type-cache-keying)) |
| `@qd.data_oriented` objects | Yes | member types recursively; primitive member types and values baked into kernel (see [Advanced — compound-type cache keying](#compound-type-cache-keying)) |
| `@qd.data_oriented` objects | Yes | member types recursively, narrowed by pruning (see [Pruning-driven argument hashing](#pruning-driven-argument-hashing)); primitive member types and values baked into kernel (see [Advanced — compound-type cache keying](#compound-type-cache-keying)) |
| `qd.Template` primitives (int, float, bool) | Yes | type and value (baked into kernel) |
| Non-template primitives (int, float, bool) | Yes | type only |
| `enum.Enum` | Yes | name and value |
| `qd.field` / `ScalarField` / `MatrixField` | **No** | — |
| `qd.field` / `ScalarField` / `MatrixField` at a kernel-read path | **No** | — |
| Anything else at a kernel-read path | **No** | — |

If any parameter is of an unsupported type, fastcache is disabled for that call and the kernel falls back to normal compilation. For `qd.field` / `ScalarField` / `MatrixField` arriving through a `qd.Tensor`-annotated parameter, this is silent — no warning is emitted. For other unsupported types, a warning is logged at the `warn` level identifying the offending parameter.
If any kernel-used parameter is of an unsupported type, fastcache is disabled for that call and the kernel falls back to normal compilation. For `qd.field` / `ScalarField` / `MatrixField` arriving through a `qd.Tensor`-annotated parameter, this is silent — no warning is emitted. For other unsupported types, a warning is logged at the `warn` level identifying the offending parameter.

Kernel-unused members of any type — including unrecognised ones — do **not** disable fastcache. The pruning narrowing in the args hasher skips them entirely, so opaque metadata (UUIDs, Pydantic configs, parent back-pointers) attached to a `@qd.data_oriented` instance is harmless as long as the kernel doesn't read it.

### 3. Source code must be available

Expand All @@ -120,6 +123,34 @@ Each compiled artifact is stored under a key derived from all of the following:

When any of these change, the resulting key is different, so a new compilation occurs and a new entry is stored. Previous entries remain on disk — multiple cached versions coexist. You do not need to manually clear the cache when making code changes — the hash mismatch causes a transparent recompilation.

### Pruning-driven argument hashing

Fastcache uses a **two-level cache**:

- **L1** (source + config only): stores the set of *flat names* the kernel actually reads — e.g. `__qd_state__qd_x` for a kernel that reads `state.x`. This is the kernel's pruning info, computed at compile time by the AST builder.
- **L2** (L1 + narrow argument hash): stores the compiled artifact under a key that only hashes the arg paths in the L1 pruning set.

#### Two rules

The args hasher enforces two strict invariants:

1. **The cache key may only include contributions from kernel-pruned paths.** A path is "pruned" (in the pruning-info sense) if Quadrants's compiler recorded the kernel reading it. Pruning info covers:
- Dataclass-flattened param accesses (`__qd_some_dc__qd_field` Names produced by `FlattenAttributeNameTransformer`, marked via `build_Name`).
- Ndarray accesses on data_oriented / template args (`struct_ndarray_launch_info`, folded into the pruning set by `Kernel._fold_struct_nd_paths_into_pruning`).
- Any other attribute-chain access on a kernel arg (`self.dofs.x`, `cfg.n`, …) recorded by `ASTTransformer.build_Attribute`'s `_qd_arg_chain` tracking and folded by `Kernel._fold_kernel_arg_chain_paths_into_pruning`. This covers primitive members baked into the kernel, nested struct paths, and accesses through `@qd.func` callees (propagated by `Pruning.record_after_call`).

Paths *not* in the pruning set are skipped by the args hasher — they are guaranteed not to affect kernel codegen because the kernel cannot read them.

2. **Unrecognised types at kernel-read paths must not be silently dropped or hashed by type-name.** If pruning says the kernel reads a path and the value at that path is a type the args hasher doesn't explicitly handle (Pydantic models, UUIDs, third-party tensor wrappers, …), fastcache is disabled for the call with a one-shot `[FASTCACHE][UNKNOWN_TYPE]` warning identifying the offending type plus an `[INVALID_FUNC]` log line confirming the cache is off. Capturing type identity without type parameters (dtype/shape on a hypothetical tensor type) would silently mask a value-affecting change.

#### Practical implications

- **Kernel-unused members do not affect the cache key.** If a `@qd.data_oriented` container has an opaque metadata member (UUID, Pydantic config, parent-solver back-pointer), and the kernel never reads it, the member is *not* hashed. Changes to `self._uid` or `self.cfg` don't disturb the cache key of a kernel that reads `self.dofs_state.x`.
- **Kernel-unused members of unrecognised types are also fine.** Pruning narrowing skips them before the type-recognition check runs.
- **Kernel-read members of unrecognised types fail fastcache loudly.** Either add explicit handling in `quadrants/lang/_fast_caching/args_hasher.py::stringify_obj_type` (for new tensor-like types whose dtype/shape matter), or move the access out of the kernel-read path (for opaque metadata that shouldn't be there in the first place).

`qd.field` / `ScalarField` / `MatrixField` are *recognised-but-unsupported*: encountering one at a kernel-read path disables fastcache for the call (with a warn-level diagnostic).

## Advanced

### Diagnostics
Expand Down Expand Up @@ -147,7 +178,7 @@ On the first run you'll see `cache_stored=True` but `cache_loaded=False`. On the

The args hasher walks compound-type kernel parameters recursively. For each leaf member it decides what (if anything) contributes to the cache key. The headline rules:

**`@qd.data_oriented`:** the walker descends into `vars(obj)`. For each child:
**`@qd.data_oriented`:** the walker descends into `vars(obj)`, narrowed by pruning info. For each walked child:

- `qd.ndarray` member — `(dtype, ndim, layout)` is included in the cache key. Element values are not.
- Primitive (`int` / `float` / `bool` / `enum.Enum`) member — value is baked into the kernel (same semantics as a `qd.Template` primitive). Two instances of the same class with different primitive member values get different cache entries.
Expand Down
Loading
Loading