aprender 0.72.2

Next-generation ML framework in pure Rust — `cargo install aprender` for the `apr` CLI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
---
schema_version: "1.0"
kind: library
entrypoints: [crates/aprender-contracts-cli]
contract_count: 1837
---

<p align="center">
  <img src="docs/hero.svg" alt="aprender — Next-generation ML framework in pure Rust" width="100%">
</p>

<p align="center">
  <a href="https://crates.io/crates/aprender">
    <img src="https://img.shields.io/crates/v/aprender.svg" alt="crates.io">
  </a>
  <a href="https://docs.rs/aprender">
    <img src="https://docs.rs/aprender/badge.svg" alt="docs.rs">
  </a>
  <a href="https://github.com/paiml/aprender/actions/workflows/ci.yml">
    <img src="https://github.com/paiml/aprender/actions/workflows/ci.yml/badge.svg" alt="CI">
  </a>
  <a href="LICENSE">
    <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
  </a>
</p>

## Quick Start

Pre-built and ready to use (Linux x86_64/aarch64 only today — other platforms fall
through to `cargo install` below):

```text
curl -LsSf https://paiml.com/apr/install.sh | sh
```

To read the script before it runs, download it with `curl -LsSf https://paiml.com/apr/install.sh -o install.sh`, review it, then run `sh install.sh`. The installer is POSIX `sh` and checks the downloaded release archive against its published `.sha256` before installing.

Compile and build:

```text
cargo install aprender                    # CPU ONLY - no GPU backend is compiled in
cargo install aprender --features cuda    # NVIDIA GPU acceleration
cargo install aprender --features full    # everything (training, visualization, zram)
apr pull hf://Qwen/Qwen2.5-Coder-0.5B-Instruct
apr list
apr run ~/.cache/pacha/models/1e3be9285aea6923.safetensors "2+2?"
```

full log of output

<details>

```text

apr pull hf://Qwen/Qwen2.5-Coder-0.5B-Instruct
== APR Pull ===

Model: hf://Qwen/Qwen2.5-Coder-0.5B-Instruct/model.safetensors

Downloading (streaming)...
 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
✓ Downloaded successfully
  Path: ~/.cache/pacha/models/1e3be9285aea6923.safetensors
  Size: 942.3 MB
...  
Usage:
  apr run ~/.cache/pacha/models/1e3be9285aea6923.safetensors
  apr serve ~/.cache/pacha/models/1e3be9285aea6923.safetensors
  
apr list
=== Cached Models ===

NAME                                     SIZE         FORMAT       PATH
--------------------------------------------------------------------------------------------------------
1e3be9285aea6923                         942.3 MB     SafeTensors  ~/.cache/pacha/models/1e3be9285aea6923.safetensors (orphan)

Total: 1 models (0 tracked + 1 orphans), 942.3 MB used
apr run ~/.cache/pacha/models/1e3be9285aea6923.safetensors "what is 2+2?"
=== APR Run ===

Source: ~/.cache/pacha/models/1e3be9285aea6923.safetensors
[GH-189] Loaded tokenizer from ~/.cache/pacha/models/1e3be9285aea6923.tokenizer.json: 22 special tokens

Output:
2 + 2 equals 4.

Completed in 4.82s (cached)

apr run ~/.cache/pacha/models/1e3be9285aea6923.safetensors "can you help me summarize quick sort algorithm, in less than 100 words"
=== APR Run ===

Source: ~/.cache/pacha/models/1e3be9285aea6923.safetensors
[GH-189] Loaded tokenizer from ~/.cache/pacha/models/1e3be9285aea6923.tokenizer.json: 22 special tokens

Output:
Quick sort is a divide-and-conquer algorithm that sorts an array of elements. It works by selecting a 'pivot' element from the array and partitioning the

Completed in 7.03s (cached)
$
```

</details>

For release notes see [GitHub Releases](https://github.com/paiml/aprender/releases).

## What is Aprender?

A complete ML framework in pure Rust. One `cargo install`, one `apr` binary,
the full model lifecycle — inference, training, quantization, profiling,
publishing — all backed by YAML provable contracts that fail CI on drift.

### At HEAD

| 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`) |
| 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`) |

These numbers are enforced by [`contracts/readme-claims-v1.yaml`](contracts/readme-claims-v1.yaml).
Drift between this table and live repo state fails `bash scripts/check_readme_claims.sh`
→ see [FALSIFY-README-001..006](contracts/readme-claims-v1.yaml).

### Verified this release

<!-- RELEASE_MATRIX_START (generated by release_section.rs — do not edit) -->

Verified matrix for **0.69.1**, from the ladder receipts:

A `pass` cell means `apr run` completed on that host without falling back and its golden output matched. It does **not** mean `chat`, `code` and `serve` passed: the ladder records those verbs but its `green` does not read them.

