From d10807cb59eb2ae1f61b277991eb968c192ef7e4 Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Mon, 5 Oct 2026 03:53:38 +0000 Subject: [PATCH 1/2] docs: describe the stage pipeline instead of the removed legacy search Library API, architecture overview, planner pipeline and the accuracy and cost developer docs now describe the #509 stage pipeline. Docs that only describe removed APIs (legacy search, CostModel, explanation, physical-plan cost adapter) are marked historical. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 +- docs/README.md | 5 +- docs/design_docs/architecture/README.md | 76 +-- .../architecture/asap-aware-mapping.md | 6 + .../architecture/asap-aware-plan-search.md | 8 +- .../evidence-dependent-candidates.md | 6 + .../architecture/input-output-workflow.md | 25 +- ...d_interface_with_pluggable_optimization.md | 8 +- docs/design_docs/concepts/accuracy-models.md | 13 +- docs/design_docs/concepts/planner-pipeline.md | 26 +- docs/design_docs/concepts/post-asap-ir.md | 7 +- .../decisions/concat-unique-keys.md | 3 +- docs/design_docs/decisions/cse-cost-model.md | 4 +- .../analytical-resource-cost.md | 4 +- .../ddsketch-quantile-ratios.md | 4 + .../end-to-end-accuracy-guarantees.md | 7 +- .../maintained-populations.md | 4 +- .../asap-aware-mapping/summary-properties.md | 2 + .../proposals/asapquery-rule-coverage.md | 4 + docs/develop_docs/README.md | 12 +- .../asap-aware-mapping-architecture.md | 326 ++-------- .../asap-aware-mapping-contracts.md | 5 + .../end-to-end-accuracy-guarantees.md | 74 ++- .../develop_docs/extend-asap-aware-mapping.md | 5 + docs/develop_docs/library-api.md | 573 ++++-------------- docs/develop_docs/local-logical-candidates.md | 5 +- .../metrics-observability-corpora.md | 33 +- docs/develop_docs/offline-sketch-evidence.md | 13 +- docs/develop_docs/physical-handoff-costs.md | 7 +- .../planner-vocabulary-migration.md | 4 + docs/develop_docs/replacement-explanations.md | 5 +- docs/develop_docs/storage-operation-costs.md | 9 +- .../target-candidate-api-migration.md | 3 + docs/user_guide_docs/run-a-query.md | 10 +- 34 files changed, 390 insertions(+), 910 deletions(-) diff --git a/README.md b/README.md index 3c21bd7d1..176645020 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # ASAPPlanner -ASAPPlanner turns SQL, PromQL, and MetricsQL query workloads into legal candidate plans that may use Approximate Streaming Analytics Primitives (ASAPs), such as sketches and exact summaries. It normalizes language-specific queries into a shared representation, then enumerates and ranks semantically equivalent alternatives. Downstream systems choose, deploy, and execute a physical plan. +ASAPPlanner turns SQL, PromQL, and MetricsQL query workloads into plans that may use Approximate Streaming Analytics Primitives (ASAPs), such as sketches and exact summaries. It normalizes language-specific queries into a shared representation, enumerates semantically equivalent alternatives, and selects the cheapest one that meets each query's accuracy target. Downstream systems deploy and execute the physical plan. ## Start here - New to the repository? Read the [planner pipeline](docs/design_docs/concepts/planner-pipeline.md), then the [glossary](docs/design_docs/concepts/glossary.md). - Want to run a query? Follow [Run and inspect a query](docs/user_guide_docs/run-a-query.md). - Embedding Planner? Use the [library API guide](docs/develop_docs/library-api.md). -- Extending Planner? Start with the [ASAP-aware mapping architecture](docs/develop_docs/asap-aware-mapping-architecture.md), then [extend ASAP-aware mapping](docs/develop_docs/extend-asap-aware-mapping.md). +- Extending Planner? Start with the [ASAP-aware mapping architecture](docs/develop_docs/asap-aware-mapping-architecture.md). - Evaluating a design? Browse the [design documentation](docs/design_docs/README.md), [developer documentation](docs/develop_docs/README.md), and [user guides](docs/user_guide_docs/README.md). The [documentation map](docs/README.md) gives each audience a complete reading path. diff --git a/docs/README.md b/docs/README.md index e01911037..e219175d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,13 +19,12 @@ Start with [ASAPPlanner input, output, and workflows](design_docs/architecture/i for the integration boundary, nested inputs, and choice of planning workflow. Use [Public library functions and examples](develop_docs/library-api.md) for -frontend lowering, workload search, ranking, selection and DAG assembly. +frontend lowering, planning a workload with the stage pipeline, and export. ## Extend the planner 1. Read the [ASAP-aware mapping architecture](develop_docs/asap-aware-mapping-architecture.md). -2. Consult [mapping contracts](develop_docs/asap-aware-mapping-contracts.md). -3. Follow [Extend ASAP-aware mapping](develop_docs/extend-asap-aware-mapping.md). +2. Read [local logical candidates](develop_docs/local-logical-candidates.md) for Stage 1. ## Understand a design diff --git a/docs/design_docs/architecture/README.md b/docs/design_docs/architecture/README.md index 7f60d0932..7d9412267 100644 --- a/docs/design_docs/architecture/README.md +++ b/docs/design_docs/architecture/README.md @@ -1,14 +1,15 @@ # ASAPPlanner design overview ASAPPlanner is a reusable planning library. It converts queries and workload -requirements into a deployment-independent space of logical Post-ASAP candidates. -It does not commit, deploy, or execute a physical plan; downstream systems such -as ASAPQuery-backend bind the candidates to physical alternatives, make the +requirements into a selected logical Post-ASAP plan, through the #509 stage +pipeline. It does not deploy or execute a physical plan; downstream systems +such as ASAPQuery-backend bind the plan to physical operators, make the deployment-level decision, and run the selected contract. -For the integration workflow, start with [ASAPPlanner input, output, and -workflows](input-output-workflow.md). It defines inputs, `CandidateLogicalASAPDAGs`, selection -and assembly workflows, and future replanning support. +For the integration workflow, start with the +[library API](../../develop_docs/library-api.md); +[ASAPPlanner input, output, and workflows](input-output-workflow.md) defines +the workload inputs. ## Planner component flow @@ -16,35 +17,27 @@ and assembly workflows, and future replanning support. flowchart TD W["PlanningWorkload: query demand + optional data facts"] F["Frontend dependencies: SQL catalog or PromQL time"] - E["Strategy, accuracy model, and applicable evidence"] - PRE["Frontend lowering → canonical Pre-ASAP OperatorNode roots"] - SEARCH["Whole-workload candidate search: sharing, legality, accuracy"] - SPACE["CandidateLogicalASAPDAGs: compact logical candidate DAG space"] - RANK["Optional cost_sorted: ranked inspection view"] - SELECT["Optional global_selection + assemble_selected_dag"] - DAG["Selected logical Post-ASAP DAG"] - BACKEND["Downstream: bind physical alternatives, decide deployment, compile and execute"] + M["PlanningModels: accuracy model, calibration, deployment capabilities"] + PRE["Frontend lowering → canonical Pre-ASAP roots"] + S1["Stage 1: Pass 1 alternatives per target; Pass 2 sharing variants"] + S2["Stage 2: physical candidates (materialization)"] + S3["Stage 3: accuracy and capability checks, pricing, selection"] + PLAN["Selected logical Post-ASAP plan + selection report"] + BACKEND["Downstream: bind physical operators, deploy, compile and execute"] W --> PRE F --> PRE - PRE --> SEARCH - E --> SEARCH - SEARCH --> SPACE - SPACE --> RANK --> BACKEND - SPACE --> SELECT --> DAG --> BACKEND + PRE --> S1 --> S2 --> S3 + M --> S3 + S3 --> PLAN --> BACKEND ``` -`CandidateLogicalASAPDAGs` is the output of logical candidate search. Each target's candidate set holds -alternatives and rejection reasons, but no materialization decision. -Choose between two branches: inspect candidates (optionally ranked), or select -and assemble logical DAGs. Stage 2 materialization (#509) will decide per -sub-DAG whether to materialize and whether at ingestion or query time; until -then every summary runs at query time. No branch by itself deploys or executes -a physical plan. -Known-invalid evidence rejects a logical candidate. Missing accuracy evidence -leaves a constructible candidate visible in `CandidateLogicalASAPDAGs` but uncertified; default -selection does not commit it without the required guarantee. Cost evidence can -rank eligible candidates, but it cannot establish a missing guarantee or turn -an unsupported physical alternative into a deployable plan. +Stage 1 lists alternatives without pricing them. Stage 3 rejects a candidate +whose summary estimate misses its query's accuracy target (or has no accuracy +model), that needs a capability the deployment lacks, or that exceeds its +memory budget, and selects the cheapest remaining one. Rejection reasons are +reported with the selection. Cost evidence can rank valid candidates, but it +cannot establish a missing guarantee or turn an unsupported physical +alternative into a deployable plan. ## Module map @@ -53,7 +46,7 @@ an unsupported physical alternative into a deployable plan. | Shared IR | `asap-types` | The unified operator IR (`ir`: one `OperatorNode` before and after ASAP optimization), schemas, workloads, guarantees, and exported plan data | | Front-end common | `frontend-common` | Name-based `UnresolvedOp` tree shared by the front ends, and `resolve_root` into the operator IR | | Query frontends | `frontend-sql`, `frontend-promql`, `frontend-metricsql` | Parse source languages and produce canonical Pre-ASAP queries | -| ASAP-aware mapping | `asap-logical-optimizer`, `asap-physical-optimizer`, `asap-plan-selection` | #509 Stages 1–3: candidate generation, CSE, legality and accuracy propagation; physical candidates; costing and selection | +| ASAP-aware mapping | `asap-logical-optimizer`, `asap-physical-optimizer`, `asap-plan-selection` | #509 Stages 1–3: logical alternatives and sharing; physical candidates; accuracy checks, costing and selection | | Planner facade | `asap-planner` | Lowering dispatch, the optimization pass (`OptimizationPass`, `StagePipeline`) and `optimize` | | Developer inspection | `devtools` | Expose planner DAGs, alternatives, decisions, and explanations for inspection | | End-to-end validation | `integration-tests` | Verify behavior across frontends, mapping, and output IR | @@ -66,27 +59,18 @@ requirements, the planning horizon, available materialized state, downstream capabilities, and complete cost evidence. Missing or stale evidence must remain explicit rather than being treated as zero. -The primary output is `CandidateLogicalASAPDAGs`; `cost_sorted` derives an optional ranked -view with index-aligned costs. Downstream may inspect compatible choices -across targets rather than assuming the first candidate is a feasible -physical workload plan. Candidates carry logical summary algorithms, -parameters, and guarantees, but no materialization decision. Rejection reasons -are retained in the candidate space. +The output is the selected plan: one logical Post-ASAP root per query +(`PlanOutput`), with the selection report — the priced candidates, the rejected +ones with their reasons, and whether the selection is guaranteed optimal. +`plan_stages` also returns Stage 1's alternatives for inspection. -ASAPQuery-backend and other downstream applications translate the candidates +ASAPQuery-backend and other downstream applications translate the selected plan into physical alternatives. They own concrete implementations, storage layout, placement, sharding, deployment-level cost and compatibility, final commitment, serving, and operational feedback. Their physical planning can reorder candidates because it has evidence that the reusable Planner does not, but it must not silently change Planner-owned semantics. -`candidate_selection::global_selection` optionally coordinates structural choices across -targets; `GlobalSelection::assemble_selected_dag` constructs a selected semantic DAG. -Those APIs do not establish physical feasibility or a -materialization decision. See the [library guide](../../develop_docs/library-api.md#optional-whole-plan-selection-and-dag-assembly) -for the distinction. Downstream may consume candidates directly and retains -responsibility for physical commitment. - ## Further reading - [Parsing and canonicalization](parse-and-canonicalize.md) diff --git a/docs/design_docs/architecture/asap-aware-mapping.md b/docs/design_docs/architecture/asap-aware-mapping.md index c04fa530a..82efe26ef 100644 --- a/docs/design_docs/architecture/asap-aware-mapping.md +++ b/docs/design_docs/architecture/asap-aware-mapping.md @@ -1,5 +1,11 @@ # ASAP-Aware Mapping +> **Historical:** this document describes the legacy replacement search +> (`ReplacementStrategy`, `CandidateLogicalASAPDAGs`, `global_selection`), which +> was removed (#630, #635). The #509 stage pipeline replaced it; see +> [planner layering](../proposals/planner-layering.md) and the +> [library API](../../develop_docs/library-api.md). + ## Overview ASAP-aware mapping decides **whether and how a query intent can be answered using summaries instead of scanning raw data**. diff --git a/docs/design_docs/architecture/asap-aware-plan-search.md b/docs/design_docs/architecture/asap-aware-plan-search.md index e612740b6..695ed821a 100644 --- a/docs/design_docs/architecture/asap-aware-plan-search.md +++ b/docs/design_docs/architecture/asap-aware-plan-search.md @@ -1,5 +1,11 @@ # Candidate Plan Search +> **Historical:** this document describes the legacy replacement search +> (`ReplacementStrategy`, `CandidateLogicalASAPDAGs`, `global_selection`), which +> was removed (#630, #635). The #509 stage pipeline replaced it; see +> [planner layering](../proposals/planner-layering.md) and the +> [library API](../../develop_docs/library-api.md). + ASAP-aware mapping should consider all alternatives holistically rather than optimize prematurely. Suppose a plan contains several independent-looking decision points: @@ -91,6 +97,6 @@ order. The [code architecture](../../develop_docs/asap-aware-mapping-architecture.md) describes current discovery and registry behavior; the -[library guide](../../develop_docs/library-api.md#optional-whole-plan-selection-and-dag-assembly) +[library guide](../../develop_docs/library-api.md) shows selection and its evidence boundaries. Broader optimization dimensions are tracked in the [proposal](../proposals/asap-aware-mapping/optimizations.md). diff --git a/docs/design_docs/architecture/evidence-dependent-candidates.md b/docs/design_docs/architecture/evidence-dependent-candidates.md index 39889ef46..2f252f595 100644 --- a/docs/design_docs/architecture/evidence-dependent-candidates.md +++ b/docs/design_docs/architecture/evidence-dependent-candidates.md @@ -1,5 +1,11 @@ # Evidence-dependent candidates +> **Historical:** this document describes the legacy replacement search +> (`ReplacementStrategy`, `CandidateLogicalASAPDAGs`, `global_selection`), which +> was removed (#630, #635). The #509 stage pipeline replaced it; see +> [planner layering](../proposals/planner-layering.md) and the +> [library API](../../develop_docs/library-api.md). + Audience: ASAPPlanner library integrators, especially ASAPQuery-backend. `CandidateLogicalASAPDAGs` is a space of constructible logical alternatives, not a list of diff --git a/docs/design_docs/architecture/input-output-workflow.md b/docs/design_docs/architecture/input-output-workflow.md index 8d1418251..c6e2f757e 100644 --- a/docs/design_docs/architecture/input-output-workflow.md +++ b/docs/design_docs/architecture/input-output-workflow.md @@ -2,6 +2,17 @@ ## Overview +> **Status:** the candidate-space output and the ranking and selection +> workflows this document describes (`CandidateLogicalASAPDAGs`, `cost_sorted`, +> `global_selection`, `CostModel`) were removed with the legacy search (#630, +> #635). ASAPPlanner now runs the #509 stage pipeline and returns the selected +> plan: `asap_planner::e2e_plan` returns a `PlanOutput`, and +> `asap_plan_selection::plan_stages` a `StagePipelineRun` with Stage 1's +> alternatives and the selection report. See the +> [library API](../../develop_docs/library-api.md) for the current workflow. +> The input sections below still describe the current `PlanningWorkload`; the +> output and workflow sections are kept as a historical record. + This document is for library integrators such as ASAPQuery-backend, not users submitting queries through a backend. @@ -17,7 +28,7 @@ Post-ASAP alternatives for the workload. | `PlanningWorkload.query_workload` | Query language and one-time/repeating query workloads | Yes | | `PlanningWorkload.data_workload` | Data arrival and optional evidence about ingestion, cardinality, and distribution | No implicit default. Set `None` when unavailable for non-PromQL workloads; PromQL requires `Some(DataWorkload)` with a nonzero ingestion interval. | | Frontend-specific dependencies (outside `PlanningWorkload`) | `SqlCatalog` for SQL; `now_ms` and, when needed, `HistogramCatalog` for PromQL | `SqlCatalog` is required for SQL lowering; `now_ms` is required for PromQL lowering | -| Planning models | Candidate cost/ranking and accuracy composition/checking | Used by the relevant APIs; built-in `DefaultCostModel` and `DefaultAccuracyModel` are available | +| Planning models | Accuracy checking, cost calibration and deployment capabilities (`PlanningModels`) | `PlanningModels::builtin()` uses `DefaultAccuracyModel`, an illustrative calibration and unrestricted capabilities | | External evidence and capabilities | Domain facts, measured costs, workload statistics, and runtime support | Supply when available and when the chosen optimization depends on them; absence is not proof | Frontend lowering and candidate search are stages within this workflow, not @@ -277,8 +288,8 @@ latter cannot be fabricated by one. | Input | Where it enters / default | Why it matters | |---|---|---| -| Accuracy model | Target-aware search takes an `AccuracyModel`; `DefaultAccuracyModel` is available. Default strategies also use it for candidate construction. | Composes candidate guarantees and checks them against requested accuracy. The model does not itself provide missing data-domain facts. | -| Cost model | Candidate strategies and `cost_sorted`/`global_selection` use a `CostModel`; `DefaultCostModel` is available. | Ranks or selects candidates. The built-in model is not a measured deployment cost for every physical implementation. | +| Accuracy model | `PlanningModels.accuracy`; `DefaultAccuracyModel` by default. Stage 3 checks each summary estimate with it. | Derives each estimate's guarantee and checks it against the requested accuracy. The model does not itself provide missing data-domain facts. | +| Cost calibration | `PlanningModels.calibration`; `Stage3Calibration::ILLUSTRATIVE` by default. | Stage 3 prices candidates analytically; the built-in calibration is not a measured deployment cost. | | Accuracy/domain evidence | `AccuracyEvidenceProvider`; default strategies use `NoAccuracyEvidence` when no provider is supplied. | Input ranges, nonempty populations, Top-K intervals, and similar facts can certify or rule out particular approximations. Missing facts remain unknown. | | Measured cost evidence | Supplied through a deployment-specific cost model or physical-evidence provider when cost-based physical comparison is needed. | CPU, memory, and I/O estimates must be comparable before claiming a summary beats raw recomputation. | | Runtime capabilities | Checked by deployment-specific providers. | Prevents choosing a maintenance/window operation the intended executor cannot implement. | @@ -297,7 +308,7 @@ the applicable strategy, accuracy target, and helper; missing evidence is not a blanket reason to discard unrelated candidates. For the direct DDSketch ratio above, search retains a candidate without a proven root guarantee when domain evidence is missing; automatic `global_selection` does not choose it. -See the [candidate-search reference](../../develop_docs/library-api.md#generate-and-rank-candidates) +See the [candidate-search reference](../../develop_docs/library-api.md) for this backend-selection path. --- @@ -414,16 +425,16 @@ the result for one query root. |:---:| | **Input:** [CandidateLogicalASAPDAGs](asap-aware-plan-search.md) + cost model | | ↓ | -| **Select:** [global_selection](../../develop_docs/library-api.md#what-does-global-selection-mean) chooses compatible alternatives | +| **Select:** [global_selection](../../develop_docs/library-api.md) chooses compatible alternatives | | ↓ | -| **Assemble:** [assemble_selected_dag(root)](../../develop_docs/library-api.md#api-definition-and-example) connects those choices for each query root | +| **Assemble:** [assemble_selected_dag(root)](../../develop_docs/library-api.md) connects those choices for each query root | | ↓ | | **Output:** one selected logical [Post-ASAP DAG](../concepts/post-asap-ir.md) per query root | Each output DAG specifies the chosen operators, parameters, and accuracy guarantees. Its root is an `Rc` (the same IR as the input, with some nodes now ASAP operators) and carries no execution timing yet; the -[API reference](../../develop_docs/library-api.md#api-definition-and-example) +[API reference](../../develop_docs/library-api.md) describes the function signatures and return handling. This path selects how to compute the query, not whether summary state is diff --git a/docs/design_docs/architecture/updated_interface_with_pluggable_optimization.md b/docs/design_docs/architecture/updated_interface_with_pluggable_optimization.md index c3daefe75..f3f456313 100644 --- a/docs/design_docs/architecture/updated_interface_with_pluggable_optimization.md +++ b/docs/design_docs/architecture/updated_interface_with_pluggable_optimization.md @@ -14,8 +14,8 @@ What that buys: * A new optimization algorithm can be freely implemented as a trait implementation, rather than a rule disguised to fit a two-phase pipeline it does not share. -Unchanged: `CandidateLogicalASAPDAGs`, `cost_sorted`, `global_selection`, and the interface -[input, output, and workflows](input-output-workflow.md) describes. +The legacy API (`CandidateLogicalASAPDAGs`, `cost_sorted`, `global_selection`) +was later removed (#630, #635); the "before" example below uses it. ```text PlanningWorkload ──lowering──▶ ParsedWorkload ──OptimizationPass──▶ PlanOutput @@ -123,8 +123,8 @@ and the target beneath it. When it fails, it builds every combination if there are at most 64, and otherwise flags `Selection::method` as not guaranteed optimal. -`ReplacementStrategy` remains a concept of the legacy candidate search, which -the default pass no longer uses. +`ReplacementStrategy` was a concept of the legacy candidate search, which has +been removed (#635). ### 3.2 Plugging in another pass diff --git a/docs/design_docs/concepts/accuracy-models.md b/docs/design_docs/concepts/accuracy-models.md index d2588edad..00fc1bb93 100644 --- a/docs/design_docs/concepts/accuracy-models.md +++ b/docs/design_docs/concepts/accuracy-models.md @@ -130,14 +130,11 @@ another implementation with the same algorithm name. In particular, a named empirical calibration is different from an arbitrary benchmark's maximum observed error; both its confidence and applicability must remain explicit. -The current interfaces still expose general parameter proposal through -`CostModel::size_params`. Default sizing is dispatched to the estimator modules through the existing -public candidate-construction entry point. Accuracy validation is independent of those proposals. The new -source-contract path centralizes HLL sizing and guarantee derivation in -Planner's accuracy module, overriding the generic proposal when the applicable -contract is supplied. It does not yet move every algorithm's sizing interface -out of CostModel. The design boundary is that parameter proposals never grant -accuracy authority to the cost model. +Stage 1 sizes every sketch through the estimator modules +(`asap_logical_optimizer::pass1::realization::default_size_params`); no cost +model proposes parameters. Accuracy validation (Stage 3's `AccuracyModel`) is +independent of sizing. The design boundary is that parameter proposals never +grant accuracy authority to the cost model. ## Composing guarantees through a DAG diff --git a/docs/design_docs/concepts/planner-pipeline.md b/docs/design_docs/concepts/planner-pipeline.md index c07d17775..d4062a3c0 100644 --- a/docs/design_docs/concepts/planner-pipeline.md +++ b/docs/design_docs/concepts/planner-pipeline.md @@ -1,26 +1,26 @@ # Planner pipeline -ASAPPlanner accepts a planning workload and produces `CandidateLogicalASAPDAGs`, a compact -representation of candidate Post-ASAP DAGs. Ranking and selection are operations -over that output, not mandatory stages of candidate search. +ASAPPlanner accepts a planning workload and produces the selected logical +Post-ASAP plan, through the #509 stage pipeline. SQL / PromQL / MetricsQL queries | parse and canonicalize v Pre-ASAP IR: exact, language-independent query intent - | enumerate legal summary-aware alternatives + | Stage 1: list summary-aware alternatives per target (Pass 1) + | and sharing variants across queries (Pass 2) v - CandidateLogicalASAPDAGs: candidate Post-ASAP DAGs - | - +--> inspect candidates, optionally using cost_sorted - +--> select and assemble logical DAGs + Stage 2: physical candidates (materialization) + v + Stage 3: accuracy and capability checks, pricing, selection + v + Selected Post-ASAP plan + selection report -Stage 2 materialization (#509) will decide per sub-DAG whether to materialize -and whether at ingestion or query time; until then every summary runs at query -time. All physical binding, deployment, and execution remain downstream +All physical binding, deployment, and execution remain downstream responsibilities. -The [input, output, and workflows](../architecture/input-output-workflow.md) -document defines the public boundary and helper call order. +The [library API](../../develop_docs/library-api.md) shows the calls, and +[input, output, and workflows](../architecture/input-output-workflow.md) +defines the workload inputs. The [Pre-ASAP IR](pre-asap-ir.md) captures what a query means without any summary implementation. The [Post-ASAP IR](post-asap-ir.md) represents the same intent using possible ASAP primitives. See the [architecture overview](../architecture/README.md) for the full component flow and boundary details. diff --git a/docs/design_docs/concepts/post-asap-ir.md b/docs/design_docs/concepts/post-asap-ir.md index 67a5ed406..9af67a1c9 100644 --- a/docs/design_docs/concepts/post-asap-ir.md +++ b/docs/design_docs/concepts/post-asap-ir.md @@ -67,11 +67,8 @@ execution implement the actual build and update operations. Not every summary fa Exact work is represented by the ordinary operators, unchanged: -- A sub-DAG the planner does not rewrite keeps its `NonASAPOp` nodes. Plan - assembly marks such a sub-DAG with an exact `ResultGuarantee` - (`asap_logical_optimizer::pass1::replacement::retain_exact`); a sub-DAG with no ASAP - operator and no guarantee is a logical rewrite candidate that has not been - assessed yet (`is_logical_rewrite`). +- A sub-DAG the planner does not rewrite keeps its `NonASAPOp` nodes (Stage + 1's `PassThrough` alternative). - `BinaryOp` combines independently planned operands. Summary planning may set its typed division guards (`checked_finite_division`, `checked_relative_division`); the operator's timing comes from the diff --git a/docs/design_docs/decisions/concat-unique-keys.md b/docs/design_docs/decisions/concat-unique-keys.md index 7d603e08f..21ac7764c 100644 --- a/docs/design_docs/decisions/concat-unique-keys.md +++ b/docs/design_docs/decisions/concat-unique-keys.md @@ -1,6 +1,7 @@ # `Concat` and `unique_keys`: the discriminator override (issue #228) -> Status: accepted decision for the implementation described here. +> Status: accepted decision for the implementation described here. File +> names are as of the decision; `dag_export.rs` has since been removed. > **Update**: the investigation below found no current call site paying for a > redundant `Dedup` that this override would remove, and the original version diff --git a/docs/design_docs/decisions/cse-cost-model.md b/docs/design_docs/decisions/cse-cost-model.md index e4f9423a1..089d59839 100644 --- a/docs/design_docs/decisions/cse-cost-model.md +++ b/docs/design_docs/decisions/cse-cost-model.md @@ -1,6 +1,8 @@ # Common sub-DAG sharing: rule-based vs. cost-based framework (issue #237) -> Status: accepted decision for the implementation described here. +> Status: accepted decision for the implementation described here. That +> implementation (`CostModel::cse_share_decision` in the legacy search) was +> removed (#635); kept as a decision record. ## Context diff --git a/docs/design_docs/proposals/asap-aware-mapping/analytical-resource-cost.md b/docs/design_docs/proposals/asap-aware-mapping/analytical-resource-cost.md index 1524bb811..85e8c1d6a 100644 --- a/docs/design_docs/proposals/asap-aware-mapping/analytical-resource-cost.md +++ b/docs/design_docs/proposals/asap-aware-mapping/analytical-resource-cost.md @@ -2,7 +2,9 @@ > Status: implemented model with explicit support limits. The > [analytical estimator](../../../../crates/plan-selection/src/cost/analytical_cost.rs) -> and physical-plan adapter implement supported evidenced comparisons. +> prices Stage 3's candidates. The physical-plan adapter +> (`PhysicalPlanCostModel`) described below was removed with the legacy cost +> model (#622). > Unsupported operators, arrival modes and missing evidence remain unavailable; > proposed extensions are not implied by the implemented formulas. diff --git a/docs/design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md b/docs/design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md index 8bb452783..4863e53cf 100644 --- a/docs/design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md +++ b/docs/design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md @@ -1,5 +1,9 @@ # DDSketch ratio certification (planner integration) +> **Historical:** ratio certification ran in the legacy search +> (`ASAPStrategies`, accuracy propagation), which was removed (#635). The stage +> pipeline does not certify quotient guarantees yet (#623). + Planner uses `asap_sketchlib` commit `da3635a80f8f854d47b772d49d5a9e5fb6927d8e` from [sketchlib PR #141](https://github.com/ProjectASAP/asap_sketchlib/pull/141) for mapping bounds and numerical integration tests. This pin does not update Collector or backend deployments. For valid relative-value operand bounds a and b, with b < 1 and a nonzero true denominator, the ratio bound is `(a+b)/(1-b)`. No independence assumption is needed. Sizing both DDSketch operands to `epsilon/(2+epsilon)` meets the ratio target, and compatible readouts can share one producer. diff --git a/docs/design_docs/proposals/asap-aware-mapping/end-to-end-accuracy-guarantees.md b/docs/design_docs/proposals/asap-aware-mapping/end-to-end-accuracy-guarantees.md index d705f6b7a..936ca8807 100644 --- a/docs/design_docs/proposals/asap-aware-mapping/end-to-end-accuracy-guarantees.md +++ b/docs/design_docs/proposals/asap-aware-mapping/end-to-end-accuracy-guarantees.md @@ -1,7 +1,10 @@ # Design: End-to-End Accuracy Guarantees -> Status: partially implemented design. Typed guarantees, supported composition -> rules and accuracy gating are implemented. Empirical-input and combined +> Status: partially implemented design. Typed guarantees and accuracy gating +> are implemented: Stage 3 checks each summary estimate's local guarantee. The +> composition rules and budget allocation were implemented by the legacy search +> and removed with it (#635); the stage pipeline does not compose guarantees +> yet (#623). Empirical-input and combined > parameter configuration remain extensions as described below. The > [implementation companion](../../../develop_docs/end-to-end-accuracy-guarantees.md) > describes current contracts and validation. diff --git a/docs/design_docs/proposals/asap-aware-mapping/maintained-populations.md b/docs/design_docs/proposals/asap-aware-mapping/maintained-populations.md index 91382160a..59b735af8 100644 --- a/docs/design_docs/proposals/asap-aware-mapping/maintained-populations.md +++ b/docs/design_docs/proposals/asap-aware-mapping/maintained-populations.md @@ -50,8 +50,8 @@ column and grouping determine whether consumers refer to the same population. ## Rule: share one population across compatible readouts -**Realization:** `MaintainedPopulationStrategy`, an opt-in `ReplacementStrategy` -in [maintained_population.rs](../../../../crates/logical-optimizer/src/pass1/maintained_population.rs). +**Realization:** `MaintainedPopulationStrategy::candidate`, an opt-in rule a +deployment calls (the stage pipeline does not offer it), in [maintained_population.rs](../../../../crates/logical-optimizer/src/pass1/maintained_population.rs). **Target sub-DAGs:** diff --git a/docs/design_docs/proposals/asap-aware-mapping/summary-properties.md b/docs/design_docs/proposals/asap-aware-mapping/summary-properties.md index da2ce90ff..c15171451 100644 --- a/docs/design_docs/proposals/asap-aware-mapping/summary-properties.md +++ b/docs/design_docs/proposals/asap-aware-mapping/summary-properties.md @@ -1,6 +1,8 @@ # Summary properties to model > Status: design exploration. Verify implementation support before relying on a property in planning. +> The guarantee propagation and `CostModel` steps named below belonged to the +> legacy search, which was removed (#635). To determine whether transformations are valid, ASAP-aware mapping needs a common description of summary capabilities. diff --git a/docs/design_docs/proposals/asapquery-rule-coverage.md b/docs/design_docs/proposals/asapquery-rule-coverage.md index 9ba999292..c1aa8c0d2 100644 --- a/docs/design_docs/proposals/asapquery-rule-coverage.md +++ b/docs/design_docs/proposals/asapquery-rule-coverage.md @@ -1,6 +1,10 @@ # ASAPQuery rule coverage > Status: proposal and coverage analysis. Check the implementation and tests before relying on it as a supported-behavior catalog. +> `ASAPStrategies` and `HydraGroupingStrategy` named below were removed with +> the legacy search (#635); Stage 1 (`pass1::realization`, +> `pass1::logical_candidates`) now enumerates realizations and Hydra +> alternatives. This document compares ASAPPlanner's rule system with the Rust planner in ASAPQuery at upstream commit `2586400b3b0436a5414c901ebce07065d20b5223`. diff --git a/docs/develop_docs/README.md b/docs/develop_docs/README.md index 817722adf..a4032b736 100644 --- a/docs/develop_docs/README.md +++ b/docs/develop_docs/README.md @@ -5,8 +5,7 @@ formats, evidence, and verification workflows. - [Public library functions and examples](library-api.md) - [ASAP-aware mapping architecture](asap-aware-mapping-architecture.md) -- [ASAP-aware mapping contracts](asap-aware-mapping-contracts.md) -- [Extend ASAP-aware mapping](extend-asap-aware-mapping.md) +- [Local logical candidates (Stage 1, Pass 1)](local-logical-candidates.md) - [Pre-ASAP IR reference](pre-asap-ir.md) - [Post-ASAP IR](../design_docs/concepts/post-asap-ir.md) - [Accuracy implementation companion](end-to-end-accuracy-guarantees.md) @@ -14,7 +13,12 @@ formats, evidence, and verification workflows. - [Offline sketch evidence](offline-sketch-evidence.md) - [Metrics-observability corpora](metrics-observability-corpora.md) - [Physical handoff cost references](physical-handoff-costs.md), [storage operations](storage-operation-costs.md) -- [Replacement explanations](replacement-explanations.md) - [Physical compile coverage for deployment computation](physical-compile-coverage.md) -- [Planner vocabulary migration (#427)](planner-vocabulary-migration.md) +Historical records of removed APIs: + +- [ASAP-aware mapping contracts](asap-aware-mapping-contracts.md) and + [extension guide](extend-asap-aware-mapping.md) for the legacy replacement search +- [Replacement explanations](replacement-explanations.md) +- [Planner vocabulary migration (#427)](planner-vocabulary-migration.md), + [target candidate API migration (#456)](target-candidate-api-migration.md) diff --git a/docs/develop_docs/asap-aware-mapping-architecture.md b/docs/develop_docs/asap-aware-mapping-architecture.md index 5e400f194..4cba2285d 100644 --- a/docs/develop_docs/asap-aware-mapping-architecture.md +++ b/docs/develop_docs/asap-aware-mapping-architecture.md @@ -1,268 +1,70 @@ # ASAP-Aware Mapping architecture -This document explains the architecture of ASAP-aware mapping: the planning -layer that turns logical query operations into alternative `Realization` -values built from ASAP primitives, such as exact summaries and approximate -sketches. Here, a **realization** is one candidate physical form of -one logical operation—not a selected workload plan or a deployed executable. -Read it to understand how strategies, realizations, and costing interact. - -For procedural work—adding a `ReplacementStrategy`, changing a `CostModel`, or -writing the expected tests—use [Extend ASAP-aware mapping](extend-asap-aware-mapping.md). - -For the higher-level motivation and replacement-plan-search design, see the -[ASAP-aware mapping overview](../design_docs/architecture/asap-aware-mapping.md). Current interfaces are -defined in [mapping contracts](asap-aware-mapping-contracts.md). - -Names such as `MyStrategy`, `MyCostModel`, and `PreferDDSketch` are -illustrative; they do not ship with this crate. Samples that use real public -types and functions follow the APIs exported by `asap-logical-optimizer` -(Stage 1 candidate search) and `asap-plan-selection` (cost models and selection). - -If you only need to find the right extension point, start with the [extension map](extend-asap-aware-mapping.md#7-current-extension-map). If you are implementing a strategy, read this mental model, the [mapping contracts](asap-aware-mapping-contracts.md), and the [extension guide](extend-asap-aware-mapping.md). - ---- - -## Code architecture - -## 1. Mental model - -ASAP-aware mapping has two different jobs that should remain separate: - -1. **Generate valid alternatives.** -2. **Rank alternatives and optionally coordinate compatible selections.** - -`ReplacementStrategy` is responsible for the first job. - -`CostModel` supplies preferences and cost evidence for the second; search and -selection APIs apply those decisions while preserving legality. - -A strategy should answer: - -> Does this transformation apply here, and if so, what are all semantically valid replacements? - -A cost model should answer: - -> Given valid choices, which choices are preferable? - -It is consulted only at selection time; sketch parameters come from the analytical estimators. - -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). - ---- - -## 2. Architecture overview - -The diagram below follows a workload of one or more query roots through target discovery, candidate generation, ranking, reporting, and downstream visualization. Section 3 focuses on the replacement-strategy path. - -Terminology used in the diagram: +This document explains how the planner turns logical query operations into +alternative realizations built from ASAP primitives, such as exact accumulators +and approximate sketches, and selects among them. A **realization** is one +candidate form of one logical operation, not a selected workload plan or a +deployed executable. + +The design is the #509 stage pipeline +([planner layering](../design_docs/proposals/planner-layering.md)); the +[library API](library-api.md) shows how to call it. The legacy replacement +search (`ReplacementStrategy`, `search_workload`, `CostModel`) that this +document used to describe was removed; the +[contracts](asap-aware-mapping-contracts.md) and +[extension guide](extend-asap-aware-mapping.md) are kept as a historical +record of it. + +## Terms - A **workload** is the set of named queries planned together. A **query root** - is the top-level `Rc` (the unified operator IR) for one of - those queries. **Pre-ASAP** means a DAG that contains only ordinary - `NonASAPOp` operators, before the planner realizes an operation with ASAP - primitives; **post-ASAP** means the same IR after some nodes became `ASAPOp` - summary operators. -- A **DAG** (directed acyclic graph) represents query operators whose sub-DAGs - may be shared. See [sub-DAG sharing and ASAP-aware CSE](../design_docs/proposals/planner-layering.md#pass-2-asap-aware-common-subexpression-elimination) - for the sharing rules. Rust's `Rc` (reference-counted pointer) records - shared node identity. -- A **target** is one replaceable site. A **candidate** is one valid alternative - for it. `Replacement::SubDAG` is a replacement sub-DAG: either a constructed - post-ASAP summary (it contains an `ASAPOp`, e.g. an exact accumulator or an - approximate sketch) or a logical rewrite with no ASAP operator - (`is_logical_rewrite` tells them apart). - `Replacement::ExactComposition` refers to a child target whose realization - must remain undecided until compatible selection. A **sketch** - is a compact data structure that trades exactness for bounded error. A - query's **accuracy target** states the allowed error and failure probability. - A candidate's **rationale** is its human-readable explanation. -- `CandidateLogicalASAPDAGs` is a compact candidate space with one - `TargetSubDAGCandidates` per target instead of one full plan per combination of choices. - A `node_hash` is a structural fingerprint used to narrow explanation lookup; - exact structural equality is still checked afterward. - -```mermaid -flowchart TB - classDef input fill:#e8f1ff,stroke:#4b78b8,color:#172b4d - classDef generate fill:#e7f7ef,stroke:#31835e,color:#173f2d - classDef store fill:#fff6dd,stroke:#b78922,color:#513d0c - classDef choose fill:#fcebdc,stroke:#c46a25,color:#572d0c - classDef report fill:#f2eafe,stroke:#7950b3,color:#34204f - - subgraph DISCOVERY[1. Discover every replaceable site] - WL["Input workload
one or more named pre-ASAP OperatorNode roots"]:::input - SEARCH["search_workload_with
run CSE once, then visit every node in every root DAG"]:::generate - TARGET["TargetSubDAG
one candidate site plus the number of workload locations
that reference the same Rc<OperatorNode>"]:::generate - WL -->|"roots"| SEARCH -->|"one target per distinct node"| TARGET - end - - subgraph GENERATION[2. Generate all legal alternatives at each site] - STRATEGY["ReplacementStrategy
when a target matches, enumerate every legal replacement;
implementations generate but do not choose"]:::generate - CAND["ReplacementSubDAG candidates
each contains a Subtree (summary or logical rewrite) or ExactComposition
plus typed provenance and rationale;
no alternative is removed solely on cost"]:::store - TARGET -->|"try every registered strategy"| STRATEGY --> CAND - end - - subgraph SEARCHSPACE[3. Store the workload-wide search space] - SPACE["CandidateLogicalASAPDAGs
one TargetSubDAGCandidates per target; each candidate set keeps
all candidates, including dependent compositions"]:::store - CAND -->|"deduplicate by target and candidate identity"| SPACE - end - - subgraph RANKING[Optional ranked view] - CM(["CostModel
selection-time preferences and costs"]):::choose - SORT["candidate_selection::cost_sorted
use the CostModel to order each candidate set
and cost every candidate"]:::choose - CM -.-> SORT - RANKED["RankedTargetSubDAGCandidates
the same candidates in preferred order,
with costs aligned by index"]:::choose - SPACE --> SORT -->|"reorder only; preserve every candidate"| RANKED - end - - subgraph REPORTING[Optional reporting view] - EXPLAIN["explain_replacements
select reportable candidates, copy their rationale,
and add kind, location, target, and node_hash"]:::report - EXPORT["dag_export
narrow by node_hash, then confirm structural equality"]:::report - VIEWER["dag-viewer
show a badge and explanation beside that node"]:::report - SPACE -->|"reporting view; no new planner decision"| EXPLAIN --> EXPORT --> VIEWER - end + is the top-level `QueryRoot` (an `Rc` operator DAG or a scalar + expression over them) of one query. **Pre-ASAP** means a DAG of ordinary + `NonASAPOp` operators; **post-ASAP** means the same IR after some nodes became + `ASAPOp` summary operators. +- A **target** is one single-measure aggregate a summary can realize. An + **alternative** is one `Realization` of it: `PassThrough` (exact execution of + the original sub-DAG), `ExactAggregate` (a mergeable exact accumulator) or + `Sketch` (an approximate summary sized to the query's accuracy target). +- An **accuracy target** states the allowed error and failure probability. + +## The stages + +```text +pre-ASAP roots + -> Stage 1, Pass 1: one LocalLogicalTarget per target aggregate, with its + alternatives (pass1::logical_candidates, pass1::realization) + -> Stage 1, Pass 2: sharing variants across queries — independent, + identical expressions merged, one summary sized for the strictest + consumer — plus tumbling-window forms for repeating queries (pass2) + -> Stage 2: physical candidates of each composed logical candidate, one per + materialization choice (asap-physical-optimizer) + -> Stage 3: reject candidates that miss an accuracy target, need a capability + the deployment lacks or exceed its memory budget; price the rest and + select the cheapest (asap-plan-selection) ``` -The generic `ReplacementStrategy` box is the extension point. The default -registry supplies summary realization, Hydra grouping, shared-sub-DAG, -average-rewrite and exact-composition strategies. Section 3.3 describes the -registries and the workload-derived roll-up rule. - ---- - -## 3. How the current pieces fit together - -The planner repeats one operation throughout the workload: find a target, ask -each registered strategy for every valid replacement, and store those -replacements as alternatives for that target. Ranking happens only after the -complete alternative set has been built. - -### 3.1 Discover targets across the workload - -Use `search_workload` or `search_workload_with` for normal planner search. The -search performs these steps: - -1. Run CSE once to merge structurally identical sub-DAGs that may legally be - shared. -2. Walk the complete DAG beneath every query root, including nodes below - unshared parents. -3. Construct one `TargetSubDAG` per distinct node, with the node's measured - `consumer_count`. -4. Run every registered `ReplacementStrategy` against each target to a - **fixpoint**: repeat until no new candidates are discovered, subject to - `MAX_SEARCH_ITERATIONS`. Search also prepares compatible compositions. - `search_workload_with_targets` applies explicit per-root accuracy targets - before ranking. - -The discovery and strategy-invocation path is: - -```mermaid -flowchart LR - classDef workload fill:#e7f7ef,stroke:#31835e,color:#173f2d - classDef common fill:#fff6dd,stroke:#b78922,color:#513d0c - - ROOTS["Input
one or more named OperatorNode roots"]:::workload - ROOTS --> CSE["Canonicalize sharing
merge structurally identical, legally shareable sub-DAGs"]:::workload - CSE --> WALK["Discover sites
walk the complete DAG, including nodes below unshared parents"]:::workload - WALK --> T["Build TargetSubDAG
retain the sub-DAG's Rc identity and measured consumer_count"]:::workload - T --> MATCH - MATCH["matches(target)
cheaply decide whether this strategy has alternatives"]:::common - MATCH -->|"true"| REPLACE["propose(target)
construct supported legal alternatives;
retain structured accuracy rejections"]:::common - MATCH -->|"false"| NONE["No candidates
continue with the next strategy"]:::common - REPLACE --> OUT["Candidate list for this strategy and target
each ReplacementSubDAG carries the replacement and rationale"]:::common -``` - -`consumer_count` is workload information, not an estimate of runtime -executions. It matters to strategies such as `SharedSubDAGStrategy`, which -only has a share-versus-recompute choice when a target has multiple consumers. - -### 3.2 Generate candidates through `ReplacementStrategy` - -For each target, the planner first calls `matches(target)`. A matching strategy -then supplies accepted candidates and structured rejections through -`propose(target)`. Its default wraps `replacements(target)`; strategies with -accuracy checks can override it. - -Each returned `ReplacementSubDAG` contains: - -- a `Replacement`: a constructed summary, logical rewrite or dependent exact - composition; -- the proposing strategy name and typed provenance; and -- the rationale for offering that replacement. - -The containing `TargetSubDAGCandidates` records the target. Legality includes required schema, -capability and accuracy checks; supported algorithm applicability alone is not -a result certificate. - -The complete `replacements()` result is the candidate set produced by one -strategy for one target. Strategies take no `CostModel` and must not remove a -valid candidate because of cost; ranking happens at selection time. - -### 3.3 Current concrete strategies - -The default context-free registry contains five `ReplacementStrategy` implementations: - -- `ASAPStrategies` matches supported aggregate and binary shapes. Its - `replacements(target)` method constructs every legal post-ASAP summary sub-DAG, - including applicable sketch, exact-accumulator, and pass-through - realizations. Candidates are sized analytically for the target's accuracy - requirement and listed in `summary_candidates` order; candidates without a - sufficient guarantee are rejected before costing. -- `SharedSubDAGStrategy` uses `consumer_count` to identify shared targets. It - emits both build-once-and-share and recompute-independently rewrites when a - target has multiple consumers. -- `HydraGroupingStrategy` proposes eligible shared multi-subpopulation layouts. -- `AvgToSumOverCountStrategy` proposes supported average rewrites. -- `ExactCompositionStrategy` preserves child-target references for compatible - composition selection. - -`default_strategies` uses `SemanticEquivalentRewriteStrategy` (via its -`AvgToSumOverCountStrategy` alias) in its rewrite slot. The evidence-aware registry supplies the accuracy evidence provider to -summary and Hydra construction. Search derives `RollupStrategy` after CSE from -the actual sibling set. See the -[registry definitions](../../crates/logical-optimizer/src/pass1/replacement.rs). - -The important rule is: - -> Strategies should reuse existing decision and implementation logic where possible instead of reimplementing it. - -These strategies expose their alternatives through the same -`ReplacementSubDAG` interface, so search and reporting do not need -strategy-specific discovery logic. - -### 3.4 Store and rank the complete search space - -Workload search deduplicates candidates into a `CandidateLogicalASAPDAGs`. Each distinct -target has one `TargetSubDAGCandidates` containing retained alternatives and -rejection reasons. This -compact representation preserves independent choices without enumerating a flat -list of `2^N` complete plans for `N` replaceable targets. - -`candidate_selection::cost_sorted` ranks each target's existing candidates with the -supplied `CostModel`. It returns the same candidates in preferred order, with -costs aligned by index; ranking does not select or remove a candidate. - -### 3.5 Single-target use and final selection - -`TargetSubDAG::new(&root)` creates a target for one isolated node and sets -`consumer_count` to `1`. It is useful for tests and focused tooling, but it does -not discover targets or provide workload-level sharing information. Use -`search_workload` or `search_workload_with` whenever accurate consumer counts -matter. - -A single-target inspection caller may take the first -candidate with `.into_iter().next()` and handle the empty case according to its -execution policy; the first candidate is in `summary_candidates` order, not cost -order. Constructing all candidates before taking the first costs more than -constructing only one, but it keeps the strategy -contract consistent and preserves the full choice set for other callers. - -`candidate_selection::global_selection` optionally coordinates cross-target sharing and -composition choices. `GlobalSelection::assemble_selected_dag` constructs the selected -semantic DAG. These APIs do not decide materialization or establish physical -deployment feasibility. Recurrence-aware variants require the corresponding -workload and evidence inputs; downstream owns physical commitment and execution. -See the [library workflow](library-api.md#optional-whole-plan-selection-and-dag-assembly). - ---- +| Concern | Location | +| --- | --- | +| Which realizations an intent has, sizing, summary input rules | `asap_logical_optimizer::pass1::realization` | +| Pass 1 inventory and composition of one choice per target | `asap_logical_optimizer::pass1::logical_candidates` | +| Pass 2 sharing rules | `asap_logical_optimizer::pass2` | +| Analytical error bounds of each summary family | `asap_logical_optimizer::accuracy` | +| Materialization | `asap_physical_optimizer` | +| Accuracy model, capabilities, pricing and selection | `asap_plan_selection` (`plan_stages`, `select_plan`) | +| Facade | `asap_planner::e2e_plan` | + +Stage 1 never prices: candidate generation is independent of cost (#572, +decision Q36(a)). It does not rank alternatives either; the catalog order of +`summary_candidates` has no preference meaning. Stage 3 is the only stage that +computes cost, and it checks accuracy per summary estimate with the +`AccuracyModel` in `PlanningModels`. + +## Adding a realization + +A new summary algorithm for an intent is added to `summary_candidates` and +sized in `accuracy::estimators::size_params`; its local guarantee goes in +`accuracy::estimators::local_guarantee`, and Stage 3 rejects it for an +accuracy-targeted query until one exists. How each input row updates the +summary is decided in `logical_candidates::summary_update`. The executor must +also be able to build and read it (`DeploymentCapabilities`). diff --git a/docs/develop_docs/asap-aware-mapping-contracts.md b/docs/develop_docs/asap-aware-mapping-contracts.md index f519d573e..033d9de53 100644 --- a/docs/develop_docs/asap-aware-mapping-contracts.md +++ b/docs/develop_docs/asap-aware-mapping-contracts.md @@ -1,5 +1,10 @@ # ASAP-Aware Mapping contracts +> **Historical:** this document describes the legacy replacement search +> (`ReplacementStrategy`, `search_workload`, `CostModel`, `explanation.rs`), +> which was removed (#635). The stage pipeline that replaced it is described in +> [ASAP-aware mapping architecture](asap-aware-mapping-architecture.md). + This reference defines the current public concepts and interface contracts used by ASAP-aware mapping. Read the [architecture](asap-aware-mapping-architecture.md) first; use the [extension guide](extend-asap-aware-mapping.md) when changing one. diff --git a/docs/develop_docs/end-to-end-accuracy-guarantees.md b/docs/develop_docs/end-to-end-accuracy-guarantees.md index 1baf0a60c..b36b362c3 100644 --- a/docs/develop_docs/end-to-end-accuracy-guarantees.md +++ b/docs/develop_docs/end-to-end-accuracy-guarantees.md @@ -10,60 +10,55 @@ workflow. ## How one candidate moves through the implementation -The implementation follows one candidate from a query requirement to either a -proven Post-ASAP node or a structured rejection. The direct DDSketch ratio -exception described below also permits an unproven candidate for backend inspection: +The stage pipeline checks accuracy per summary estimate, in Stage 3: ```text aggregate intent and AccuracyTarget - -> enumerate a summary implementation - -> choose and commit concrete summary parameters - -> derive the local guarantee from those committed parameters - -> realize the child and obtain its finalized guarantee - -> propagate guarantees through the registered composition rule - -> check the final guarantee against AccuracyTarget - -> keep legal candidate or record AccuracyError - -> rank retained candidates with CostModel (ranking does not prove accuracy) + -> Stage 1 lists summary implementations, with parameters sized from the + target (asap_logical_optimizer::pass1::realization::default_size_params) + -> Stage 2 builds the physical candidates + -> Stage 3 derives each estimate's local guarantee from the committed + parameters (AccuracyModel::local_guarantee) + -> checks it against the query's AccuracyTarget (AccuracyModel::satisfies) + -> rejects the candidate when there is no model or the guarantee misses + the target; Count-Min also needs non-negative update weights + -> prices the remaining candidates (pricing does not prove accuracy) ``` -For a single summary, the child is exact and propagation leaves the local -summary guarantee as the final guarantee. For nested summaries, child and local -guarantees are combined. `AccuracyTarget` is the requirement; `ResultGuarantee` -is the evidence produced for one concrete candidate. +The guarantee is the summary's local one: the stage pipeline does not yet +compose guarantees through nested approximate layers or exact operations over +them. The legacy replacement search did, with the composition rules below; it +was removed, and the coverage it carried is listed in #623. `AccuracyTarget` is +the requirement; `ResultGuarantee` is the evidence produced for one concrete +candidate. The design document's [end-to-end example](../design_docs/proposals/asap-aware-mapping/end-to-end-accuracy-guarantees.md#end-to-end-example) -is the canonical numeric walkthrough from allocation through legality -checking. +is the numeric walkthrough of the composition design. The main implementation locations are: | Concern | Location | | --- | --- | | Guarantee and error vocabulary | `asap_types::ir::properties::guarantee` | -| Accuracy model and built-in propagation | `asap_logical_optimizer::accuracy` | -| Candidate construction and legality filtering | `asap_logical_optimizer::pass1::replacement` | +| Local guarantees and target check | `asap_logical_optimizer::accuracy` (`local_guarantee`, `satisfies`) | +| Accuracy model Stage 3 calls | `asap_plan_selection::{AccuracyModel, DefaultAccuracyModel}` | | Parameter sizing | `asap_logical_optimizer::accuracy::estimators` | -| Guarantee and rejection export | `asap_types::dag_export` and the `dag_export` devtool | +| Rejection reasons | `asap_plan_selection::Selection::rejected` | Read the sections below when changing one of those contracts. ## Realization model -Accuracy reasoning and allocation are separate from cost modeling: +Accuracy reasoning is separate from cost modeling: ```rust trait AccuracyModel { - fn local_guarantee(/* family, query, parameters */) - -> Option; - - fn propagate( + fn local_guarantee( &self, - op: &CompositionOperator, - inputs: &[ResultGuarantee], - local: Option<&ResultGuarantee>, - stats: &PropagationStats, - ) -> Result; + family: &FieldDataType, + query: &SketchStatistic, + ) -> Option; fn satisfies( &self, @@ -73,17 +68,16 @@ trait AccuracyModel { } ``` -`AccuracyBudgetAllocator` proposes finite parameter allocations for nested -approximate layers. Every candidate must then be resized, propagated, and -checked before being treated as satisfying the target. This is distinct from -candidate visibility: direct DDSketch ratios lacking domain evidence remain in -`CandidateLogicalASAPDAGs` with `guarantee: None` and can appear in `cost_sorted`, but automatic -`global_selection` skips them. Presence and cost are not accuracy certification. -See the [workflow design](../design_docs/architecture/input-output-workflow.md#planning-evidence-inputs) +A family with no model (`local_guarantee` returns `None`) is rejected for an +accuracy-targeted query. See the +[workflow design](../design_docs/architecture/input-output-workflow.md#planning-evidence-inputs) for this boundary. ## Composition contracts +These rules were implemented by the removed legacy search +(`accuracy::composition`); the stage pipeline does not apply them yet (#623). + ### Exact values An exact value contributes zero error and zero failure probability: @@ -239,8 +233,8 @@ ASAPPlanner contains the guarantee algebra and parameter-derived contracts. It imports `asap_sketchlib` DDSketch mapping bounds for ratio certification; see [DDSketch ratio certification](../design_docs/proposals/asap-aware-mapping/ddsketch-quantile-ratios.md). This dependency does not make Planner a query executor. Data- or runtime-dependent evidence enters -through an `AccuracyEvidenceProvider`, is exposed to propagation as typed -`PropagationStats`, and is recorded in provenance. `NoAccuracyEvidence` is the +through an `AccuracyEvidenceProvider` as typed `PropagationStats` and is +recorded in provenance; the stage pipeline does not read it yet. `NoAccuracyEvidence` is the default: it supplies no missing facts. Guarantee derivation remains conservative; the direct DDSketch ratio exception above retains a candidate without claiming its accuracy is proven. @@ -319,7 +313,7 @@ Tests must cover: - accepted separated and rejected overlapping TopK intervals; - Hydra inner-plus-shared-grid composition; - implementation-qualified KLL/HLL/KMV/Theta confidence behavior; -- rejection before cost ranking and global selection; and +- rejection before Stage 3 prices a candidate; and - DAG export and frontend-to-post-ASAP integration. For a new target or summary, add a positive guarantee case and boundary cases for diff --git a/docs/develop_docs/extend-asap-aware-mapping.md b/docs/develop_docs/extend-asap-aware-mapping.md index 6432ad768..ba7f29a07 100644 --- a/docs/develop_docs/extend-asap-aware-mapping.md +++ b/docs/develop_docs/extend-asap-aware-mapping.md @@ -1,5 +1,10 @@ # Extend ASAP-aware mapping +> **Historical:** this document describes the legacy replacement search +> (`ReplacementStrategy`, `search_workload`, `CostModel`, `explanation.rs`), +> which was removed (#635). The stage pipeline that replaced it is described in +> [ASAP-aware mapping architecture](asap-aware-mapping-architecture.md). + Use this guide after reading the [mapping architecture](asap-aware-mapping-architecture.md) and consulting the [mapping contracts](asap-aware-mapping-contracts.md). diff --git a/docs/develop_docs/library-api.md b/docs/develop_docs/library-api.md index 2c0b7ab0f..f5c04149f 100644 --- a/docs/develop_docs/library-api.md +++ b/docs/develop_docs/library-api.md @@ -1,40 +1,35 @@ # Public library functions -Audience: developers embedding ASAPPlanner or adding strategies/models. This is -a compact reference for the public workflow APIs, not an -exhaustive symbol reference. The [CLI guide](../user_guide_docs/run-a-query.md) covers command-line inspection; the [design overview](../design_docs/architecture/README.md) defines ownership. - -ASAPPlanner's primary output is `CandidateLogicalASAPDAGs`; ranking is a view over its candidates. -Downstream owns physical binding and commitment. Selection/DAG assembly helpers -do not deploy a plan, and a serializable DAG is not evidence of runtime readiness. +Audience: developers embedding ASAPPlanner or supplying deployment models. This +is a compact reference for the public workflow APIs, not an exhaustive symbol +reference. The [CLI guide](../user_guide_docs/run-a-query.md) covers +command-line inspection; the [design overview](../design_docs/architecture/README.md) +defines ownership. + +ASAPPlanner runs the #509 stage pipeline: Stage 1 lists the logical +alternatives of each query (Pass 1) and the sharing variants across queries +(Pass 2), Stage 2 builds the physical candidates (materialization), and Stage 3 +checks each candidate's accuracy and capabilities and selects the cheapest. +Downstream owns physical binding and commitment: a selected DAG is not evidence +of runtime readiness. ## Choose a library workflow | Desired result | Calls | Example | | --- | --- | --- | | Pre-ASAP IR | Frontend `lower_*` | [Lower a query](#lower-a-query-into-pre-asap-ir) | -| All ranked candidates | `search_workload_with_targets` -> `cost_sorted` | [Generate and rank](#generate-and-rank-candidates) | -| Custom optimization set | Construct `Vec>`, then search | [Strategies and models](#choose-strategies-and-models) | -| Selected semantic DAG / export | `global_selection` -> `assemble_selected_dag` -> export | [Selection example](#optional-whole-plan-selection-and-dag-assembly) | - -Each recipe ends at a different artifact. Use only the stages needed for that -artifact, while preserving the checks required by its intended consumer. +| Selected plan for a workload | `asap_planner::e2e_plan` | [Plan a workload](#plan-a-workload) | +| Selected plan from Pre-ASAP roots | `asap_plan_selection::plan_stages` | [Stage pipeline](#run-the-stage-pipeline-on-pre-asap-roots) | +| Stage 1 alternatives only | `stage1_logical_candidates` | [Stage 1 inventory](#inspect-stage-1-alternatives) | +| Exported DAG | `apply_materialization_timings` -> `compile_*_asap_dag` | [Export](#export) | ## Dependencies -Inside this workspace, depend on the frontend you need, -`asap-logical-optimizer` (Stage 1 candidate search), `asap-plan-selection` -(cost models and selection) and `asap-types`. External users can use Git +Inside this workspace, depend on the frontend you need, `asap-planner` (the +facade), or `asap-logical-optimizer` (Stage 1) and `asap-plan-selection` +(Stages 2 and 3 entry point), and `asap-types`. External users can use Git dependencies pinned to a compatible revision; use the same revision across -these crates. For the example below: - -```toml -[dependencies] -asap-frontend-promql = { git = "https://github.com/ProjectASAP/ASAPPlanner", rev = "e7fdb2492c42c9f5b34760706a5162aa586d3025" } -asap-plan-selection = { git = "https://github.com/ProjectASAP/ASAPPlanner", rev = "e7fdb2492c42c9f5b34760706a5162aa586d3025" } -asap-logical-optimizer = { git = "https://github.com/ProjectASAP/ASAPPlanner", rev = "e7fdb2492c42c9f5b34760706a5162aa586d3025" } -asap-types = { git = "https://github.com/ProjectASAP/ASAPPlanner", rev = "e7fdb2492c42c9f5b34760706a5162aa586d3025" } -``` +these crates. ## Lower a query into Pre-ASAP IR @@ -82,9 +77,9 @@ own accuracy requirement, with these explicit target choices: | `AccuracyTarget::Epsilon(e)` | An epsilon error requirement interpreted by the relevant accuracy rule | `Epsilon(0.01)` | | `AccuracyTarget::EpsilonDelta { epsilon, delta }` | Error requirement with a failure-probability bound | `{ epsilon: 0.01, delta: 0.05 }` | -Epsilon does not mean the same error quantity for every statistic. Inspect the -candidate's guarantee and its error metric; a target is a requirement, not proof -that a supported candidate exists. +Epsilon does not mean the same error quantity for every statistic: the +accuracy model checks it against each estimate's own error metric. A target is +a requirement, not proof that a supported candidate exists. ```rust use asap_frontend_promql::lower_promql_workload; @@ -143,91 +138,46 @@ async lower_sql_dialect(query: &str, catalog: &SqlCatalog, The catalog is required and describes your tables. For a complete schema-building example, see [the CLI frontend example](../../crates/devtools/src/bin/show_logical_dag.rs). -## Generate and rank candidates - -### Target sub-DAG candidates - -`TargetSubDAGCandidates` collects alternatives for one query subexpression -discovered by search. `CandidateLogicalASAPDAGs` contains these per-target candidate sets and -the workload's query roots. A root is a whole query; an inner expression can -also be a target. - -For example, a supported `quantile(0.99, latency)` subexpression may have multiple -summary alternatives. Those alternatives belong to the same candidate set -because they are choices for the same computation. Another subexpression has its -own candidate set. If two queries reference a shared subexpression, they can -share its selected computation. +## Plan a workload -`cost_sorted()` returns a `RankedTargetSubDAGCandidates` for each target: the subexpression, -its candidates in ranked order, and a cost entry aligned with each candidate. -It keeps the alternatives available; it does not select an entire workload plan. - -### API definition - -```text -search_workload_with_targets<'s, Id>( - roots: Vec<(Id, Rc, Option)>, - strategies: &[Box], - accuracy_model: &dyn AccuracyModel, -) -> CandidateLogicalASAPDAGs - -candidate_selection::cost_sorted<'a, Id>(space: &'a CandidateLogicalASAPDAGs, cost_model: &dyn CostModel) - -> Vec> -``` - -| Argument | Choices / meaning | Required? | -| --- | --- | --- | -| `roots` | One tuple per query: caller ID, canonical IR, and root target | Yes | -| Root target | `Some(AccuracyTarget::…)` applies an explicit end-to-end requirement; `None` adds no explicit root target | Tuple field required; value optional | -| `strategies` | Default factory output or an explicit strategy vector; see option tables below | Yes; even an empty vector does not disable automatic workload strategies | -| `accuracy_model` | `DefaultAccuracyModel` or a custom `AccuracyModel` implementation | Yes | -| Ranking `cost_model` | `DefaultCostModel` or an evidence-backed/custom `CostModel` | Yes | - -`search_workload_with_targets` normally rejects candidates without a guarantee -that satisfies the root target. One exception is a direct DDSketch quantile -ratio: without input-domain evidence, it remains in `CandidateLogicalASAPDAGs` with -`guarantee: None` so the downstream backend can decide whether to select it. -Its presence does **not** mean it satisfies the target. `cost_sorted` still -shows it, but `global_selection` skips it and DAG assembly uses the exact fallback -unless a certified alternative is available. A backend that wants the -uncertified candidate must explicitly inspect it and check its own domain -evidence and execution requirements before selecting or deploying it. - -### Example +`e2e_plan(UserInput) -> Result` lowers a +`PlanningWorkload` with the frontend its language names and runs the stage +pipeline. `UserInput::new(&workload, frontend_input, models)` takes: +| Argument | Choices | +| --- | --- | +| `FrontendInput` | `Promql { now_ms, histograms }`, `Sql { catalog }` or `Metricsql` | +| `PlanningModels` | `PlanningModels::builtin()`, refined with `with_accuracy`, `with_calibration` and `with_capabilities` ([Models](#models)) | -The following complete Rust example lowers one query, supplies an explicit root -accuracy target, and prints every ranked candidate instead of selecting a winner. -The default cost model is suitable for inspection, not deployment calibration. +`PlanOutput::plans` holds one `QueryPlan { entry_index, root }` per workload +entry, in `QueryWorkload::entries()` order; `PlanOutput::selection` reports +which candidate was selected, the priced and rejected ones, and whether the +selection is guaranteed optimal. A caller that already holds Pre-ASAP IR calls +`asap_planner::optimize` instead. ```rust -use asap_frontend_promql::lower_promql_workload; +use asap_planner::{e2e_plan, FrontendInput, PlanningModels, UserInput}; +use asap_types::types::AccuracyTarget; use asap_types::workload::{ - AccuracyRequirement, BatchEntry, DataWorkload, DurationMs, Evidence, Query, - PlanningWorkload, QueryLanguage, QueryRequirements, QueryWorkload, + AccuracyRequirement, BatchEntry, DataWorkload, DurationMs, Evidence, PlanningWorkload, + Query, QueryLanguage, QueryRequirements, QueryWorkload, }; -use asap_plan_selection::candidate_selection::cost_sorted; -use asap_plan_selection::DefaultCostModel; -use asap_logical_optimizer::{ - default_strategies, search_workload_with_targets, DefaultAccuracyModel, -}; -use asap_types::types::AccuracyTarget; -fn main() -> Result<(), Box> { - let accuracy = AccuracyTarget::Epsilon(0.01); +#[tokio::main] +async fn main() -> Result<(), Box> { let workload = PlanningWorkload { query_workload: QueryWorkload { language: QueryLanguage::PromQL, query_batch: Some(vec![BatchEntry { - query: Query("quantile(0.99, latency)".into()), - requirements: QueryRequirements { - accuracy: AccuracyRequirement::Explicit(accuracy.clone()), - ..Default::default() - }, - predictability: Default::default(), - invocations: 1, - execute_at: None, - time_selection: Default::default(), + query: Query("quantile(0.99, latency)".into()), + requirements: QueryRequirements { + accuracy: AccuracyRequirement::Explicit(AccuracyTarget::Epsilon(0.01)), + ..Default::default() + }, + predictability: Default::default(), + invocations: 1, + execute_at: None, + time_selection: Default::default(), }]), repeating_queries: None, }, @@ -239,263 +189,65 @@ fn main() -> Result<(), Box> { ..Default::default() }), }; - let root = lower_promql_workload(&workload, 0)?.remove(0); - let cost_model = DefaultCostModel; - let strategies = default_strategies(); - let space = search_workload_with_targets( - vec![("q1", root, Some(accuracy))], - &strategies, - &DefaultAccuracyModel, + let input = UserInput::new( + &workload, + FrontendInput::Promql { now_ms: 0, histograms: None }, + PlanningModels::builtin(), ); - for group in cost_sorted(&space, &cost_model) { - for (candidate, cost) in group.candidates.iter().zip(&group.costs) { - println!("candidate={candidate:?}, reported_cost={cost:?}"); - } + let output = e2e_plan(input).await?; + for plan in &output.plans { + println!("entry {}: {:#?}", plan.entry_index, plan.root); } Ok(()) } ``` -| API (`asap_logical_optimizer`; `candidate_selection` is `asap_plan_selection::candidate_selection`) | Inputs | Output and limits | -| --- | --- | --- | -| `search_workload` | `(query_id, Rc)` roots | `CandidateLogicalASAPDAGs` with built-in strategies/model; no explicit per-root target argument | -| `search_workload_with` | Roots, strategy slice | `CandidateLogicalASAPDAGs`; callers choose context-free replacement strategies | -| `search_workload_with_targets` | Roots with optional end-to-end targets, strategies, accuracy model | Candidate space with supplied root-target checks; `None` does not supply a root-level requirement; uncertified direct DDSketch ratios remain available for backend selection | -| `candidate_selection::cost_sorted` | Cost model | `Vec`; retains alternatives and pairs `candidates[i]` with `costs[i]` | -| `candidate_selection::cost_sorted_with_recurrence` | Cost model, recurrence profiles, optional horizon | Ranked per-target candidate sets or `RecurrenceError`; uses recurrence for applicable share/recompute comparisons | -| `ASAPStrategies::replacements` through `ReplacementStrategy` | One `TargetSubDAG` | Alternatives at that target; not whole-workload search | - -`cost_sorted` is a ranking view, not a request to discard all but the first -candidate. Display costs follow model hooks and may be unavailable/non-finite; -they are not necessarily a globally sortable physical-cost scalar. Unavailable -cost alternatives may remain for explanation. Inspect eligibility and evidence -before physical selection; do not treat their presence as deployment permission. - -### Enumerate candidate DAGs per root +## Run the stage pipeline on Pre-ASAP roots ```text -CandidateLogicalASAPDAGs::enumerate_candidate_dags_for_root(&self, id: &Id, expansion_limit: usize) - -> Result, RealizationError> -``` - -Returns every distinct finalized DAG for one root, unranked; other roots' -choices are not multiplied in. Exceeding `expansion_limit` is an error, never a -partial inventory. - -For PromQL roots that carry a target, `search_workload_with_targets` also asks -each strategy's `ReplacementStrategy::propose_for_root`. `ASAPStrategies` -answers an instant-vector TopK with current-series heap realizations over rows -carrying the complete series identity (`$promql_series_identity`). They are -finalized, deduplicated, and marked `ReplacementProvenance::RootPhysicalRealization`. -Callers do not apply `with_series_identity` themselves. Compile each with -`promql_rows::compile_current_series_evaluation`; other queries keep their previous -inventory. `global_selection` never commits these candidates; the backend -compiles and prices them. CandidateLogicalASAPDAGs lists no placement variants: node timing -comes from a `MaterializationAssignment` (all query time until Stage 2 -materialization, #509, decides otherwise). - -## Choose strategies and models - -### Strategy options - -The `strategies` argument takes Rust objects implementing `ReplacementStrategy`, -not string names or a closed enum. These built-in context-free choices can be -combined in one vector; each proposes candidates where its applicability checks -pass. An omitted strategy contributes no proposals of its own. - -| Value to put inside `Box::new(...)` | Meaning | In default factories? | -| --- | --- | --- | -| `ASAPStrategies::default()` | Enumerates supported exact/sketch implementations and parameter choices for aggregate targets | Yes | -| `HydraGroupingStrategy::default()` | Considers a shared multi-subpopulation structure for supported grouped sketch families, subject to accuracy evidence | Yes | -| `SharedSubDAGStrategy` | Proposes sharing versus independent recomputation at reused sub-DAGs | Yes | -| `SemanticEquivalentRewriteStrategy` | Proposes supported equivalent aggregate rewrites, including decomposing average into sum/count | Yes | -| `ExactCompositionStrategy` | Proposes exact operations around summary evaluations or in maintenance; not filtered by runtime support; `global_selection` commits one only with positive (`Some(true)`) cost-model support evidence | Yes | -| Your `ReplacementStrategy` implementation | Adds domain-specific legal replacement proposals | No | - -`AvgToSumOverCountStrategy` is an alias for `SemanticEquivalentRewriteStrategy` -at this revision; it is not a separate narrow rewrite to enable alongside it. - -The following are derived automatically from the workload by `search_workload*`: - -| Automatic behavior | Meaning | Can the strategy vector disable it? | -| --- | --- | --- | -| Canonical sharing/CSE | Interns structurally equal input subexpressions | No | -| `RollupStrategy` | Proposes compatible reuse across grouping granularities | No | -| `AccuracyReconciliationStrategy` | Proposes compatible sharing across different accuracy requirements | No | -| `TopKLimitReuseStrategy` | Proposes reuse among compatible top-k limits | No | - -The current API does not expose a universal enable/disable flag for every pass. -For inspecting only one strategy at one target, use -`ReplacementStrategy::replacements(&TargetSubDAG)`; this does not perform the -whole-workload search. Selecting a strategy does not force its candidate to win. - -### Factory choices - -```text -default_strategies() -> Vec> -replacement::default_strategies_with_evidence<'a>( - evidence: &'a dyn AccuracyEvidenceProvider, -) -> Vec> -``` - -| Factory | Use when | Models used | -| --- | --- | --- | -| `default_strategies()` | Exploring with built-in defaults | Built-in accuracy/allocation defaults; no extra evidence | -| `default_strategies_with_evidence(&evidence)` | Supplying planning-time accuracy evidence | Supplied evidence; default accuracy/allocation | -| Explicit vector | Controlling which context-free strategies are supplied | Models passed into each constructor | - -No factory takes a cost model: candidate generation is cost-model independent. -Pass the deployment cost model to `cost_sorted`/`global_selection`. - -### Example: supply two strategies and run search - -```rust -use asap_frontend_promql::lower_promql_workload; -use asap_types::workload::{ - AccuracyRequirement, BatchEntry, DataWorkload, DurationMs, Evidence, Query, - PlanningWorkload, QueryLanguage, QueryRequirements, QueryWorkload, -}; -use asap_plan_selection::candidate_selection::cost_sorted; -use asap_plan_selection::DefaultCostModel; -use asap_logical_optimizer::{ - search_workload_with_targets, DefaultAccuracyModel, ReplacementStrategy, - ASAPStrategies, SharedSubDAGStrategy, -}; -use asap_types::types::AccuracyTarget; - -fn main() -> Result<(), Box> { - let accuracy = AccuracyTarget::Epsilon(0.01); - let workload = PlanningWorkload { - query_workload: QueryWorkload { - language: QueryLanguage::PromQL, - query_batch: Some(vec![BatchEntry { - query: Query("quantile(0.99, latency)".into()), - requirements: QueryRequirements { - accuracy: AccuracyRequirement::Explicit(accuracy.clone()), - ..Default::default() - }, - predictability: Default::default(), - invocations: 1, - execute_at: None, - time_selection: Default::default(), - }]), - repeating_queries: None, - }, - data_workload: Some(DataWorkload { - data_ingestion_interval: Evidence { - value: Some(DurationMs(1_000)), - ..Default::default() - }, - ..Default::default() - }), - }; - let root = lower_promql_workload(&workload, 0)?.remove(0); - let model = DefaultCostModel; - let strategies: Vec> = vec![ - Box::new(ASAPStrategies::default()), - Box::new(SharedSubDAGStrategy), - ]; - let space = search_workload_with_targets( - vec![("q1", root, Some(accuracy))], &strategies, &DefaultAccuracyModel, - ); - println!("{:#?}", cost_sorted(&space, &model)); - Ok(()) -} +asap_plan_selection::plan_stages( + roots: Vec<(Id, QueryRoot)>, + demand: &[RootDemand], + data: &DataWorkload, + models: PlanningModels<'_>, + display: usize, +) -> Result, SelectionError> ``` -This omits Hydra and semantic/exact-composition strategies from the supplied -vector. Automatic workload strategies still run. Omitting an optimization does -not waive semantic or accuracy requirements. - -### Model and evidence options - -Traits permit custom implementations; the following are concrete built-in options. -Cost models are in `asap_plan_selection` (module-qualified paths below are -relative to `asap_plan_selection::cost`); accuracy models and evidence are in `asap_logical_optimizer`. - -| Parameter | Available value / constructor | Meaning | -| --- | --- | --- | -| `&dyn CostModel` | `DefaultCostModel` | Built-in ordering and structural estimates; no measured deployment guarantee | -| `&dyn CostModel` | `empirical_cost::EmpiricalCostModel::new(provider)` | Offline sketch-benchmark model: ranks algorithms using matching offline measurements | -| `&dyn CostModel` | `physical_plan_cost_model::PhysicalPlanCostModel::new(&provider, calibration)?` | Deployment-specific physical-plan model: compares complete physical alternatives using provider evidence and resource calibration; evidence may be offline or online | -| `&dyn AccuracyModel` | `DefaultAccuracyModel` | Built-in guarantee rules and satisfaction checks | -| `&dyn AccuracyBudgetAllocator` | `EqualSplitAllocator` | Built-in allocation of composition accuracy budgets | -| `&dyn AccuracyEvidenceProvider` | `NoAccuracyEvidence` | No extra planning-time statistics; evidence-dependent claims remain unavailable | -| `&dyn AccuracyEvidenceProvider` | `WorkloadAccuracyEvidence { data: &data, now_ms }` | Uses fresh data-workload evidence at the planning time | -| Any provider trait above | Your implementation | Supplies alternative models/evidence under the same contracts | - -### Offline measurements versus physical-plan costing - -These models differ in scope, not simply in whether they are offline or online. - -| Model | Evidence and comparison | Missing evidence / limits | -| --- | --- | --- | -| `EmpiricalCostModel` | Offline sketch benchmarks matched to exact parameters, distribution, environment and validity interval; current algorithm ranking uses measured update CPU nanoseconds | If the measurements required for ranking are incomplete, preserves the incoming algorithm order. `estimate_cost()` still uses `DefaultCostModel` structural scores | -| `PhysicalPlanCostModel` | A downstream provider supplies a consistent evidence snapshot and complete physical alternatives; calibration converts modeled resource quantities into comparable costs | A candidate with incomplete evidence is unavailable, without structural-cost fallback. Current candidate admission also requires it to cost less than the raw alternative | - -`PhysicalPlanCostModel` does not collect online telemetry itself. Its provider -may supply offline estimates/calibration or evidence derived from online -observations. Therefore, “offline sketch-benchmark model” and “physical-plan cost -model” describe their roles more accurately than “offline model” and “online model.” - -For example, a sketch with the lowest measured update cost can rank first under -`EmpiricalCostModel`, while its complete execution plan can still cost more than -another sketch or raw execution under `PhysicalPlanCostModel`. Offline error -measurements alone do not authorize smaller sketch parameters or replace formal -accuracy guarantees. +| Argument | Meaning | +| --- | --- | +| `roots` | One Pre-ASAP root per query, with a caller ID | +| `demand` | Per root: accuracy target, recurrence, predictability and latency bound | +| `data` | Data arrival and ingestion evidence Stage 2 and Stage 3 price with | +| `models` | [Models](#models) | +| `display` | Also build and price up to this many candidates for display (0: none) | -### Example: configure all sketch-strategy providers +`StagePipelineRun::stage1` is Stage 1's inventory, `plan` the selected +`SelectedPlan` (its `logical` roots, `physical` candidate and `selection` +report), and `enumeration` the displayed candidates. `select_plan` and +`select_exhaustive` run Stages 2 and 3 over an existing Stage 1 inventory. -```rust -use asap_logical_optimizer::{ - DefaultAccuracyModel, EqualSplitAllocator, - NoAccuracyEvidence, ReplacementStrategy, ASAPStrategies, -}; - -fn main() { - let accuracy = DefaultAccuracyModel; - let allocation = EqualSplitAllocator; - let evidence = NoAccuracyEvidence; - let strategies: Vec> = vec![Box::new( - ASAPStrategies::new_with_planning_inputs_and_evidence( - &accuracy, &allocation, &evidence, - ), - )]; - // Use &strategies and &accuracy in search_workload_with_targets. - println!("{} explicitly configured strategy", strategies.len()); -} -``` +## Inspect Stage 1 alternatives -Constructor definition: +`asap_logical_optimizer::pass2::identical_expressions::stage1_logical_candidates(roots, &metric_types, &demand)` +returns one `SharingVariant` per Pass 2 sharing form (independent, +identical expressions, summary capability), each with its Pass 1 +`LocalLogicalCandidates`: the alternatives (`Realization`) of every target +aggregate. `compose_logical_candidate(&inventory, &choice)` builds the +Pre-ASAP-plus-summary roots for one choice per target. See +[local logical candidates](local-logical-candidates.md). Alternatives are +unranked and carry no accuracy certificate; Stage 3 checks accuracy. -```text -ASAPStrategies::new_with_planning_inputs_and_evidence( - accuracy_model: &dyn AccuracyModel, - allocator: &dyn AccuracyBudgetAllocator, - evidence: &dyn AccuracyEvidenceProvider, -) -> ASAPStrategies -``` +## Models -All provider arguments are required for this constructor. They must outlive the -strategy vector. `ASAPStrategies::default()` uses default accuracy/allocation -and no extra evidence. +`PlanningModels` holds the planning logic a deployment can replace: -| Extension point | What it controls | What it cannot establish alone | +| Field / builder | Built-in value | Meaning | | --- | --- | --- | -| `ReplacementStrategy` | Proposed semantic alternatives | Permission to violate query semantics or downstream support | -| `CostModel` | Selection-time ranking, cost, support-evidence and recurrence cost hooks | Correctness, measured costs without evidence, or installed runtime support | -| `AccuracyModel` | Derivation, propagation and satisfaction of guarantees | A meaningful guarantee without its required assumptions/evidence | -| `AccuracyBudgetAllocator` | Local accuracy requirements proposed within composition | End-to-end correctness without subsequent validation | -| `AccuracyEvidenceProvider` | Planning-time statistics used by supported strategies | Authority to change query requirements | - -Accuracy models, allocators and evidence are consumed during generation; the cost -model is consumed only at selection (`cost_sorted`, `global_selection` and their -`_with_recurrence` variants). Sketch parameters come from the analytical -estimators, not the cost model. For evidence-aware defaults, use -`asap_logical_optimizer::pass1::replacement::default_strategies_with_evidence`. -For custom accuracy/allocation/evidence on sketches, -`ASAPStrategies::new_with_planning_inputs_and_evidence` exposes these providers. -Keep each provider's evidence scope and freshness valid for the query population. +| `accuracy` / `with_accuracy(&dyn AccuracyModel)` | `asap_plan_selection::DefaultAccuracyModel` | Each estimate's guarantee (`local_guarantee`) and whether it meets the query's target (`satisfies`); Stage 3 rejects an estimate whose family has no model | +| `calibration` / `with_calibration(Stage3Calibration)` | `Stage3Calibration::ILLUSTRATIVE` | Weights that turn modeled resources into cost; illustrative, not measured | +| `capabilities` / `with_capabilities(&DeploymentCapabilities)` | Unrestricted | What the deployment can build, read out and keep; candidates needing more are rejected | +| `evidence` / `with_evidence(&dyn AccuracyEvidenceProvider)` | `NoAccuracyEvidence` | Planning-time accuracy evidence; the stage pipeline does not read it yet | ## Workload inputs and defaults @@ -509,151 +261,36 @@ ingestion rate. `PlanningWorkload::validate()` shares these checks. | Type/input | Current behavior | Caller responsibility | | --- | --- | --- | -| `QueryRequirements::default()` | `ImplicitExact`, unspecified response latency | Pass approximation explicitly and thread per-root requirements into search | +| `QueryRequirements::default()` | `ImplicitExact`, unspecified response latency | Pass approximation explicitly; each root's requirement becomes its `RootDemand` | | `DataWorkload::default()` | Unknown arrival, unknown evidence | Supply facts needed for the requested comparisons | | `Evidence::default()` | No value, unknown source | Unknown/stale evidence is not zero; provide scoped valid observations | -| `DefaultCostModel` | Built-in ordering and structural cost hooks | Supply deployment evidence for calibrated comparisons | +| `Stage3Calibration::ILLUSTRATIVE` | Illustrative, uncalibrated cost weights | Supply a calibration measured for your deployment | `Default` is a Rust constructor contract, not a general serde omission rule. Several workload fields require explicit serialized values. A struct field being optional also does not guarantee every planning operation can succeed without it. -## Optional whole-plan selection and DAG assembly - -### What does global selection mean? - -`global_selection()` coordinates choices **across target sub-DAG candidate sets -in the workload**. Here, “global” describes that cross-target scope. It does not -mean a proven globally optimal solution over every possible physical plan, nor -selection across every machine in a deployment. - -Consider this conceptual dependency DAG: - -```text -Q1 --+ - +--> A --> B -Q2 --+ - -A's candidate set: alternatives for computing A -B's candidate set: alternatives for computing B -``` - -Both queries need A, and computing A needs B. Choosing to compute A once and -share it, versus recomputing it for each consumer, changes how many evaluations -of B are needed. That can change which choice for B is preferable. - -`cost_sorted()` ranks each target's alternatives using that target's recorded -consumer count. `global_selection()` accounts for ancestor sharing decisions -when deriving effective usage counts, and keeps coupled parent/child composition -choices consistent. The result records coordinated choices; `assemble_selected_dag()` then -constructs the selected semantic DAG while preserving shared nodes. - -| Operation | Question answered | Result | -| --- | --- | --- | -| `cost_sorted()` | How are the alternatives ranked for each subexpression? | Ranked alternatives per target | -| `global_selection()` | Which compatible choices should be used together, accounting for sharing and dependencies? | A coordinated selection across targets under the supplied model | - -Plain `global_selection()` does not decide materialization or establish -physical deployment feasibility. Stage 2 materialization (#509) will own -materialization; downstream still owns physical commitment. - -| Function or method | Behavior | -| --- | --- | -| `candidate_selection::global_selection(&space, &model)` | Compatible structural selection across targets; no recurrence or materialization planning implied | -| `candidate_selection::global_selection_with_recurrence(...)` | Compatible selection using supplied recurrence profiles/horizon; no materialization commitments implied | -| `GlobalSelection::assemble_selected_dag(&target)` | `Result>, RealizationError>`; constructs untimed semantic IR, not stored summary data | - -Use a target associated with the searched space; DAG assembly can return `None` -when that target is absent. A downstream integration can use these convenience -APIs when its supplied model/evidence supports the intended comparison. Neither -plain structural selection nor taking each target's first candidate substitutes -for checking complete physical alternatives and deployment constraints. - -### API definition and example - -```text -candidate_selection::global_selection<'a, Id>(space: &'a CandidateLogicalASAPDAGs, cost_model: &dyn CostModel) - -> CostedGlobalSelection<'a> // derefs to GlobalSelection -GlobalSelection::assemble_selected_dag(&self, target: &Rc) - -> Result>, RealizationError> -``` - -For structural inspection only, this complete example selects a semantic root -and exports its inspection DAG. It performs no materialization or deployment -planning. - -```rust -use asap_frontend_promql::lower_promql_workload; -use asap_types::workload::{ - AccuracyRequirement, BatchEntry, DataWorkload, DurationMs, Evidence, Query, - PlanningWorkload, QueryLanguage, QueryRequirements, QueryWorkload, -}; -use asap_plan_selection::candidate_selection::global_selection; -use asap_plan_selection::DefaultCostModel; -use asap_logical_optimizer::search_workload; -use asap_types::types::AccuracyTarget; - -fn main() -> Result<(), Box> { - let workload = PlanningWorkload { - query_workload: QueryWorkload { - language: QueryLanguage::PromQL, - query_batch: Some(vec![BatchEntry { - query: Query("sum(latency)".into()), - requirements: QueryRequirements { - accuracy: AccuracyRequirement::Explicit(AccuracyTarget::Exact), - ..Default::default() - }, - predictability: Default::default(), - invocations: 1, - execute_at: None, - time_selection: Default::default(), - }]), - repeating_queries: None, - }, - data_workload: Some(DataWorkload { - data_ingestion_interval: Evidence { - value: Some(DurationMs(1_000)), - ..Default::default() - }, - ..Default::default() - }), - }; - let root = lower_promql_workload(&workload, 0)?.remove(0); - let space = search_workload(vec![("q1", root)]); - let selection = global_selection(&space, &DefaultCostModel); - // Search may canonicalize roots; use the root returned by CandidateLogicalASAPDAGs. - if let Some(summary) = selection.assemble_selected_dag(&space.roots[0].1)? { - let dag = asap_types::dag_export::export_summary(&summary); - println!("{dag:#?}"); - } - Ok(()) -} -``` - -## Export and explain +## Export | Function/type | Purpose | | --- | --- | -| `asap_types::dag_export::export(&query)` | Pre-ASAP inspection dag | -| `asap_types::dag_export::export_summary(&summary)` | Post-ASAP inspection dag | | `asap_types::ir::apply_materialization_timings(&root, &assignment, &mut TimingMemo::new())` | Write execution timing into every node from a `MaterializationAssignment` (default: all query time) and validate the data-state edges; `PlanOutput::execution_timed_dag()` applies the default to a planned workload | -| `asap_types::ir::export::compile_post_asap_dag(&timed_root)` | Export a timed DAG as a `PostAsapDAG` (wire version 7); rejects an untimed node; not a physical plan | -| `PostAsapDAGDocument::new(dag)` and `.validate()` | Versioned semantic envelope and explicit validation; constructing it alone does not validate | -| `explain_replacements` / `explain_replacements_with` | Findings from default/custom-strategy search; not a complete physical feasibility report | +| `asap_types::ir::export::compile_logical_asap_dag(&root)` | Export a DAG as a `LogicalASAPDAG` for inspection; not a physical plan | +| `LogicalASAPDAGDocument::new(dag)` and `.validate()` | Versioned envelope and explicit validation; constructing it alone does not validate | +| `asap_types::ir::export::compile_physical_asap_dag(&timed_root)` | Export a timed DAG as the `PhysicalASAPDAG` a deployment binds; rejects an untimed node | -Choose the export matching your intended handoff: an inspection DAG is not -interchangeable with a versioned execution contract. Preserve -cost/guarantee evidence needed downstream instead of exporting only a bare DAG. -For public symbol details, build local API documentation with: +Choose the export matching your intended handoff, and preserve the selection +report a downstream needs instead of exporting only a bare DAG. For public +symbol details, build local API documentation with: ```sh -cargo doc -p asap-logical-optimizer -p asap-plan-selection -p asap-types --no-deps +cargo doc -p asap-planner -p asap-logical-optimizer -p asap-plan-selection -p asap-types --no-deps ``` ## Source references - [Frontend PromQL](../../crates/frontend-promql/src/lib.rs), [SQL](../../crates/frontend-sql/src/lib.rs), [MetricsQL](../../crates/frontend-metricsql/src/lib.rs) -- [Search, ranking and selection](../../crates/logical-optimizer/src/pass1/replacement.rs) -- [Cost models](../../crates/plan-selection/src/cost/cost_model.rs) +- [Facade](../../crates/planner/src/lib.rs) +- [Stage 1](../../crates/logical-optimizer/src/lib.rs), [Stages 2 and 3 entry point](../../crates/plan-selection/src/lib.rs) - [Workload types](../../crates/types/src/workload/mod.rs) - [Planner-runtime contract](../design_docs/architecture/planner-runtime-contract.md) diff --git a/docs/develop_docs/local-logical-candidates.md b/docs/develop_docs/local-logical-candidates.md index 4673647bd..594fb031e 100644 --- a/docs/develop_docs/local-logical-candidates.md +++ b/docs/develop_docs/local-logical-candidates.md @@ -34,6 +34,5 @@ plans, and callers must not execute the first choice as a selection policy. Multi-measure aggregates remain intact in the roots until an explicit semantic split is supported. Opaque deployment extensions retain exact execution here; additional local alternatives require an explicit logical rule rather than a cost -model making a generation decision. The legacy ranked search remains available -for the existing pipeline until its later cutover; this module supplies the new -logical-only entry point without changing production selection prematurely. +model making a generation decision. This module is the stage pipeline's Stage 1 +entry point; the legacy ranked search it replaced was removed. diff --git a/docs/develop_docs/metrics-observability-corpora.md b/docs/develop_docs/metrics-observability-corpora.md index b5472ef89..a4902c4c2 100644 --- a/docs/develop_docs/metrics-observability-corpora.md +++ b/docs/develop_docs/metrics-observability-corpora.md @@ -42,7 +42,7 @@ o11y-bench, and awesome-prometheus-alerts. They are not duplicated here. formats and writes deterministic fixtures; it does not parse or judge PromQL. - `metrics_observability.rs` is the executable corpus test. It runs the - PromQL parser/lowerer and the sketch-only post-ASAP replacement pass, and + PromQL parser/lowerer and Stage 1 (Pass 1 and composition) per query, and prints the current measurements. - `summarize_misses.py` is an optional report formatter. It consumes the test's opt-in `POST_ASAP_MISS` lines and groups misses by coarse root shape. @@ -50,22 +50,16 @@ o11y-bench, and awesome-prometheus-alerts. They are not duplicated here. The test prints totals, parse errors, lowering errors, pre-ASAP successes, post-ASAP candidates, unchanged queries, and post-ASAP errors. `Pre-ASAP` means that parsing and lowering produced an `OperatorNode` DAG. `Post-ASAP candidate` -means the isolated `ASAPStrategies` produced a candidate that contains -an ASAP operator (`contains_asap()`). `Unchanged` is a successful pre-ASAP query -for which that strategy returned only the kept pre-ASAP sub-DAG (`retain_exact`). +means one of the query's first 64 Stage 1 choices composes to a candidate +that contains an ASAP operator (`contains_asap()`). `Unchanged` is a successful +pre-ASAP query for which only the pass-through choice composes to such a +candidate, and a post-ASAP error is a query Pass 1 rejects or whose +pass-through choice does not compose (`support::stage1_plan`). -## Strategies +## Scope -The corpus measurement deliberately uses only -`ASAPStrategies::default().replacements(...)` on each -query root. It does not measure workload-wide search or the other default -strategies. - -The default workload search currently registers `ASAPStrategies`, -`HydraGroupingStrategy`, `SharedSubDAGStrategy`, and -`AvgToSumOverCountStrategy`. Workload context can additionally contribute -`RollupStrategy` and `AccuracyReconciliationStrategy`. This baseline is -therefore a sketch-only comparison point. +The measurement runs Pass 1 on each query alone. It does not measure Pass 2's +sharing across queries, or Stage 2 and Stage 3. ## Alerts @@ -84,10 +78,9 @@ binary expressions, parenthesized/binary expressions, bare selectors, and root functions such as `sum`, `histogram_quantile`, `increase`, `absent`, `topk`, and `scalar`. -Many misses are expected because the isolated strategy only replaces a -bindable aggregate at the query root. A nested aggregate can be sketchable -even when the root expression is a selector, binary expression, or another -non-bindable function. +Many misses are expected because Pass 1 only realizes a single-measure +aggregate with a summary; a query without one (a bare selector, a per-series +function) keeps its exact plan. ## Reproducing and refreshing the baseline @@ -99,7 +92,7 @@ python3 tools/metrics_observability/extract_promql.py \ /metrics_observability \ crates/frontend-promql/tests/observability/data/metrics_observability -# Re-run totals, errors, pre-ASAP, and sketch-only post-ASAP candidates. +# Re-run totals, errors, pre-ASAP, and Stage 1 post-ASAP candidates. cargo test -p asap-frontend-promql --test metrics_observability -- --nocapture # Recreate the miss-pattern report. diff --git a/docs/develop_docs/offline-sketch-evidence.md b/docs/develop_docs/offline-sketch-evidence.md index fd0e5c416..d75cbd007 100644 --- a/docs/develop_docs/offline-sketch-evidence.md +++ b/docs/develop_docs/offline-sketch-evidence.md @@ -1,8 +1,13 @@ # Consuming offline sketch measurements -This document is for developers integrating sketch-bench with the planner. The -Rust schema is `asap_plan_selection::cost::empirical_cost::EvidenceArtifact`; its JSON -schema version is `1`. Required artifact-level `benchmark_version` and +This document is for developers integrating sketch-bench with the planner. + +> **Status:** the planner-side consumer (`asap_plan_selection::cost::empirical_cost`: +> `EvidenceArtifact`, `EmpiricalCostModel`) was removed with the legacy +> `CostModel`; Stage 3 does not read offline measurements yet. This document +> records the artifact format for producers and a future Stage 3 consumer. + +The artifact's JSON schema version is `1`. Required artifact-level `benchmark_version` and `model_version` identify the producer and cost interpretation independently of the serialization schema. Producers export offline benchmark measurements using this contract; benchmark tooling is delivered separately from the core provider. @@ -76,7 +81,7 @@ include generator configuration and seed, or trace checksum and sampling rules. Validity intervals are supplied by the producer or deployment policy; they are an explicit applicability assumption, not a measured property. -`EmpiricalCostModel` implements the planner's existing `CostModel` boundary. +The removed `EmpiricalCostModel` implemented the legacy `CostModel` boundary. It derives the planner's default parameter configurations for the requested accuracy, and orders algorithms by mean measured update CPU only when all candidates have applicable measurements. Otherwise it preserves discovery order. diff --git a/docs/develop_docs/physical-handoff-costs.md b/docs/develop_docs/physical-handoff-costs.md index e830b1d14..525fc6539 100644 --- a/docs/develop_docs/physical-handoff-costs.md +++ b/docs/develop_docs/physical-handoff-costs.md @@ -11,9 +11,10 @@ calibration, and ranking remain in the mapping crate. handoff traffic/write work is distinct from CPU work, scanned bytes, and stored byte occupancy; it is not collapsed into the generic CPU/byte resource container. -The physical-plan adapter accepts an optional `PhysicalHandoffProfile` in the -immutable `PhysicalEvidenceSnapshot`. With no profile, these -dimensions remain unestimated and the existing resource objective is preserved. +`asap_plan_selection::cost::physical_handoff_cost::estimate_physical_handoffs` prices a +deployment-supplied `PhysicalHandoffProfile`. Stage 3 does not read it: the +physical-plan adapter that did (`PhysicalPlanCostModel`) was removed with the +legacy cost model. With no profile, these dimensions remain unestimated. The profile's `plans` list binds handoffs to complete physical alternatives. Each plan supplies its `root` and a `nodes` map containing every physical node, diff --git a/docs/develop_docs/planner-vocabulary-migration.md b/docs/develop_docs/planner-vocabulary-migration.md index d4b29fecd..91ba4640c 100644 --- a/docs/develop_docs/planner-vocabulary-migration.md +++ b/docs/develop_docs/planner-vocabulary-migration.md @@ -1,5 +1,9 @@ # Planner vocabulary migration (#427) +> **Note:** the `ASAPStrategies` / `HydraGroupingStrategy` constructors and the +> `Realization`-based `CostModel` below were removed with the legacy search +> (#635); `Realization` now lives in `asap_logical_optimizer::pass1::realization`. + This is a Rust source API rename. Update imports and call sites using the table below. Planning, ranking, schema resolution, cost arithmetic, and window coverage rules are unchanged. Old Rust names are removed rather than retained as a second diff --git a/docs/develop_docs/replacement-explanations.md b/docs/develop_docs/replacement-explanations.md index caefd00f3..c28129287 100644 --- a/docs/develop_docs/replacement-explanations.md +++ b/docs/develop_docs/replacement-explanations.md @@ -24,4 +24,7 @@ Additional opportunities: - reuse finer-grained aggregation through roll-up ``` -Implemented as `asap-logical-optimizer`'s `pass1::explanation` module (`explain_replacements`/`explain_replacements_with`, issue #257) +The legacy replacement search implemented this (`pass1::explanation`, +`explain_replacements`, issue #257); it was removed with that search. Today the +`sketch_coverage` devtool lists, per query, the sketch alternatives Stage 1's +Pass 1 offers. diff --git a/docs/develop_docs/storage-operation-costs.md b/docs/develop_docs/storage-operation-costs.md index 5d3cc7150..fc586810b 100644 --- a/docs/develop_docs/storage-operation-costs.md +++ b/docs/develop_docs/storage-operation-costs.md @@ -1,9 +1,10 @@ # Storage operation estimates -The physical-plan ranking adapter accepts an optional `StorageIoProfile` in -its immutable `PhysicalEvidenceSnapshot`. -Omitting the profile preserves the existing CPU/memory/scan-byte objective; -operation counts are unestimated, not inferred to be zero. +`asap_plan_selection::cost::storage_io::estimate_storage_io` prices a +deployment-supplied `StorageIoProfile` for one physical DAG. Stage 3 does not +read it: the physical-plan ranking adapter that did (`PhysicalPlanCostModel`) +was removed with the legacy cost model. Without a profile, operation counts are +unestimated, not inferred to be zero. A supplied profile must cover every reachable physical node, with an explicit empty `accesses` list for nodes doing no storage I/O. Entries bind the complete diff --git a/docs/develop_docs/target-candidate-api-migration.md b/docs/develop_docs/target-candidate-api-migration.md index ffb542dff..f247fc520 100644 --- a/docs/develop_docs/target-candidate-api-migration.md +++ b/docs/develop_docs/target-candidate-api-migration.md @@ -1,5 +1,8 @@ # Target candidate and DAG assembly API migration (#456) +> **Historical:** the legacy search and selection APIs these renames concern +> were removed (#630, #635). Kept as a migration record. + This source-only rename follows the input/output/workflow design in #445. Candidate generation, ordering, accuracy checks, selection, and runtime behavior are unchanged. #453 separately defines the integration API surface. diff --git a/docs/user_guide_docs/run-a-query.md b/docs/user_guide_docs/run-a-query.md index 98ed0ae63..87abf1394 100644 --- a/docs/user_guide_docs/run-a-query.md +++ b/docs/user_guide_docs/run-a-query.md @@ -5,8 +5,8 @@ corpus coverage. These commands do not deploy or execute a physical plan. To develop an application using the Rust library, start with [Library API: definitions, options, and examples](../develop_docs/library-api.md). -That guide explains how to choose strategies and models, rank candidates, and -assemble selected DAGs. +That guide explains how to plan a workload with the stage pipeline, inspect +Stage 1's alternatives, and supply deployment models. ## Choose a command @@ -19,7 +19,7 @@ tool on first use. | `analyze_corpora --corpora --data-ingestion-interval-ms 1000 --out-dir ` | Repository PromQL corpora, output directory | Writes successful/error IR dumps and summary reports | | `analyze_corpora --sql-corpora --out-dir ` | Repository SQL corpora, output directory | Writes SQL corpus reports | | `variant_coverage --data-ingestion-interval-ms 1000` | Repository corpora | Reports Pre-ASAP IR variant coverage | -| `sketch_coverage --data-ingestion-interval-ms 1000 --epsilon 0.01` | Repository corpora; epsilon defaults to `0.01` | Reports sketch/reuse opportunities among successfully lowered queries | +| `sketch_coverage --data-ingestion-interval-ms 1000 --epsilon 0.01` | Repository corpora; epsilon defaults to `0.01` | Lists, per query, the sketch alternatives Stage 1 offers, and each corpus's coverage | ## Inspect a query from the command line @@ -95,13 +95,13 @@ cargo run -p asap-devtools --bin variant_coverage -- --data-ingestion-interval-m ### Check sketch-replacement coverage -Parse the same query corpora, lower them with an approximate `AccuracyTarget`, and report what fraction of each corpus's successfully-lowered queries got a genuine sketch alternative (`SketchApproximation`, e.g. KLL vs. DDSketch) and/or a cross-query common-subexpression-reuse candidate (`CommonSubexpressionReuse`): +Parse the same query corpora, lower them with an approximate `AccuracyTarget`, and list, per query, the sketch alternatives (e.g. KLL and DDSketch) Stage 1's Pass 1 offers, with the fraction of each corpus's successfully-lowered queries that got at least one: ```sh cargo run -p asap-devtools --bin sketch_coverage -- --data-ingestion-interval-ms 1000 --epsilon 0.01 ``` -`--epsilon` is optional (defaults to `0.01`). See the binary's own doc comment for exactly how "coverage" is defined and attributed back to each query. +`--epsilon` is optional (defaults to `0.01`). ## Additional examples From fc162ecf451e37f8716b4e1566dc23f4e32ee123 Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Thu, 8 Oct 2026 15:30:56 +0000 Subject: [PATCH 2/2] docs: describe the flat DAG and physical export instead of the removed logical export Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01W7qG9aFyPij5uWsyAJCxDW --- crates/types/src/lib.rs | 4 ++-- docs/develop_docs/library-api.md | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index ab72b083a..d4984f094 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -3,8 +3,8 @@ //! - [`ir`] — the unified operator IR (#511): one operator DAG before and //! after ASAP optimization ([`ir::OperatorNode`]), arranged by #511 section //! ([`ir::operator`], [`ir::scalar`], [`ir::schema`], [`ir::properties`]), -//! plus its passes (canonicalize, CSE, timing) and the wire export -//! ([`ir::export`]). No execution logic lives in this crate (issue #190). +//! plus its passes (canonicalize, CSE, timing), the flat DAG +//! ([`ir::flat`]) and the physical export ([`ir::physical_export`]). No execution logic lives in this crate (issue #190). //! - [`workload`] — planner inputs (#509): query and data workloads, the //! lowered [`workload::parsed_workload`], and [`workload::resources`]. //! - [`deployment`] — the deployment's capabilities, a planner input (#509) diff --git a/docs/develop_docs/library-api.md b/docs/develop_docs/library-api.md index f5c04149f..294a99238 100644 --- a/docs/develop_docs/library-api.md +++ b/docs/develop_docs/library-api.md @@ -275,9 +275,8 @@ optional also does not guarantee every planning operation can succeed without it | Function/type | Purpose | | --- | --- | | `asap_types::ir::apply_materialization_timings(&root, &assignment, &mut TimingMemo::new())` | Write execution timing into every node from a `MaterializationAssignment` (default: all query time) and validate the data-state edges; `PlanOutput::execution_timed_dag()` applies the default to a planned workload | -| `asap_types::ir::export::compile_logical_asap_dag(&root)` | Export a DAG as a `LogicalASAPDAG` for inspection; not a physical plan | -| `LogicalASAPDAGDocument::new(dag)` and `.validate()` | Versioned envelope and explicit validation; constructing it alone does not validate | -| `asap_types::ir::export::compile_physical_asap_dag(&timed_root)` | Export a timed DAG as the `PhysicalASAPDAG` a deployment binds; rejects an untimed node | +| `asap_types::ir::flat::flatten(&roots)` | A DAG as a flat, serializable node list (`FlatDag`, children as node ids) for inspection; not a physical plan | +| `asap_types::ir::physical_export::compile_physical_asap_dag(&timed_root)` | Export a timed DAG as the `PhysicalASAPDAG` a deployment binds; rejects an untimed node | Choose the export matching your intended handoff, and preserve the selection report a downstream needs instead of exporting only a bare DAG. For public