Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/guards-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ jobs:
run: setsid --wait bash scripts/tests/compute_row_zero_tests_test.sh --mutants
- name: shared-checkout shallow lock case table and mutants (#5025, LAB)
run: setsid --wait bash scripts/shared_checkout_no_shallow.sh --mutants
- name: Parity-receipt count equals EXPECTED_RECEIPTS (verify mode, #5502, LAB)
id: lab_parity_receipts
if: ${{ !cancelled() }}
run: |
set -euo pipefail
exec > >(tee -a "$RUNNER_TEMP/parity-receipts.log") 2>&1
setsid --wait bash scripts/parity_receipt_denominator.sh
- name: nextest list-wrapper case table (#5298, LAB)
run: setsid --wait bash scripts/ci/nextest_list_wrapper.sh --self-test
- name: affected_set.sh case table and mutants (FLOW-010 E5, LAB)
Expand Down Expand Up @@ -429,6 +436,36 @@ jobs:
set -euo pipefail
bash scripts/lab_ticket.sh --label lab-affected-readers --title "LAB: nightly affected-set guards red (#5809)" \
--owner aprender-w5018 --log "$RUNNER_TEMP/affected.log" --event "$GITHUB_EVENT_NAME" --run-url "$RUN_URL"
# #5803/#5804: the moved tuner tests must still be listed by cargo exactly once, none ignored. LAB, never on a PR path (C324):
# it invokes cargo, so it runs only in this night job; the moved tests themselves run from ci/explicit-test-commands.d 295 and 298.
- name: Tuner test-move guard (#5803)
id: lab_tuner_test_move
if: ${{ !cancelled() }}
run: |
set -euo pipefail
exec > >(tee -a "$RUNNER_TEMP/tuner-move.log") 2>&1
setsid --wait bash scripts/check_tuner_test_move.sh --self-test
setsid --wait bash scripts/check_tuner_test_move.sh
- name: A red tuner-test-move night opens or updates its one LAB ticket (#5803)
if: failure() && steps.lab_tuner_test_move.outcome == 'failure'
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
bash scripts/lab_ticket.sh --label lab-tuner-test-move --title "LAB: nightly tuner test-move guard red (#5803)" \
--owner aprender-w5018 --log "$RUNNER_TEMP/tuner-move.log" --event "$GITHUB_EVENT_NAME" --run-url "$RUN_URL"
- name: A red parity-receipt night opens or updates its one LAB ticket (#5502)
if: failure() && steps.lab_parity_receipts.outcome == 'failure'
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
bash scripts/lab_ticket.sh --label lab-parity-receipts --title "LAB: parity-receipt count differs from EXPECTED_RECEIPTS (#5502)" \
--owner aprender-w5018 --log "$RUNNER_TEMP/parity-receipts.log" --event "$GITHUB_EVENT_NAME" --run-url "$RUN_URL"
- name: A red census night opens or updates the one LAB ticket (#4709)
if: failure() && steps.ratchet_census.outcome == 'failure'
env:
Expand Down
20 changes: 14 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ members = [
# --- APR-MONO Phase 2b: was provable-contracts ---
"crates/aprender-contracts",
"crates/aprender-contracts-macros",
"crates/aprender-contracts-key",
"crates/aprender-contracts-cli",
# EPIC #4232: shared update check + `<bin> update` for every sovereign binary.
"crates/aprender-update",
Expand Down Expand Up @@ -321,6 +322,7 @@ aprender-train-common = { path = "crates/aprender-train-common", version = "0.71
aprender-train-distill = { path = "crates/aprender-train-distill", version = "0.71.3" }
aprender-orchestrate = { path = "crates/aprender-orchestrate", version = "0.71.3" }
aprender-contracts = { path = "crates/aprender-contracts", version = "0.71.3" }
aprender-contracts-key = { path = "crates/aprender-contracts-key", version = "0.71.3" }
aprender-build-sha = { path = "crates/aprender-build-sha", version = "0.71.3" }
aprender-rollup = { path = "crates/aprender-rollup", version = "0.71.3" }
aprender-profile = { path = "crates/aprender-profile", version = "0.71.3" }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ publishing — all backed by YAML provable contracts that fail CI on drift.
| Metric | Count | Source of truth |
|-------:|------:|---|
| Workspace crates | **81** workspace crates | `cargo metadata --no-deps` (NOT `ls crates/` — 4 are `exclude`d, 1 has no Cargo.toml) |
| Provable contracts | **<!-- CONTRACT_COUNT_START -->2005<!-- CONTRACT_COUNT_END -->** provable contracts | `contracts/census.json` `.n_files` — the set `pv lint` walks (`pv census`, ONT-001 ONT-1; regenerated by `make contracts`, written by `make readme-sync`, guarded by `scripts/check_readme_claims.sh`) |
| Provable contracts | **<!-- CONTRACT_COUNT_START -->2006<!-- CONTRACT_COUNT_END -->** provable contracts | `contracts/census.json` `.n_files` — the set `pv lint` walks (`pv census`, ONT-001 ONT-1; regenerated by `make contracts`, written by `make readme-sync`, guarded by `scripts/check_readme_claims.sh`) |
| CLI commands | **113** CLI commands | `contracts/apr-cli-commands-v1.yaml` §`commands` (parse the list; `apr --help` also lists `help` itself, and `grep -c '^ - name:'` gives 119 — other same-indent `name:` keys exist in the file) |
| Book CLI chapters | **114** chapters | `ls book/src/cli/*.md` |
| Book lib chapters | **73** chapters | `ls book/src/lib/*.md` (parity with `pub mod`) |
Expand Down Expand Up @@ -403,7 +403,7 @@ falsification_tests:
prediction: apr validate bad-model.apr exits non-zero
```

The tree carries <!-- CONTRACT_COUNT_START -->2005<!-- CONTRACT_COUNT_END --> contracts across inference, training, quantization, attention, FFN,
The tree carries <!-- CONTRACT_COUNT_START -->2006<!-- CONTRACT_COUNT_END --> contracts across inference, training, quantization, attention, FFN,
tokenization, model formats, CLI safety — and this README itself.

## Migration from old crates
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cargo test -p aprender-core --test tuner_training_data_quality
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cargo test -p aprender-compute --test tuner_falsification
7 changes: 7 additions & 0 deletions contracts/contracts.nt
Original file line number Diff line number Diff line change
Expand Up @@ -9774,6 +9774,13 @@
<https://ont.paiml.dev/v1alpha1/contract/course-flow-batch-wip-v1> <https://ont.paiml.dev/v1alpha1/id> "course-flow-batch-wip-v1"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-batch-wip-v1> <https://ont.paiml.dev/v1alpha1/name> "course-flow-batch-wip"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-batch-wip-v1> <https://ont.paiml.dev/v1alpha1/version> "1.0.0"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/prov#Entity> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Contract> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Kernel> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <https://ont.paiml.dev/v1alpha1/file> "contracts/course-flow-content-factory-v2-v1.yaml"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <https://ont.paiml.dev/v1alpha1/id> "course-flow-content-factory-v2-v1"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <https://ont.paiml.dev/v1alpha1/name> "course-flow-content-factory-v2"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/course-flow-content-factory-v2-v1> <https://ont.paiml.dev/v1alpha1/version> "1.0.0"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/cpp-type-preservation-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/prov#Entity> .
<https://ont.paiml.dev/v1alpha1/contract/cpp-type-preservation-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Contract> .
<https://ont.paiml.dev/v1alpha1/contract/cpp-type-preservation-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Kernel> .
Expand Down
66 changes: 66 additions & 0 deletions contracts/course-flow-content-factory-v2-v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
metadata:
version: "1.0.0"
created: "2026-10-19"
author: PAIML Engineering
registry: true
references:
- "flow model spec, internal (eqs 1 and 2)"
- "FLOW-007 row B5 (#5557)"
description: "course_flow models Content Factory v2: three-platform fan-out, rework loop, trunk-first GPU rule, LinkedIn renders, work vs elapsed build time"

name: course-flow-content-factory-v2
version: "1.0.0"
scope: "crates/aprender-simulate/src/scenarios/course_flow.rs"

description: |
The factory-v2 case sends every master to edX, Coursera and LinkedIn with no split.
A build repeats with probability alpha, so the mean build work is B/(1-alpha). While the
running mean lead exceeds trunk_first_w_d days the GPU serves courses before Shorts.
LinkedIn adds li_k + 4 renders per master on the GPU. The result keeps build work
(sizing, eq 2) apart from build elapsed time (lead time, eq 1).

equations:
coursera_stage_load:
description: "Coursera stage busy fraction when every master takes the stage (the Chrome lanes also carry the Shorts stage, which the test adds, about 0.87 points)"
formula: "busy = starts_per_day * stage_min / (chrome_lanes * 1440) = 0.625, plus the Shorts stage load"

rework_build_work:
description: "mean build work per course under the rework loop"
formula: "B' = B / (1 - alpha)"

falsification_tests:
- id: FALSIFY-001
description: "Coursera stage busy matches the law within 2 points"
test: "cargo test -p aprender-simulate --lib coursera_stage_matches_the_law_within_two_points"
prediction: "the test passes"
if_fails: "the fan-out does not send every master to the Coursera stage"

- id: FALSIFY-002
description: "at alpha 0.16 the build stage is busy at the law's 94.7% within two points"
test: "cargo test -p aprender-simulate --lib build_busy_at_alpha_016_matches_the_law"
prediction: "the test passes"
if_fails: "the rework loop is missing or mis-sized"

- id: FALSIFY-003
description: "mean build work is B/(1-alpha)"
test: "cargo test -p aprender-simulate --lib rework_loop_scales_build_work_by_one_over_one_minus_alpha"
prediction: "the test passes"
if_fails: "the rework loop does not scale build work by 1/(1-alpha)"

- id: FALSIFY-004
description: "build elapsed time exceeds build work (they are reported apart)"
test: "cargo test -p aprender-simulate --lib build_elapsed_is_not_the_build_work"
prediction: "the test passes"
if_fails: "work and elapsed build time are conflated"

- id: FALSIFY-005
description: "the trunk-first rule is not a no-op: the rule fires only on the render stage, with a positive threshold, after a course finished and while the mean lead exceeds it, and turning it on changes the run (LinkedIn renders are covered by linkedin_renders_load_the_gpu, not by this test)"
test: "cargo test -p aprender-simulate --lib trunk_first_rule_changes_the_outcome"
prediction: "the test passes"
if_fails: "the GPU priority rule is dead"

- id: FALSIFY-006
description: "alpha is a probability below 1 (1 or more would repeat a build forever) and li_k is bounded to [0, 64] (li_k + 4 renders per master)"
test: "cargo test -p aprender-simulate --lib alpha_must_be_below_one"
prediction: "the test passes"
if_fails: "apply_course_inputs accepts an alpha that never terminates"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"census_id_set_sha256": "e01a28975ffa19f5a1ed1edd3f59d7eacd73a80decd44c723be4fe5dd9b53e7e",
"census_id_set_sha256": "4039ff1d878c323278405342882da062310a234a5674cf56a0086a75015eb880",
"relations_sha256": "811c78184eb2609244c59534f015f0be2f90923120a3027c176ad56723d0d850",
"checkable_n": 87,
"result": {
Expand Down
18 changes: 6 additions & 12 deletions crates/aprender-compute/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -84,7 +82,7 @@ web-sys = { version = "0.3", optional = true, features = ["console"] }
hostname = "0.4"

[build-dependencies]
provable-contracts = { workspace = true } # #4369: build_helper::env_key, the one CONTRACT_* key builder
aprender-contracts-key = { workspace = true } # #4369, #5909: the one CONTRACT_* key builder (no engine dependency)
serde = { version = "1", features = ["derive"] }
serde-saphyr = { workspace = true }

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion crates/aprender-compute/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
50 changes: 5 additions & 45 deletions crates/aprender-compute/book/src/ecosystem/aprender.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down Expand Up @@ -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

Expand Down
Loading
Loading