A non-`pass` cell names the gates the receipt records, which is not always the cause: a reason `apr qa` computed in the same run is not carried here unless the receipt accounts for it (`qa_rc` is the tell).

| Model rung | gx10 | lambda |
|---|---|---|
| `qwen2-1.5b-q4km` | pass | pass |
| `qwen3-1.7b-q4km` | pass | pass |
| `qwen3-8b-q4km` | pass | pass |
| `qwen35-0.8b-q4km` | pass | pass |
| `qwen35-2b-q4km` | pass | pass |
| `qwen35-4b-q4km` | pass | pass |
| `qwen35-9b-q4km` | pass | pass |
| `qwen35-27b-q4km` | **FAIL** | pass |
| `inv:Qwen3-30B-A3B-Instruct-2507-Q4_K_M.gguf` | **FAIL** | **FAIL** |
| `inv:Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf` | **FAIL** | **FAIL** |
| `inv:qwen2.5-coder-0.5b-instruct-q4_k_m.gguf` | pass | pass |
| `inv:qwen2.5-coder-1.5b-instruct-fp16.apr` | pass | — |
| `inv:qwen2.5-coder-1.5b-instruct-q4_k_m.apr` | pass | — |
| `inv:qwen2.5-coder-1.5b-instruct-q4_k_m.gguf` | pass | pass |
| `inv:qwen2.5-coder-1.5b-instruct-q4k.apr` | pass | pass |
| `inv:qwen2.5-coder-32b-instruct-q4_k_m.gguf` | pass | — |
| `inv:qwen2.5-coder-7b-instruct-q4_k_m.gguf` | pass | pass |
| `inv:Qwen2.5-0.5B-Instruct-IQ3_M.gguf` | — | pass |
| `inv:Qwen2.5-0.5B-Instruct-IQ4_XS.gguf` | — | pass |
| `inv:Qwen2.5-0.5B-Instruct-f16.gguf` | — | **FAIL** |
| `inv:Qwen3.5-0.8B-IQ4_XS.gguf` | — | **FAIL** |
| `inv:Qwen3.5-0.8B-UD-IQ2_XXS.gguf` | — | **FAIL** |
| `inv:Qwen3.5-35B-A3B-UD-IQ4_XS.gguf` | — | **FAIL** |
| `inv:Qwen3.5-4B-UD-Q4_K_XL.gguf` | — | pass |
| `inv:qwen2.5-coder-0.5b-instruct.apr` | — | **FAIL** |
| `inv:qwen2.5-coder-1.5b-instruct-q4k-v2.apr` | — | pass |
| `inv:qwen2.5-coder-1.5b-instruct-st.apr` | — | pass |
| `inv:qwen2.5-coder-1.5b-q4k.apr` | — | pass |
| `inv:tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf` | — | pass |

- **gx10**: 17 rung(s), 17 required, 3 not green — `apr 0.69.1 (9b7739951)`
- **lambda**: 26 rung(s), 26 required, 7 not green — `apr 0.69.1 (9b7739951)`

<!-- RELEASE_MATRIX_END -->

