Skip to content

Commit af65096

Browse files
zzylolclaude
andcommitted
docs(planner): describe cost-free Stage 1 candidate generation
Update the developer docs and the `CostModel` trait doc: strategies take no cost model, sketch sizing is analytical, extension intents stay pass-through, and the cost model is consulted only at selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7b35498 commit af65096

7 files changed

Lines changed: 86 additions & 205 deletions

File tree

‎crates/asap-aware-mapping/src/cost_model.rs‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,14 @@ pub fn default_cse_shared_maintenance_cost(family: &FieldDataType) -> Cost {
362362
Cost(weight * UNIT)
363363
}
364364

365-
/// Ranks the candidate sketch algorithms for one [`AggIntent`], best choice
366-
/// first.
365+
/// Selection-time preferences and costs over the candidates Stage 1
366+
/// generates.
367367
///
368368
/// [`replacement::summary_candidates`] returns every algorithm that *can* answer an
369369
/// intent, in an arbitrary static preference order (issue #98's "one home"
370-
/// for the candidate set). A `CostModel` re-orders that list under real,
371-
/// deployment-specific cost knowledge this crate has no way to know about —
372-
/// `replacement::realizations_for_intent` constructs every candidate in the
373-
/// resulting order.
370+
/// for the candidate set), and candidate generation keeps that order. A
371+
/// `CostModel` re-orders the candidates when they are selected, under real,
372+
/// deployment-specific cost knowledge this crate has no way to know about.
374373
pub trait CostModel {
375374
/// Whether [`Self::candidate_cost`] prices a complete physical
376375
/// alternative, including its raw baseline, rather than a local

‎docs/develop_docs/asap-aware-mapping-architecture.md‎

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ A strategy should answer:
4242
4343
A cost model should answer:
4444

45-
> Given valid choices, which choices are preferable, and how should they be parameterized?
45+
> Given valid choices, which choices are preferable?
46+
47+
It is consulted only at selection time; sketch parameters come from the analytical estimators.
4648

4749
Do not put cost-based pruning into a `ReplacementStrategy`. A strategy must enumerate every valid alternative, even when the default cost model clearly prefers one. See [Rule 2](extend-asap-aware-mapping.md#rule-2-enumerate-do-not-rank).
4850

@@ -98,8 +100,6 @@ flowchart TB
98100
STRATEGY["ReplacementStrategy<br/>when a target matches, enumerate every legal replacement;<br/>implementations generate but do not choose"]:::generate
99101
CAND["ReplacementSubDAG candidates<br/>each contains a Subtree (summary or logical rewrite) or ExactComposition<br/>plus typed provenance and rationale;<br/>no alternative is removed solely on cost"]:::store
100102
TARGET -->|"try every registered strategy"| STRATEGY --> CAND
101-
CM(["CostModel<br/>orders candidates and supplies<br/>deployment-specific parameters"]):::choose
102-
CM -. "rank and parameterize; accuracy checks remain required" .-> STRATEGY
103103
end
104104
105105
subgraph SEARCHSPACE[3. Store the workload-wide search space]
@@ -108,7 +108,9 @@ flowchart TB
108108
end
109109
110110
subgraph RANKING[Optional ranked view]
111+
CM(["CostModel<br/>selection-time preferences and costs"]):::choose
111112
SORT["CandidateLogicalASAPDAGs::cost_sorted<br/>use the CostModel to order each candidate set<br/>and cost every candidate"]:::choose
113+
CM -.-> SORT
112114
RANKED["RankedTargetSubDAGCandidates<br/>the same candidates in preferred order,<br/>with costs aligned by index"]:::choose
113115
SPACE --> SORT -->|"reorder only; preserve every candidate"| RANKED
114116
end
@@ -193,9 +195,8 @@ capability and accuracy checks; supported algorithm applicability alone is not
193195
a result certificate.
194196

195197
The complete `replacements()` result is the candidate set produced by one
196-
strategy for one target. A strategy may order or parameterize candidates with
197-
help from a `CostModel`, but it must not remove a valid candidate because of
198-
cost.
198+
strategy for one target. Strategies take no `CostModel` and must not remove a
199+
valid candidate because of cost; ranking happens at selection time.
199200

200201
### 3.3 Current concrete strategies
201202

@@ -204,9 +205,9 @@ The default context-free registry contains five `ReplacementStrategy` implementa
204205
- `ASAPStrategies` matches supported aggregate and binary shapes. Its
205206
`replacements(target)` method constructs every legal post-ASAP summary sub-DAG,
206207
including applicable sketch, exact-accumulator, and pass-through
207-
realizations. Candidates are sized and ordered for the target's accuracy
208-
requirement; candidates without a sufficient guarantee are rejected before
209-
costing.
208+
realizations. Candidates are sized analytically for the target's accuracy
209+
requirement and listed in `summary_candidates` order; candidates without a
210+
sufficient guarantee are rejected before costing.
210211
- `SharedSubDAGStrategy` uses `consumer_count` to identify shared targets. It
211212
emits both build-once-and-share and recompute-independently rewrites when a
212213
target has multiple consumers.
@@ -215,8 +216,8 @@ The default context-free registry contains five `ReplacementStrategy` implementa
215216
- `ExactCompositionStrategy` preserves child-target references for compatible
216217
composition selection.
217218

218-
`default_strategies_with` uses `SemanticEquivalentRewriteStrategy` in its rewrite
219-
slot. The evidence-aware registry supplies the accuracy evidence provider to
219+
`default_strategies` uses `SemanticEquivalentRewriteStrategy` (via its
220+
`AvgToSumOverCountStrategy` alias) in its rewrite slot. The evidence-aware registry supplies the accuracy evidence provider to
220221
summary and Hydra construction. Search derives `RollupStrategy` after CSE from
221222
the actual sibling set. See the
222223
[registry definitions](../../crates/asap-aware-mapping/src/replacement.rs).
@@ -251,8 +252,9 @@ matter.
251252

252253
A single-target inspection caller may take the first
253254
candidate with `.into_iter().next()` and handle the empty case according to its
254-
execution policy. Constructing all candidates before taking the first costs
255-
more than constructing only the preferred candidate, but it keeps the strategy
255+
execution policy; the first candidate is in `summary_candidates` order, not cost
256+
order. Constructing all candidates before taking the first costs more than
257+
constructing only one, but it keeps the strategy
256258
contract consistent and preserves the full choice set for other callers.
257259

258260
`CandidateLogicalASAPDAGs::global_selection` optionally coordinates cross-target sharing and

‎docs/develop_docs/asap-aware-mapping-contracts.md‎

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ Search calls `propose`, so rejected candidates remain available for explanation.
147147

148148
> What are all semantically valid alternatives for this target?
149149
150-
`replacements` must be **exhaustive and not cost-filtered**. When its output has
151-
a preferred order, that ordering must come from the supplied `CostModel`; the
152-
strategy must still return every supported legal candidate. Required accuracy,
150+
`replacements` must be **exhaustive and not cost-filtered**. Strategies take no
151+
`CostModel`; output order carries no cost preference, and ranking happens at
152+
selection time. The strategy must return every supported legal candidate. Required accuracy,
153153
schema and capability checks can reject an otherwise applicable algorithm;
154154
exhaustiveness is not a promise of all theoretically possible plans.
155155

@@ -164,7 +164,9 @@ accumulator, or a pass-through that keeps the original operation instead of
164164
building a summary. `realizations_for_intent` enumerates these concrete
165165
realizations; `ASAPStrategies::replacements()` constructs each one as
166166
a `ReplacementSubDAG`. It returns all
167-
candidates in preferred order without selecting a winner. At workload scale,
167+
candidates in `summary_candidates` order, sized by the analytical estimators,
168+
without selecting a winner; `AggIntent::Extension` intents stay
169+
`Realization::PassThrough`. At workload scale,
168170
`search_workload`/`search_workload_with` preserve all supported legal alternatives
169171
across every `TargetSubDAG`. Optional planner APIs coordinate compatible semantic selections; physical
170172
commitment and placement remain downstream deployment decisions.
@@ -201,17 +203,14 @@ see [code architecture §3](asap-aware-mapping-architecture.md#3-how-the-current
201203

202204
### `CostModel`
203205

204-
`CostModel` covers every deployment-specific numeric or configuration decision—not only which candidate is cheapest. For example, sketch sizing trades memory and update cost for accuracy, so it belongs here too.
206+
`CostModel` covers deployment-specific preference and cost decisions. It is consulted only at selection time (`cost_sorted`, `global_selection` and their `_with_recurrence` variants), never during candidate generation: sketch parameters come from the analytical estimators (`accuracy::estimators::size_params`), and extension intents stay pass-through.
205207

206208
The crate cannot hardcode real deployment costs: `asap-aware-mapping` uses `asap-types` and pinned `asap_sketchlib` mapping
207209
bounds, but does not execute workloads or own deployment measurements. Most hooks therefore provide the crate's built-in static behavior as a default. Override only the decisions your deployment needs to change.
208210

209211
| Hook | Use it to | Default? |
210212
|---|---|---|
211213
| `rank_candidates` | Order valid sketch algorithms | No |
212-
| `size_params` | Convert an accuracy target into sketch parameters | Yes |
213-
| `realize_extension` | Map a custom intent to a realization | Yes |
214-
| `evaluation_extension` | Query a custom extension summary | Panics until paired with a custom realization |
215214
| `cse_recompute_cost` | Estimate independent recomputation | Yes |
216215
| `cse_shared_maintenance_cost` | Estimate shared maintenance | Yes |
217216
| `cse_share_decision` | Choose sharing or recomputation | Yes |
@@ -223,41 +222,6 @@ bounds, but does not execute workloads or own deployment measurements. Most hook
223222
fn rank_candidates(&self, intent: &AggIntent, candidates: &[SketchAlgorithm]) -> Vec<SketchAlgorithm>;
224223
```
225224

226-
- **`size_params`** — choose parameters, such as sketch capacity, for an already-selected `SketchAlgorithm` and accuracy target `(eps, delta)`, where `eps` is the tolerated error and `delta` is the tolerated probability of exceeding that error. It is separate from ranking so a deployment can customize sizing without changing algorithm preference. The trait provides a default implementation.
227-
228-
```rust
229-
fn size_params(&self, kind: SketchAlgorithm, intent: &AggIntent, eps: f64, delta: f64) -> SketchParams;
230-
```
231-
232-
- **`realize_extension`** — map a deployment-defined `AggIntent::Extension` to a post-ASAP `Realization`. The default is `Realization::PassThrough`.
233-
234-
Use `AggIntent::Extension { ext_kind, payload }` for intent shapes that only your deployment needs. Core treats both fields as opaque. For example, a deployment can tag an approximate-frequency intent with `ext_kind: "frequency"` and recognize it in `realize_extension`:
235-
236-
```rust
237-
fn realize_extension(&self, ext_kind: &str, _payload: &serde_json::Value) -> Realization {
238-
if ext_kind == "frequency" {
239-
Realization::Sketch(SketchKind::new(
240-
SketchAlgorithm::CountSketch,
241-
SketchParams::CountSketch { width: 1024, depth: 5 },
242-
))
243-
} else {
244-
Realization::PassThrough // fall back to the default for anything else
245-
}
246-
}
247-
```
248-
249-
Return `Realization::PassThrough` for unrecognized extension kinds. Do not panic.
250-
251-
```rust
252-
fn realize_extension(&self, ext_kind: &str, payload: &serde_json::Value) -> Realization;
253-
```
254-
255-
- **`evaluation_extension`** — define how queries read an extension summary that `realize_extension` mapped to a `Sketch`. The two hooks are a pair: realization defines what is maintained; evaluation defines how it is queried. Override both for the same `ext_kind`. The default evaluation panics to prevent a silent wrong answer.
256-
257-
```rust
258-
fn evaluation_extension(&self, ext_kind: &str, payload: &serde_json::Value, col: &ColumnRef) -> SketchStatistic;
259-
```
260-
261225
- **`cse_recompute_cost`** — estimate the one-time cost of recomputing a CSE candidate's sub-DAG independently at a single consumer. Default: `default_cse_recompute_cost`, a structural-size proxy.
262226

263227
```rust
@@ -297,7 +261,8 @@ A custom cost model does not necessarily need to override every hook. The curren
297261
`ReplacementStrategy` answers "what are the candidates for this one target?" `CandidateLogicalASAPDAGs` answers the same question for every target in a whole workload at once, without enumerating `2^N` fully-copied plans for `N` independently-choosable sites.
298262

299263
```rust
300-
// replacement.rs
264+
// replacement.rs (TargetSubDAGCandidates) and
265+
// plan_selection/candidate_selection.rs (RankedTargetSubDAGCandidates)
301266

302267
// One TargetSubDAGCandidates per distinct TargetSubDAG in the whole workload —
303268
// never a flat list of fully assembled plans.
@@ -345,7 +310,7 @@ to the selected algorithm and classifies the pair into its category. The public
345310
`.category()`, `.algorithm()`, and `.params()` accessors expose the committed
346311
values without permitting an invalid combination.
347312

348-
Where this matters in practice: `CostModel::rank_candidates`, `CostModel::size_params`, and `ASAPStrategies::replacements` operate at the **algorithm** level. `summary_candidates(intent)` returns a list of `SketchAlgorithm`s (`[Kll, DDSketch]` for a `Quantile` intent), never a bare `SketchKind` with nothing chosen underneath it. `SketchKind` appears after an algorithm has been selected and sized—on `Realization::Sketch(SketchKind)` and `FieldDataType::Sketch(SketchKind, GroupingStrategy)`.
313+
Where this matters in practice: `CostModel::rank_candidates` and `ASAPStrategies::replacements` operate at the **algorithm** level. `summary_candidates(intent)` returns a list of `SketchAlgorithm`s (`[Kll, DDSketch]` for a `Quantile` intent), never a bare `SketchKind` with nothing chosen underneath it. `SketchKind` appears after an algorithm has been selected and sized—on `Realization::Sketch(SketchKind)` and `FieldDataType::Sketch(SketchKind, GroupingStrategy)`.
349314

350315
`Sample`, `Wavelet`, and `StatModel` each use a flat `(Kind, Params)` pair. `Sketch` needs the additional algorithm level because multiple algorithms can serve the same purpose—for example, KLL and DDSketch both answer quantile queries.
351316

@@ -395,7 +360,7 @@ Each `ReplacementExplanation::reason` is copied verbatim from the matching candi
395360

396361
### Why there is no `ExplanationRule` trait
397362

398-
Explanations are derived from candidates already present in `CandidateLogicalASAPDAGs`. A new candidate kind therefore requires an `impl ReplacementStrategy` wired into `default_strategies`/`default_strategies_with`; a second explanation-specific trait would duplicate registration and could drift from the actual search space. Custom callers supply strategies through `explain_replacements_with`, using the same extension point exposed by `search_workload_with`.
363+
Explanations are derived from candidates already present in `CandidateLogicalASAPDAGs`. A new candidate kind therefore requires an `impl ReplacementStrategy` wired into `default_strategies`; a second explanation-specific trait would duplicate registration and could drift from the actual search space. Custom callers supply strategies through `explain_replacements_with`, using the same extension point exposed by `search_workload_with`.
399364

400365
### How it derives `location` text
401366

‎docs/develop_docs/end-to-end-accuracy-guarantees.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The main implementation locations are:
4343
| Guarantee and error vocabulary | `asap_types::ir::properties::guarantee` |
4444
| Accuracy model and built-in propagation | `asap_aware_mapping::accuracy` |
4545
| Candidate construction and legality filtering | `asap_aware_mapping::replacement` |
46-
| Parameter sizing hooks | `asap_aware_mapping::cost_model` |
46+
| Parameter sizing | `asap_aware_mapping::accuracy::estimators` |
4747
| Guarantee and rejection export | `asap_types::dag_export` and the `dag_export` devtool |
4848

4949
Read the sections below when changing one of those contracts.

0 commit comments

Comments
 (0)