Metrika icon # Metrika **Stata-class statistics, GPU-accelerated by Apple Silicon.** Native Swift. No Electron. No Python runtime. No compromises. [![Release](https://img.shields.io/badge/release-v1.0.1-4f8cff)](../../releases/latest) [![macOS](https://img.shields.io/badge/macOS-14%2B-black?logo=apple)](#requirements) [![Swift](https://img.shields.io/badge/Swift-6-F05138?logo=swift&logoColor=white)](#building-from-source) [![Apple Silicon](https://img.shields.io/badge/Apple%20Silicon-arm64-333)](#requirements) [![GPU](https://img.shields.io/badge/GPU-Metal%20%2B%20MLX-9b6cff)](#the-gpu-invisibly) [![Tests](https://img.shields.io/badge/tests-116%20passing-34c759)](#numerical-validation) [![Validated](https://img.shields.io/badge/R--validated-1e--10-34c759)](#numerical-validation) [![Notarized](https://img.shields.io/badge/DMG-signed%20%26%20notarized-0a84ff)](../../releases/latest) Cluster-robust regression with factor variables and a 10,000-replicate GPU bootstrap *One session: a cluster-robust factor-variable regression, then a 10,000-replicate pairs bootstrap — **batched on the Apple GPU**, exactly reproducible from `seed(42)` on any backend.*
--- ## Why Metrika - **One line, publication-ready output.** `reg log_rev price i.region, cluster(firm_id)` — the Stata mental model, with factor variables, `if`/`in` qualifiers, robust and cluster-robust inference. - **The GPU is invisible.** A planner dispatches every command to CPU (LAPACK) or GPU (MLX) automatically. Large bootstrap runs execute as batched Metal solves; you never choose a backend. - **Reproducibility is a feature, not an accident.** All randomness flows through a counter-based Philox4x32 generator: `set seed 42` produces *bit-identical* resamples on CPU and GPU, in any chunk order, across any parallelism. - **Numbers you can defend.** Every CPU estimator is validated against R to **1e-10 relative tolerance** — coefficients, standard errors (classical, HC0–HC3, cluster), p-values, marginal effects. Penalized and boosted models cross-validate against glmnet and xgboost. - **Big data on a laptop.** DuckDB columnar engine with bulk C-API extraction: **10 million rows load in 0.2 s**, summarize in ~0.3 s, regress in ~0.2 s. A Metal point-sprite renderer takes over scatter plots past 100k points and shrugs at 2,000,000. ## Screenshots | | | |:---:|:---:| | **Swift Charts with `by()` groups**
| **2,000,000 points — Metal renderer**
| | **Virtualized data browser with expression filter**
| **Built-in manual for all 38 commands**
| ## Install Download **[Metrika.dmg](../../releases/latest)** — signed, notarized, and stapled. Drag into Applications. macOS 14+ on Apple Silicon. ## A five-minute tour ```stata . sysuse sales // bundled 200-firm × 5-year panel . gen log_rev = ln(revenue) . summarize revenue price, detail . reg log_rev price i.region, robust // HC1 SEs, factor expansion . predict yhat . margins, dydx(price) // delta-method standard errors . xtset firm_id . xtreg log_rev price, fe cluster(firm_id) . ivregress 2sls log_rev (price = z1 z2), robust . logit purchase price . margins, dydx(price) . bootstrap, reps(100000) seed(42): reg log_rev price // GPU batched . permute, reps(10000) seed(42): reg log_rev price // exact p-values . bayes, mcmcsize(20000) seed(42): reg log_rev price // Gibbs sampler . lasso log_rev price z1 z2 orders, lambda(0.05) // glmnet-exact . boost log_rev z1 z2 orders, rounds(100) maxdepth(3) // xgboost-exact . scatter log_rev price, by(region) . histogram revenue, bins(20) . save results.dta, replace // native Stata .dta 118 ``` Everything above works identically in the console, in `.zyq` do-files (⌘R in the editor), and headlessly through `metrika-cli`. ## What's inside | Pillar | Contents | |---|---| | **Data** | parquet, csv, json, arrow, native Stata **.dta** (read 117–119, write 118) · DuckDB engine · explicit missing-value semantics with listwise-deletion reporting | | **Estimation** | OLS (QR, never X'X) · logit / probit / poisson · 2SLS · panel fixed effects · summarize / tabulate / correlate | | **Inference** | robust HC0–HC3 · cluster-robust with Stata small-sample factors · **GPU pairs bootstrap** · permutation tests · Bayesian regression (Gibbs) | | **Machine learning** | lasso & elastic net (coordinate descent, glmnet-exact) · gradient-boosted trees (xgboost-exact) | | **Post-estimation** | `predict` (xb, residuals, pr, n) · `margins, dydx()` with delta-method SEs | | **Graphics** | Swift Charts scatter/line/histogram/kdensity · **Metal renderer** for millions of points | | **Extensibility** | `.zyq` script commands with `args` macros · native Swift `ZQCommandPlugin`s with syntax validation and mutation gating | ## Numerical validation Metrika's test suite doesn't check that code runs — it checks that the *numbers are right*: - **116 tests** compare against golden values generated by R (`Tests/Fixtures/generate.R`): OLS coefficients, every SE variant, t/F/χ² p-values into the far tails (p = 4×10⁻²² matches R exactly), GLM likelihoods, marginal effects **and their delta-method SEs** — all at 1e-10 relative tolerance. - Lasso/elastic-net coefficients match **glmnet** (including its subtle gaussian y-standardization convention); the selection pattern — which coefficients are exactly zero — matches exactly. - Boosted-tree predictions match **xgboost** observation-by-observation. - The GPU bootstrap's resample indices are asserted **bit-identical** to the CPU Philox reference; Philox itself is pinned to the Random123 known-answer vectors. - With diffuse priors, the Bayesian posterior reproduces the frequentist answer within Monte-Carlo error — asserted, not assumed. ## Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Metrika (SwiftUI) │ │ Console · Data browser · Do-file editor · Manual │ └──────────────────────────┬──────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────┐ │ MetrikaKit (Swift package) │ │ │ │ ZQParser command grammar → typed AST │ │ ZQPlanner AST → CPU / GPU / hybrid dispatch │ │ ZQEngine sessions, execution, logging, help │ │ ZQData DataFrame façade over DuckDB + .dta │ │ ZQStats LAPACK estimators (Accelerate) │ │ ZQGPU MLX batched solves, Philox RNG │ │ ZQGraphics plot specs → Swift Charts / Metal │ │ ZQPlugins user commands & sandboxing │ └──────────────────────────────────────────────────────────┘ ``` `MetrikaKit` has zero UI dependencies and is fully testable with `swift test`. `ZQGPU` is the only module allowed to touch MLX/Metal; `ZQStats` the only one touching Accelerate — backends stay swappable. ## Building from source ```bash git clone https://github.com/spboucher-ai/metrika && cd metrika ./scripts/install_hooks.sh # Library + CLI + tests cd MetrikaKit swift build && swift test # 116 tests (GPU suites auto-skip) xcodebuild test -scheme MetrikaKit-Package \ -destination 'platform=macOS' -skipPackagePluginValidation # + GPU suites # App cd .. && xcodegen generate xcodebuild -project Metrika.xcodeproj -scheme Metrika \ -skipPackagePluginValidation build # Signed, notarized DMG ./scripts/release.sh ``` > SwiftPM's CLI cannot compile Metal shaders, so `swift test` skips the > GPU suites and CLI builds fall back to CPU automatically; `xcodebuild` > runs give you everything. ## Documentation - **In the app**: the Manual tab, or `help ` in the console. - **Static site**: [`docs/index.html`](docs/index.html) — regenerated from the same registry by `metrika-cli docs`, so it can never drift from the app. ## Extending Metrika Drop a `.zyq` script into `~/Library/Application Support/Metrika/Commands/`: ```stata // logreg.zyq — its filename becomes the command args response predictor gen __log = ln(`response') reg __log `predictor', robust drop __log ``` …or compile a Swift plugin into the app: ```swift public struct ZScorePlugin: ZQCommandPlugin { public static let verb = "zscore" public static let syntax = ZQSyntaxSpec(mutates: true) public func execute(_ ctx: ZQContext) async throws -> ZQResult { … } } ``` Plugins that shadow built-ins are rejected at startup; datasets can only be mutated through a declared, gated channel. ---
**© 2026 Simon-Pierre Boucher. All rights reserved.** [contact@spboucher.ai](mailto:contact@spboucher.ai)