> **Known issue — Qwen3 MoE (30B-A3B) on GB10: not claimed for 0.70; planned for 0.70.1 (#3715).** Every verb
> times out at 600 s on the 20k-token rung on GB10 (sm_121), so qwen3moe on GB10 is withdrawn from this
> release's claims (`cells.declaimed`).

> **Known issue — GB10 dense models: prompt processing unbatched; planned for 0.70.1 (#4590).** Output is correct; only prefill speed is affected. On GB10 (sm_121) every dense
> model (Qwen2.5, Qwen3) is withdrawn from this release's claims (`cells.declaimed` in
> [`contracts/model-capability-ladder-v1.yaml`]contracts/model-capability-ladder-v1.yaml); Qwen3.5 on
> GB10 and every model on other hosts are still claimed and gated.

### Command surface

| Stage | Commands |
|---|---|
| **Inference** | `apr run`, `apr chat`, `apr serve` |
| **Training** | `apr finetune`, `apr train`, `apr pretrain`, `apr distill` |
| **Model ops** | `apr convert`, `apr quantize`, `apr merge`, `apr export`, `apr compile` |
| **Inspection** | `apr inspect`, `apr validate`, `apr tensors`, `apr diff`, `apr trace`, `apr lint` |
| **Profiling** | `apr profile`, `apr bench`, `apr qa` |
| **Registry** | `apr pull`, `apr list`, `apr rm`, `apr publish`, `apr registry` |
| **GPU** | `apr gpu`, `apr parity`, `apr ptx` |
| **Observability** | `apr tui`, `apr monitor`, `apr cbtop` |

## Cookbook

End-to-end recipes (data prep → train → quantize → publish → serve) live in
[`paiml/apr-cookbook`](https://github.com/paiml/apr-cookbook) — **1,825** worked examples (apr-cookbook, 2026-09-12),
with local `book/src/` walkthroughs. The count is the
`[[example]]` entries the cookbook declares, pinned and gated by
[`contracts/readme-claims-v1.yaml`](contracts/readme-claims-v1.yaml) — it lives in another
repository, so it carries the date it was measured rather than pretending to be live.

```text
git clone https://github.com/paiml/apr-cookbook
cd apr-cookbook
cargo run --example bundle_static_model         # any example
mdbook serve book                               # walkthrough docs
```

## Install

```text
cargo install aprender    # installs the `apr` binary
apr --version
```

## A Qwen story

Eight beats, one narrative, every core command group. Anchored on the Qwen
series so the story scales from a 494-MB safetensors model to a 30 B-parameter
MoE GGUF. Every beat is a falsifier in
[`contracts/qwen-story-v1.yaml`](contracts/qwen-story-v1.yaml); the runnable
form is [`scripts/qwen-story.sh`](scripts/qwen-story.sh); nightly cron is
[`.github/workflows/qwen-story-daily.yml`](.github/workflows/qwen-story-daily.yml);
the dogfood gate is `/apr-dogfood` Gate 18.

```text
# Reproduce locally (uses ~/models cache; ~3-5 min on RTX 4090):
bash scripts/qwen-story.sh
```

### Beat 1 — Discover (Registry)

```text
apr pull hf://Qwen/Qwen2.5-Coder-0.5B-Instruct      # 494 MB safetensors
apr list                                            # confirm cached
```

### Beat 2 — Trust (QA gates)

```text
apr qa qwen2.5-coder-1.5b-instruct-q4k              # 12 falsifiable gates
apr validate qwen2.5-coder-1.5b-instruct-q4k --quality   # scores the checks that ran
apr lint qwen2.5-coder-1.5b-instruct-q4k            # best-practice signals
```

### Beat 3 — Explore (Inspection)

```text
apr inspect --json qwen2.5-coder-1.5b-instruct-q4k  # arch, params, tensors
apr tensors --json qwen2.5-coder-1.5b-instruct-q4k  # 339 tensors with shapes
apr tree qwen2.5-coder-1.5b-instruct-q4k            # layer architecture
```

### Beat 4 — Adapt (Model ops)

```text
apr export qwen2.5-coder-1.5b-instruct-q4k --format gguf -o roundtrip.gguf
apr diff qwen2.5-coder-1.5b-instruct-q4k roundtrip.gguf  # tensor-by-tensor delta
apr convert model.safetensors --quantize q4_k -o quantized.apr
```

### Beat 5 — Use (Inference)

```text
apr run qwen2.5-coder-1.5b-instruct-q4k "fn sum(a: i32, b: i32) -> i32 {" --max-tokens 16
apr chat qwen2.5-coder-1.5b-instruct-q4k            # interactive REPL
apr code -p "review this Python function" --max-turns 1   # agent mode (PMAT-182)
```

### Beat 6 — Serve (REST API)

```text
apr serve run qwen2.5-coder-1.5b-instruct-q4k --port 8080
curl -s localhost:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen","messages":[{"role":"user","content":"What is 2+2?"}],"max_tokens":8}'
# → {"choices":[{"message":{"content":"2 + 2 equals 4."}}],...}
```

### Beat 7 — Operate (Profiling)

```text
apr profile qwen2.5-coder-7b-instruct-q4_k_m        # Roofline analysis
apr gpu --json                                      # VRAM, sm_*, cuda version
apr serve plan qwen2.5-coder-7b-instruct-q4_k_m     # capacity plan before run
```

### Beat 8 — Scale (MoE introspection)

```text
apr inspect --json Qwen3-Coder-30B-A3B-Instruct     # arch=qwen3moe, 30 B params
apr tensors --json Qwen3-Coder-30B-A3B-Instruct     # 579 tensors (MoE expert layout)
```

> **0.69.1 does not support `qwen3moe` on CUDA.** Mixture-of-experts models
> (`Qwen3-30B-A3B-Instruct-2507`, `Qwen3-Coder-30B-A3B-Instruct`) run on the
> **CPU only**: `apr run <model>` works, and `apr run --gpu <model>` **refuses
> before loading**, naming the architecture and exiting 12 rather than
> generating on the CPU and reporting a fallback. The MoE GPU forward is
> [#3714]https://github.com/paiml/aprender/issues/3714 and lands in 0.70.0.
> Introspection above (`inspect`, `tensors`) is unaffected — it reads the
> header, not a forward.

### Publish (separate flow)

```text
# Publish a derived model to HuggingFace Hub (see SPEC-HF-PUBLISH-001 for the 12-file pipeline)
apr stamp ckpt.apr --tokenizer /path/to/qwen-tokenizer --license Apache-2.0 -o staging/model.apr
apr export staging/model.apr --format gguf --quantize int4 -o staging/model-q4k.gguf
apr publish staging/ paiml/my-model-v1 --library-name aprender --license Apache-2.0
```

When run with `PMAT_HUNT=1` (default), each beat emits a manifest of high-risk
untested code in the command modules it just exercised. A nightly cron opens an
issue when this manifest grows so untested branches in command handlers can't
accumulate quietly. See [`contracts/qwen-story-v1.yaml`](contracts/qwen-story-v1.yaml).

Publishing a model? See [SPEC-HF-PUBLISH-001](docs/specifications/aprender-train/model-hf-publish-pipeline-spec.md)
for the 12-file integration pipeline, three-path verification protocol, and HF API
gotchas (NDJSON commits, LFS batch sizing, Q4_K stride constraints).

## Library usage

```toml
[dependencies]
aprender = "0.35"
```

```rust
use aprender::linear_regression::LinearRegression;
use aprender::traits::Estimator;

let model = LinearRegression::new();
model.fit(&x_train, &y_train)?;
let predictions = model.predict(&x_test)?;
```

Algorithms: Linear/Logistic Regression, Decision Trees, Random Forest, GBM,
Naive Bayes, KNN, SVM, K-Means, PCA, ARIMA, ICA, GLMs, graph algorithms,
Bayesian inference, text + audio processing.

## Architecture

Monorepo, flat `crates/aprender-*` layout (same pattern as
[Polars](https://github.com/pola-rs/polars),
[Burn](https://github.com/tracel-ai/burn),
[Nushell](https://github.com/nushell/nushell)):

```
paiml/aprender/
├── Cargo.toml                      # Workspace root + `cargo install aprender`
├── crates/
│   ├── aprender-core/              # ML library (use aprender::*)
│   ├── apr-cli/                    # CLI logic (count in the table above)
│   ├── aprender-compute/           # SIMD/GPU compute kernels
│   ├── aprender-gpu/               # CUDA PTX
│   ├── aprender-serve/             # Inference server
│   ├── aprender-train/             # Training loops
│   ├── aprender-orchestrate/       # Agents + RAG
│   ├── aprender-contracts/         # Provable contracts engine
│   ├── aprender-profile/           # Profiling
│   ├── aprender-db/ aprender-graph/ aprender-rag/
│   └── ... (**84** directories — NOT the crate count: see the table above)
├── contracts/                      # provable YAML contracts (count in the table above)
└── book/                           # mdBook documentation
```

## Performance

Every published throughput or comparator figure lives with the run that produced
it. The measurement ledger is [`evidence/parity/LEDGER.md`](evidence/parity/LEDGER.md);
the rules a figure must satisfy before it may be quoted anywhere — including on
this page — are in
[`docs/specifications/PP-LLAMA-001-MASTER.md`](docs/specifications/PP-LLAMA-001-MASTER.md).

This section carried a three-row table of `tok/s` figures until 2026-09-02. None
of the three cited a receipt, and the RTX 4090 row asserted a rate the tree's own
paired measurement contradicts, so all three are withdrawn rather than restated
(PP-LLAMA-001 §2.1, PP-12). A withdrawal that reprints the number leaves the
number on the page.

The harnesses that reproduce the comparisons, without their numbers:
[candle-vs-apr](https://github.com/paiml/candle-vs-apr) for inference and
[ground-truth-apr-ludwig](https://github.com/paiml/ground-truth-apr-ludwig) for
training. A figure from either belongs on this page only once it carries a
receipt in the ledger.

## Provable contracts

Every CLI command and kernel is bound to a YAML contract with equations,
preconditions, postconditions, and falsification tests:

```yaml
equations:
  validate_exit_code:
    formula: exit_code = if score < 50 then 5 else 0
    invariants:
    - score < 50 implies exit_code != 0
falsification_tests:
- id: FALSIFY-CLI-001
  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,
tokenization, model formats, CLI safety — and this README itself.

## Migration from old crates

| Old | New | Status |
|-----|-----|--------|
| `trueno = "0.18"` | `aprender-compute = "0.33"` | Shim available |
| `entrenar = "0.7"` | `aprender-train = "0.33"` | Shim available |
| `realizar = "0.8"` | `aprender-serve = "0.33"` | Shim available |
| `batuta = "0.7"` | `aprender-orchestrate = "0.33"` | Shim available |

Old repositories are archived. All development happens here.

## Contributing

```text
git clone https://github.com/paiml/aprender
cd aprender
cargo test --workspace --lib
cargo check --workspace
apr --help
```

The README's own gate — a merge-path CI step runs exactly this line, which is what lets this fence stay `bash`:

```bash
setsid --wait bash scripts/check_readme_claims.sh    # README contract gate (as CI runs it)
```

## License

MIT