diff --git a/Cargo.lock b/Cargo.lock index 0ccb3d8ed7..2f2518bdc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,7 +453,6 @@ dependencies = [ "anyhow", "aprender-contracts", "aprender-contracts-macros", - "aprender-core", "aprender-cuda-edge", "aprender-gemm-codegen", "aprender-gpu", diff --git a/crates/aprender-compute/Cargo.toml b/crates/aprender-compute/Cargo.toml index 1aa9e9f4ab..ce1ee5b588 100644 --- a/crates/aprender-compute/Cargo.toml +++ b/crates/aprender-compute/Cargo.toml @@ -63,11 +63,9 @@ anyhow = "1.0.100" # Hardware capability detection (PMAT-447) chrono = "0.4" toml = "0.8" -# ML tuner integration (SHOWCASE-BRICK-001, Section 12) — aprender RandomForest is a -# DEV-dependency only: the SIMD foundation must not depend on the ML layer (layer -# inversion). The `ml-tuner` showcase lives in examples/ml_tuner_demo.rs + the tuner -# falsification tests, which consume aprender directly. Dev-dep path cycles are legal -# in cargo and dropped on publish, keeping the workspace a self-contained DAG (APR-MONO). +# ML tuner integration (SHOWCASE-BRICK-001, Section 12) — the SIMD foundation takes no +# dependency, not even a dev one, on the ML layer. The aprender RandomForest tests live in +# crates/aprender-core/tests/tuner_training_data_quality.rs (FLOW-010, #5803). # NOTE: trueno-graph (=aprender-graph) removed (APR-MONO self-containment): aprender-graph # depends on trueno, so compute→graph closed a cycle. The `execution-graph` feature's only # trueno-graph use was the thin `to_csr()`/`graph_to_csr()` CsrGraph exporters; CSR export @@ -106,9 +104,6 @@ trueno-cuda-edge = { workspace = true } # Sparse and solver crates for contract tests trueno-sparse = { workspace = true } trueno-solve = { workspace = true } -# ML-tuner showcase (SHOWCASE-BRICK-001): aprender RandomForest, DEV-only to avoid the -# compute→core layer inversion. Path dev-dep cycle is legal + dropped on publish. -aprender = { path = "../aprender-core", package = "aprender-core", default-features = false } ndarray = "0.17.2" faer = "0.24" # For GEMM benchmark comparison (fastest pure-Rust BLAS) matrixmultiply = "0.3" # Direct matrixmultiply crate benchmark (engine behind ndarray) @@ -139,9 +134,8 @@ hardware-detect = ["dep:dirs"] tui-monitor = ["presentar-core", "presentar-terminal", "crossterm", "trueno-gpu", "dep:tracing", "tracing-subscriber", "tracing-appender", "dep:dirs"] # CUDA support for TUI (real hardware monitoring) cuda = ["trueno-gpu/cuda"] -# ML-based kernel selection and throughput prediction (SHOWCASE-BRICK-001) -# Showcase: feed trueno tuner features into aprender RandomForest (example + tests only; -# aprender is a dev-dependency, so this feature activates no lib dependency). +# DEPRECATED no-op, kept so removing it is not a semver break for 0.71.x users (activates nothing; +# the aprender RandomForest tests moved to aprender-core, #5803). ml-tuner = [] # NOTE: `execution-graph` feature removed (APR-MONO self-containment) — its only purpose # was the trueno-graph CsrGraph exporters (see above). The exec-graph profiler itself diff --git a/crates/aprender-compute/README.md b/crates/aprender-compute/README.md index a462ad3d5e..50743d1f6e 100644 --- a/crates/aprender-compute/README.md +++ b/crates/aprender-compute/README.md @@ -222,7 +222,6 @@ GEMV, Quantization (Q4K/Q5K/Q6K). JIT disk cache at | `parallel` | Rayon parallel iterators | | `cuda-monitor` | NVIDIA device info via trueno-gpu | | `cuda` | Full CUDA support via trueno-gpu | -| `ml-tuner` | ML-based kernel selection via aprender RandomForest | | `execution-graph` | PTX-to-kernel call graph profiling | | `tui-monitor` | Terminal monitoring dashboard | | `hardware-detect` | Hardware capability detection and caching | diff --git a/crates/aprender-compute/book/src/ecosystem/aprender.md b/crates/aprender-compute/book/src/ecosystem/aprender.md index bd8a8058fd..9d82ed5d31 100644 --- a/crates/aprender-compute/book/src/ecosystem/aprender.md +++ b/crates/aprender-compute/book/src/ecosystem/aprender.md @@ -10,51 +10,12 @@ The integration provides: - **RandomForestClassifier** for kernel selection - Training on benchmark data for hardware-specific optimization -## Enabling the Integration +## Where the Integration Lives -Add the `ml-tuner` feature to your `Cargo.toml`: - -```toml -[dependencies] -trueno = { version = "0.13", features = ["ml-tuner"] } -``` - -## Feature Matrix - -| Feature | Default | ml-tuner | -|---------|---------|----------| -| TunerFeatures (42-dim) | Yes | Yes | -| Heuristic prediction | Yes | Yes | -| Roofline clamping | Yes | Yes | -| RandomForest regressor | No | Yes | -| RandomForest classifier | No | Yes | -| Custom model training | No | Yes | - -## Usage Example - -```rust -use trueno::tuner::{ThroughputRegressor, TunerFeatures, QuantType}; - -// Create RF-backed regressor -let mut regressor = ThroughputRegressor::with_random_forest(100); - -// Collect benchmark data -let training_data: Vec<(TunerFeatures, f32)> = collect_benchmarks(); - -// Train the model -regressor.train_random_forest(&training_data)?; - -// Use trained model for predictions -let features = TunerFeatures::builder() - .model_params_b(7.0) - .batch_size(4) - .quant_type(QuantType::Q4K) - .gpu_mem_bw_gbs(1000.0) - .build(); - -let pred = regressor.predict(&features); -println!("Predicted throughput: {:.1} tok/s", pred.predicted_tps); -``` +The `ml-tuner` feature was removed (FLOW-010, #5803): trueno has no dependency on +aprender, so the SIMD foundation stays below the ML layer. The RandomForest showcase +runs from aprender-core's tests (`crates/aprender-core/tests/tuner_training_data_quality.rs`), +which depend on trueno, not the reverse. trueno keeps the heuristic models. ## Why aprender? @@ -119,7 +80,6 @@ let model_json = fs::read_to_string("throughput_model.json")?; let regressor: ThroughputRegressor = serde_json::from_str(&model_json)?; ``` -**Note:** RandomForest models are not serialized (marked `#[serde(skip)]`). After loading, you must retrain or use heuristic fallback. ## Further Reading diff --git a/crates/aprender-compute/book/src/performance/ml-tuner.md b/crates/aprender-compute/book/src/performance/ml-tuner.md index 2c1074284d..e72b374070 100644 --- a/crates/aprender-compute/book/src/performance/ml-tuner.md +++ b/crates/aprender-compute/book/src/performance/ml-tuner.md @@ -82,72 +82,15 @@ for (kernel, conf) in recommendation.alternatives.iter().take(3) { | M=2-3 | VectorizedQ4K | Low batch, vectorized still efficient | | M>=4 | BatchedQ4K | High batch, batched attention wins | -## RandomForest Models (Optional) +## RandomForest Models (Relocated) -With the `ml-tuner` feature, you can use aprender's RandomForest models for learned optimization: - -```toml -# Cargo.toml -[dependencies] -trueno = { version = "0.13", features = ["ml-tuner"] } -``` - -### Training a Custom Regressor - -```rust -use trueno::tuner::{ThroughputRegressor, TunerFeatures, QuantType}; - -// Create RF-backed regressor with 100 trees -let mut regressor = ThroughputRegressor::with_random_forest(100); - -// Generate training data from benchmarks -let training_data: Vec<(TunerFeatures, f32)> = (0..100) - .map(|i| { - let batch = 1 + (i % 8) as u32; - let features = TunerFeatures::builder() - .model_params_b(1.5) - .batch_size(batch) - .quant_type(QuantType::Q4K) - .gpu_mem_bw_gbs(1000.0) - .cuda_graphs(batch == 1) - .build(); - // Measured throughput from benchmark - let throughput = 200.0 + (batch as f32) * 80.0; - (features, throughput) - }) - .collect(); - -// Train the model -regressor.train_random_forest(&training_data)?; - -// Predictions now use learned model -let pred = regressor.predict(&features); -println!("RF prediction: {:.1} tok/s", pred.predicted_tps); -``` - -### Training a Custom Classifier - -```rust -use trueno::tuner::{KernelClassifier, TunerFeatures, QuantType}; - -let mut classifier = KernelClassifier::with_random_forest(50); - -// Label encoding: VectorizedQ4K=2, BatchedQ4K=3 -let training_data: Vec<(TunerFeatures, u32)> = (0..100) - .map(|i| { - let batch = 1 + (i % 8) as u32; - let features = TunerFeatures::builder() - .model_params_b(1.5) - .batch_size(batch) - .quant_type(QuantType::Q4K) - .build(); - let label = if batch >= 4 { 3 } else { 2 }; - (features, label) - }) - .collect(); - -classifier.train(&training_data)?; -``` +The `ml-tuner` feature is now a deprecated no-op (`ml-tuner = []`, kept so removing it +is not a semver break), and the `with_random_forest` / `train_random_forest` / +`KernelClassifier::train` wrappers were removed (FLOW-010, #5803): the SIMD compute +crate must not depend on the ML layer. The aprender RandomForest showcase now lives in +`crates/aprender-core/tests/tuner_training_data_quality.rs`, which feeds `TunerFeatures` +into `aprender::RandomForestRegressor` / `RandomForestClassifier`. This crate keeps the +heuristic `ThroughputRegressor` and `KernelClassifier`. ## Full Tuner Recommendations @@ -178,11 +121,8 @@ for exp in &rec.suggested_experiments { ## Running the Demo ```bash -# Default (heuristic models) +# Heuristic models cargo run --example ml_tuner_demo - -# With RandomForest models -cargo run --example ml_tuner_demo --features ml-tuner ``` ## Integration with ComputeBrick diff --git a/crates/aprender-compute/docs/prompts/95_percent_mandate.md b/crates/aprender-compute/docs/prompts/95_percent_mandate.md index 2f5309db32..92697b0b55 100644 --- a/crates/aprender-compute/docs/prompts/95_percent_mandate.md +++ b/crates/aprender-compute/docs/prompts/95_percent_mandate.md @@ -18,7 +18,7 @@ --package trueno --package trueno-gpu \ --lib \ --no-default-features \ - --features "parallel,cuda,wgpu,ml-tuner,execution-graph,hardware-detect,tracing" \ + --features "parallel,cuda,wgpu,execution-graph,hardware-detect,tracing" \ --html ``` 2. **Close the Gaps**: diff --git a/crates/aprender-compute/examples/README.md b/crates/aprender-compute/examples/README.md index 3dba4e73d9..cf6dc74bc4 100644 --- a/crates/aprender-compute/examples/README.md +++ b/crates/aprender-compute/examples/README.md @@ -25,8 +25,8 @@ This directory contains examples demonstrating Trueno's high-performance compute | `vocab_bench.rs` | Vocabulary processing benchmark | `cargo run --release --example vocab_bench` | | `profile_vocab.rs` | Vocabulary profiling | `cargo run --release --example profile_vocab` | | `execution_graph.rs` | Execution path graph demo | `cargo run --release --features execution-graph --example execution_graph` | -| `ml_tuner_demo.rs` | ML-based kernel selection | `cargo run --release --features ml-tuner --example ml_tuner_demo` | -| `ml_tuner_evolution.rs` | ML tuner evolution demo | `cargo run --release --features ml-tuner --example ml_tuner_evolution` | +| `ml_tuner_demo.rs` | Heuristic kernel selection and throughput prediction | `cargo run --release --example ml_tuner_demo` | +| `ml_tuner_evolution.rs` | ML tuner evolution demo | `cargo run --release --features hardware-detect --example ml_tuner_evolution` | | `model_tracing.rs` | Model-level inference tracing | `cargo run --release --example model_tracing` | | `tiling_demo.rs` | Tiling compute blocks | `cargo run --release --example tiling_demo` | | `tile_profiler_demo.rs` | Tile profiler demo | `cargo run --release --example tile_profiler_demo` | diff --git a/crates/aprender-compute/examples/ml_tuner_demo.rs b/crates/aprender-compute/examples/ml_tuner_demo.rs index b176d6e348..6adb5f46f7 100644 --- a/crates/aprender-compute/examples/ml_tuner_demo.rs +++ b/crates/aprender-compute/examples/ml_tuner_demo.rs @@ -6,9 +6,6 @@ //! //! Run with default (heuristic models): //! cargo run --example ml_tuner_demo -//! -//! Run with RandomForest models (requires ml-tuner feature): -//! cargo run --example ml_tuner_demo --features ml-tuner use trueno::tuner::{BrickTuner, KernelClassifier, QuantType, ThroughputRegressor, TunerFeatures}; @@ -163,101 +160,15 @@ fn demo_full_tuner(features: &TunerFeatures) { println!(); } -/// Section 6: RandomForest models (ml-tuner showcase, SHOWCASE-BRICK-001). -/// -/// The SIMD `trueno` tuner produces `TunerFeatures` vectors; this section feeds them -/// directly into `aprender::RandomForestRegressor`/`RandomForestClassifier`. aprender is -/// a **dev-dependency** of `aprender-compute`, so the compute lib itself never depends on -/// the ML layer — the showcase lives entirely here at the example layer, above both. +/// Section 6: RandomForest models (SHOWCASE-BRICK-001). The aprender half of the showcase +/// is tested in `crates/aprender-core/tests/tuner_training_data_quality.rs`: compute takes +/// no dependency on the ML layer, not even a dev one (FLOW-010, #5803). fn demo_random_forest(features: &TunerFeatures) { - #[cfg(feature = "ml-tuner")] - { - use aprender::primitives::{Matrix, Vector}; - use aprender::tree::{RandomForestClassifier, RandomForestRegressor}; - - println!("6. RandomForest Models (ml-tuner showcase)"); - println!(" --------------------------------------"); - - let dim = TunerFeatures::DIM; - let n = 100usize; - - // Regressor: trueno features -> predicted throughput (tok/s). - let mut xr = Vec::with_capacity(n * dim); - let mut yr = Vec::with_capacity(n); - for i in 0..n { - let batch = 1 + (i % 8) as u32; - let f = TunerFeatures::builder() - .model_params_b(1.5) - .batch_size(batch) - .quant_type(QuantType::Q4K) - .gpu_mem_bw_gbs(1000.0) - .cuda_graphs(batch == 1) - .build(); - xr.extend(f.to_vector()); - yr.push(200.0 + (batch as f32) * 80.0 + (i as f32 * 0.5)); - } - println!(" Generated {n} regression samples"); - match Matrix::from_vec(n, dim, xr) { - Ok(x_mat) => { - let mut rf = RandomForestRegressor::new(100); - match rf.fit(&x_mat, &Vector::from_vec(yr)) { - Ok(()) => { - let fx = Matrix::from_vec(1, dim, features.to_vector().to_vec()) - .expect("single-row feature matrix"); - let pred = rf.predict(&fx); - println!( - " RF regressor: SUCCESS — M=4 prediction {:.1} tok/s", - pred.as_slice()[0] - ); - } - Err(e) => println!(" RF regressor: FAILED - {e}"), - } - } - Err(e) => println!(" RF regressor matrix: FAILED - {e}"), - } - - // Classifier: trueno features -> kernel-type label (3=Batched, 2=Vectorized). - let mut xc = Vec::with_capacity(n * dim); - let mut yc: Vec = Vec::with_capacity(n); - for i in 0..n { - let batch = 1 + (i % 8) as u32; - let f = TunerFeatures::builder() - .model_params_b(1.5) - .batch_size(batch) - .quant_type(QuantType::Q4K) - .build(); - xc.extend(f.to_vector()); - yc.push(if batch >= 4 { 3 } else { 2 }); - } - match Matrix::from_vec(n, dim, xc) { - Ok(xc_mat) => { - let mut rf = RandomForestClassifier::new(50); - match rf.fit(&xc_mat, &yc) { - Ok(()) => { - let preds = rf.predict(&xc_mat); - let correct = - yc.iter().zip(preds.iter()).filter(|(a, b)| **a == **b).count(); - println!( - " RF classifier: SUCCESS — train accuracy {:.1}%", - correct as f32 / n as f32 * 100.0 - ); - } - Err(e) => println!(" RF classifier: FAILED - {e}"), - } - } - Err(e) => println!(" RF classifier matrix: FAILED - {e}"), - } - println!(); - } - - #[cfg(not(feature = "ml-tuner"))] - { - let _ = features; - println!("6. RandomForest Models"); - println!(" --------------------"); - println!(" [Disabled - enable with: --features ml-tuner]"); - println!(); - } + let _ = features; + println!("6. RandomForest Models"); + println!(" --------------------"); + println!(" [See aprender-core tests/tuner_training_data_quality.rs]"); + println!(); } fn main() { @@ -279,7 +190,7 @@ fn main() { println!(" - TunerFeatures: 42-dimension vector for ML models"); println!(" - Throughput prediction with roofline clamping (v1.1.0)"); println!(" - Kernel selection: BatchedQ4K for M>=4, VectorizedQ4K otherwise"); - println!(" - RandomForest available with --features ml-tuner"); + println!(" - RandomForest showcase: aprender-core tests/tuner_training_data_quality.rs"); println!(); println!("Next steps:"); println!(" cargo run --example quickstart # Basic trueno usage"); diff --git a/crates/aprender-compute/src/tuner/error.rs b/crates/aprender-compute/src/tuner/error.rs index bb9fd2e631..bb775e3900 100644 --- a/crates/aprender-compute/src/tuner/error.rs +++ b/crates/aprender-compute/src/tuner/error.rs @@ -15,7 +15,7 @@ pub enum TunerError { ModelNotFound, /// Prediction failed PredictionFailed(String), - /// Training failed (ml-tuner feature) + /// Training failed TrainingFailed(String), /// I/O error (file operations) Io(String), diff --git a/crates/aprender-compute/src/tuner/models/kernel.rs b/crates/aprender-compute/src/tuner/models/kernel.rs index 019b9037ef..0e392bbf49 100644 --- a/crates/aprender-compute/src/tuner/models/kernel.rs +++ b/crates/aprender-compute/src/tuner/models/kernel.rs @@ -9,10 +9,10 @@ use super::KernelRecommendation; /// Kernel classifier using simple rule-based logic. /// -/// The `ml-tuner` showcase (SHOWCASE-BRICK-001) trains an +/// The RandomForest showcase (SHOWCASE-BRICK-001) trains an /// `aprender::RandomForestClassifier` on these features in -/// `examples/ml_tuner_demo.rs` — aprender is a dev-dependency so the SIMD foundation -/// stays free of the ML layer (no compute→core layer inversion). +/// `crates/aprender-core/tests/tuner_training_data_quality.rs` — compute has no +/// dependency on aprender-core, so the SIMD foundation stays free of the ML layer. #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct KernelClassifier { /// Kernel accuracy on validation (for confidence) diff --git a/crates/aprender-compute/src/tuner/models/throughput.rs b/crates/aprender-compute/src/tuner/models/throughput.rs index 373f7bdde9..9c4ff3f23a 100644 --- a/crates/aprender-compute/src/tuner/models/throughput.rs +++ b/crates/aprender-compute/src/tuner/models/throughput.rs @@ -8,10 +8,10 @@ use super::ThroughputPrediction; /// Simple linear regression model for throughput prediction. /// -/// Uses closed-form solution: w = (X^T X)^-1 X^T y. The `ml-tuner` showcase +/// Uses closed-form solution: w = (X^T X)^-1 X^T y. The RandomForest showcase /// (SHOWCASE-BRICK-001) feeds these features into `aprender::RandomForestRegressor` -/// from `examples/ml_tuner_demo.rs` (aprender is a dev-dependency — the SIMD foundation -/// must not depend on the ML layer). +/// from `crates/aprender-core/tests/tuner_training_data_quality.rs` (compute has no +/// aprender-core dependency — the SIMD foundation must not depend on the ML layer). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThroughputRegressor { /// Model weights (one per feature + bias) diff --git a/crates/aprender-compute/src/tuner/tests/coverage/gap_coverage.rs b/crates/aprender-compute/src/tuner/tests/coverage/gap_coverage.rs index 356ba8d255..9c4848100d 100644 --- a/crates/aprender-compute/src/tuner/tests/coverage/gap_coverage.rs +++ b/crates/aprender-compute/src/tuner/tests/coverage/gap_coverage.rs @@ -90,13 +90,13 @@ mod calibrate_tests { } // ============================================================================ -// 2-3. ml-tuner RandomForest showcase (RELOCATED — SHOWCASE-BRICK-001) +// 2-3. RandomForest showcase (RELOCATED — SHOWCASE-BRICK-001) // ============================================================================ // The `ThroughputRegressor::with_random_forest` / `train_random_forest` and // `KernelClassifier::train` wrappers were removed: the SIMD compute lib must not // depend on the ML layer (aprender). The aprender RandomForest showcase now lives in -// examples/ml_tuner_demo.rs + tests/tuner_falsification/training_data_quality.rs, -// which consume aprender as a dev-dependency. No lib coverage target remains here. +// crates/aprender-core/tests/tuner_training_data_quality.rs (FLOW-010, #5803), +// which tests it from the aprender-core side. No lib coverage target remains here. // ============================================================================ // 4. crc32_table (helpers.rs, line 10) — const fn coverage diff --git a/docs/roadmaps/entries/PMAT-5803.yaml b/docs/roadmaps/entries/PMAT-5803.yaml index ce6f9753f1..1dcb0dcd8f 100644 --- a/docs/roadmaps/entries/PMAT-5803.yaml +++ b/docs/roadmaps/entries/PMAT-5803.yaml @@ -1,7 +1,7 @@ - id: PMAT-5803 github_issue: 5803 item_type: task - title: 'build: FLOW-010 cut 1, slice 1 of 3 — move the tuner training-data-quality tests into aprender-core' + title: 'build: FLOW-010 cut 1, slice 2 of 3 — aprender-compute drops its dev-dependency on aprender-core' status: in_progress priority: medium assigned_to: null @@ -10,6 +10,7 @@ spec: null acceptance_criteria: - the tuner training-data-quality tests move from crates/aprender-compute/tests/tuner_falsification to crates/aprender-core/tests/tuner_training_data_quality.rs and still run (ci/explicit-test-commands.d fragments 295 and 298 name them) + - aprender-compute has no dev-dependency on aprender-core (Cargo.toml, Cargo.lock, the ml_tuner_demo example and the docs that cited it) - this slice is one PR of at most 400 changed lines (the 810-line move in #6294 was split; no size waiver, quorum triage-quorum-6336-sizewaiver2-0816Z) phases: [] subtasks: [] diff --git a/docs/roadmaps/roadmap.yaml b/docs/roadmaps/roadmap.yaml index eddc2ac474..adb6e601a1 100644 --- a/docs/roadmaps/roadmap.yaml +++ b/docs/roadmaps/roadmap.yaml @@ -25400,7 +25400,7 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe - id: PMAT-5803 github_issue: 5803 item_type: task - title: 'build: FLOW-010 cut 1, slice 1 of 3 — move the tuner training-data-quality tests into aprender-core' + title: 'build: FLOW-010 cut 1, slice 2 of 3 — aprender-compute drops its dev-dependency on aprender-core' status: in_progress priority: medium assigned_to: null @@ -25409,6 +25409,7 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe spec: null acceptance_criteria: - the tuner training-data-quality tests move from crates/aprender-compute/tests/tuner_falsification to crates/aprender-core/tests/tuner_training_data_quality.rs and still run (ci/explicit-test-commands.d fragments 295 and 298 name them) + - aprender-compute has no dev-dependency on aprender-core (Cargo.toml, Cargo.lock, the ml_tuner_demo example and the docs that cited it) - this slice is one PR of at most 400 changed lines (the 810-line move in #6294 was split; no size waiver, quorum triage-quorum-6336-sizewaiver2-0816Z) phases: [] subtasks: []