22//
33// Lowers every query in every corpus we have (mirrors `variant_coverage`'s
44// corpus list exactly, so the two reports are directly comparable) with an
5- // *approximate* `AccuracyTarget`, runs `asap_logical_optimizer::explain_replacements`
6- // over each corpus as one workload, and reports the MVP demo's query-coverage
7- // metric: of the queries that lowered successfully, what fraction got
8- //
9- // - a `SketchApproximation` candidate (a genuine sketch alternative was
10- // found for at least one aggregate in the query — the KLL-vs-DDSketch
11- // kind of degree of freedom), and/or
12- // - a `CommonSubexpressionReuse` candidate (the query shares a sub-DAG,
13- // inside itself or with another query in the same corpus, that a
14- // build-once-and-share candidate was found for).
5+ // *approximate* `AccuracyTarget`, runs Stage 1's Pass 1
6+ // (`enumerate_local_logical_candidates`) over each query, and reports which
7+ // sketch alternatives Pass 1 offers for it, plus the fraction of lowered
8+ // queries with at least one.
159//
1610// `--epsilon <f64>` (default 0.01) sets the `AccuracyTarget` every query in
17- // every corpus lowers with. Without an approximate target,
18- // `ASAPStrategies` never has a genuine sketch alternative to
19- // report.
20- //
21- // This is a workload-level count (`explain_replacements` runs
22- // `search_workload` once per corpus, over every query in it together), not
23- // just a per-query re-run of the single-target path — so cross-query CSE
24- // reuse inside one corpus shows up here the same way it would in the
25- // dag-viewer's Union mode.
11+ // every corpus lowers with. Without an approximate target, Pass 1 offers no
12+ // sketch alternative.
2613
2714use asap_devtools:: lower_promql_with_data_ingestion_interval;
2815use asap_frontend_sql:: { lower_sql_dialect, SqlCatalog } ;
29- use asap_logical_optimizer:: { explain_replacements, ExplanationKind } ;
30- use asap_types:: ir:: schema:: { DataType , Field , Schema } ;
31- use asap_types:: ir:: OperatorNode ;
16+ use asap_logical_optimizer:: pass1:: logical_candidates:: enumerate_local_logical_candidates;
17+ use asap_logical_optimizer:: Realization ;
18+ use asap_types:: ir:: schema:: { DataType , Field , GroupingStrategy , Schema } ;
19+ use asap_types:: ir:: { OperatorNode , QueryRoot } ;
3220use asap_types:: types:: AccuracyTarget ;
3321use asap_types:: workload:: SqlDialect ;
34- use std:: collections:: BTreeSet ;
22+ use std:: collections:: { BTreeMap , BTreeSet } ;
3523use std:: rc:: Rc ;
3624
3725/// Line-based `#`/`--` comment stripping, then split on `;` — the shape every
@@ -123,9 +111,10 @@ struct CorpusCoverage {
123111 name : & ' static str ,
124112 lowered : usize ,
125113 failed : usize ,
126- sketch_covered : usize ,
127- cse_covered : usize ,
128- either_covered : usize ,
114+ /// Lowered queries Pass 1 rejected.
115+ rejected : usize ,
116+ /// Per lowered query Pass 1 accepted, the sketch alternatives it offers.
117+ sketches : Vec < ( String , BTreeSet < String > ) > ,
129118}
130119
131120fn pct ( n : usize , total : usize ) -> String {
@@ -136,86 +125,69 @@ fn pct(n: usize, total: usize) -> String {
136125 }
137126}
138127
139- /// `explain_replacements`' `location` is a comma-joined list of breadcrumbs
140- /// (`collect_locations` in `explanation.rs`), one per path from a workload
141- /// root to the target — e.g. `root "q3"` or `root "q3" > lhs`. A location
142- /// "covers" `label` (a bare `root "qN"` breadcrumb) if `label` is exactly one
143- /// of those comma-separated entries or the prefix of one that goes deeper —
144- /// i.e. the explanation's target is reachable from that query's root at all.
145- fn covers ( location : & str , label : & str ) -> bool {
146- location
147- . split ( ", " )
148- . any ( |loc| loc == label || loc. starts_with ( & format ! ( "{label} > " ) ) )
149- }
150-
151- fn root_label ( id : & str ) -> String {
152- format ! ( "root {id:?}" )
153- }
154-
155- /// Run `explain_replacements` over one corpus's already-lowered roots as one
156- /// workload, then attribute each finding back to the query root(s) it's
157- /// reachable from.
128+ /// Run Pass 1 over each of one corpus's already-lowered queries and collect
129+ /// the sketch alternatives it offers for any of the query's targets. A Hydra
130+ /// alternative is listed as `Hydra(<algorithm>)`.
158131fn analyze_corpus (
159132 name : & ' static str ,
160133 roots : Vec < ( String , Rc < OperatorNode > ) > ,
161134 failed : usize ,
162135) -> CorpusCoverage {
163136 let lowered = roots. len ( ) ;
164- let labels: Vec < String > = roots. iter ( ) . map ( |( id, _) | root_label ( id) ) . collect ( ) ;
165- let explanations = explain_replacements ( roots) ;
166-
167- let mut sketch_covered: BTreeSet < usize > = BTreeSet :: new ( ) ;
168- let mut cse_covered: BTreeSet < usize > = BTreeSet :: new ( ) ;
169- for explanation in & explanations {
170- for ( i, label) in labels. iter ( ) . enumerate ( ) {
171- if !covers ( & explanation. location , label) {
172- continue ;
173- }
174- match explanation. kind {
175- ExplanationKind :: SketchApproximation => {
176- sketch_covered. insert ( i) ;
177- }
178- ExplanationKind :: CommonSubexpressionReuse => {
179- cse_covered. insert ( i) ;
137+ let mut rejected = 0 ;
138+ let mut sketches = Vec :: new ( ) ;
139+ for ( id, root) in roots {
140+ let Ok ( inventory) = enumerate_local_logical_candidates (
141+ vec ! [ ( id. clone( ) , QueryRoot :: Operator ( root) ) ] ,
142+ & BTreeMap :: new ( ) ,
143+ ) else {
144+ rejected += 1 ;
145+ continue ;
146+ } ;
147+ let offered = inventory
148+ . targets
149+ . iter ( )
150+ . flat_map ( |target| target. alternatives . iter ( ) . zip ( & target. groupings ) )
151+ . filter_map ( |( alternative, grouping) | match alternative {
152+ Realization :: Sketch ( kind) if * grouping == GroupingStrategy :: default ( ) => {
153+ Some ( format ! ( "{:?}" , kind. algorithm( ) ) )
180154 }
181- // `#[non_exhaustive]`: a future kind just doesn't count
182- // toward either bucket here until this tool is taught about it.
183- _ => { }
184- }
185- }
155+ Realization :: Sketch ( kind) => Some ( format ! ( "Hydra({:?})" , kind . algorithm ( ) ) ) ,
156+ _ => None ,
157+ } )
158+ . collect ( ) ;
159+ sketches . push ( ( id , offered ) ) ;
186160 }
187- let either_covered = sketch_covered. union ( & cse_covered) . count ( ) ;
188-
189161 CorpusCoverage {
190162 name,
191163 lowered,
192164 failed,
193- sketch_covered : sketch_covered. len ( ) ,
194- cse_covered : cse_covered. len ( ) ,
195- either_covered,
165+ rejected,
166+ sketches,
196167 }
197168}
198169
170+ fn covered ( r : & CorpusCoverage ) -> usize {
171+ r. sketches . iter ( ) . filter ( |( _, s) | !s. is_empty ( ) ) . count ( )
172+ }
173+
199174fn report ( r : & CorpusCoverage ) {
200175 println ! ( "--- {} ---" , r. name) ;
201- println ! ( "lowered: {}, failed: {}" , r. lowered, r. failed) ;
202176 println ! (
203- "sketch-approximable: {}/{} ({})" ,
204- r. sketch_covered,
205- r. lowered,
206- pct( r. sketch_covered, r. lowered)
207- ) ;
208- println ! (
209- "CSE-shareable: {}/{} ({})" ,
210- r. cse_covered,
211- r. lowered,
212- pct( r. cse_covered, r. lowered)
177+ "lowered: {}, failed: {}, rejected by Pass 1: {}" ,
178+ r. lowered, r. failed, r. rejected
213179 ) ;
180+ for ( id, offered) in & r. sketches {
181+ if !offered. is_empty ( ) {
182+ let offered: Vec < _ > = offered. iter ( ) . map ( String :: as_str) . collect ( ) ;
183+ println ! ( " {id}: {}" , offered. join( ", " ) ) ;
184+ }
185+ }
214186 println ! (
215- "either (coverage): {}/{} ({})" ,
216- r . either_covered ,
187+ "sketch-approximable: {}/{} ({})" ,
188+ covered ( r ) ,
217189 r. lowered,
218- pct( r . either_covered , r. lowered)
190+ pct( covered ( r ) , r. lowered)
219191 ) ;
220192 println ! ( ) ;
221193}
@@ -332,22 +304,16 @@ async fn main() {
332304
333305 let total_lowered: usize = results. iter ( ) . map ( |r| r. lowered ) . sum ( ) ;
334306 let total_failed: usize = results. iter ( ) . map ( |r| r. failed ) . sum ( ) ;
335- let total_sketch: usize = results. iter ( ) . map ( |r| r. sketch_covered ) . sum ( ) ;
336- let total_cse: usize = results. iter ( ) . map ( |r| r. cse_covered ) . sum ( ) ;
337- let total_either: usize = results. iter ( ) . map ( |r| r. either_covered ) . sum ( ) ;
307+ let total_rejected: usize = results. iter ( ) . map ( |r| r. rejected ) . sum ( ) ;
308+ let total_sketch: usize = results. iter ( ) . map ( covered) . sum ( ) ;
338309
339310 println ! ( "=== global (epsilon = {epsilon}) ===" ) ;
340- println ! ( "total lowered: {total_lowered}, total failed: {total_failed}" ) ;
341311 println ! (
342- "sketch-approximable : {total_sketch}/{ total_lowered} ({})" ,
343- pct ( total_sketch , total_lowered )
312+ "total lowered : {total_lowered}, total failed: {total_failed}, \
313+ rejected by Pass 1: {total_rejected}"
344314 ) ;
345315 println ! (
346- "CSE-shareable: {total_cse}/{total_lowered} ({})" ,
347- pct( total_cse, total_lowered)
348- ) ;
349- println ! (
350- "either (coverage): {total_either}/{total_lowered} ({})" ,
351- pct( total_either, total_lowered)
316+ "sketch-approximable: {total_sketch}/{total_lowered} ({})" ,
317+ pct( total_sketch, total_lowered)
352318 ) ;
353319}
0 commit comments