Skip to main content

millwright

A unified ML framework for Rust — ten crates, one lifecycle.

"Ten crates" is the ecosystem this project assembles — plotters-statistical, model-selection-rs, imbalance-rs, regression-diagnostics, hyperopt-rs, shap-rs, driftwatch, onnx-export-rs, incremental-rs, chronos-ts — riding the established smartcore / linfa / polars stack.

Status: Phases 0–8 — done

Phase 0 · the spine

fit · transform · predict · Pipeline end to end over a real backend:

  • Frame / Dataset — the contiguous, row-major f64 boundary type (src/frame.rs).
  • The four traits — object-safe Transformer, Estimator, Predictor, ProbaPredictor, plus a blanket Model (src/traits.rs).
  • The first backend — a smartcore adapter (RandomForest, LinearRegression) converting Frame → DenseMatrix at the edge only (src/backends/smartcore.rs).
  • Pipeline — named steps + a final model, "step__param" addressing; pipelines nest (src/pipeline.rs).

Phase 1 · prep & select — a real, tunable, ensemble-ready workflow

  • Preprocessing (src/transform.rs, core): SimpleImputer, StandardScaler, MinMaxScaler, OneHotEncoder, Winsorize (clip outliers), PowerTransform (Yeo-Johnson), ColumnTransformer (per-subset transforms), and the supervised TargetEncoder.
  • Balancing (src/balance.rs, via imbalance-rs): Smote, RandomOverSampler as train-time Balancers — Pipeline::balance(...), applied only during fit.
  • Model selection (src/selection/, via model-selection-rs): KFold / StratifiedKFold, a Metric enum (accuracy, F1, MAE, MSE, RMSE, R²), and GridSearch / RandomSearch over a whole pipeline, tuned by path. grid! macro included.
  • Ensembles (src/ensemble.rs, core): Voting (hard/soft), Bagging, and leak-free Stacking riding the same CV engine — all Models themselves, so they compose, tune, and nest.

Phase 2 · backends & HPO — two backends, one contract

  • The second backend (src/backends/linfa.rs, via linfa, feature linfa-backend): KMeans, GaussianMixture, Dbscan (as a new Clusterer contract) and Pca (as a Transformer) — each converting Frame → ndarray at the edge, proving the boundary conversion against a whole other engine.
  • Bayesian search (src/selection/, via hyperopt-rs, feature hpo): BayesSearch runs TPE search over a SearchSpace and returns the same SearchResult as grid/random search — one search API, three strategies.

Phase 3 · insight — trust the model, not just run it

  • Evaluation reports (src/evaluate.rs, core): model.evaluate(&test) bundles task-appropriate metrics into a Report (accuracy/precision/recall/F1 or MAE/MSE/RMSE/R²).
  • Regression diagnostics (src/diagnostics.rs, via regression-diagnostics, feature diagnostics): Diagnostics::of(&data) runs OLS and exposes summary(), R², per-column VIF, residuals, and Cook's distance.
  • Explainability (src/explain.rs, via shap-rs, feature explain): model.explain(&Explainer::kernel(), &frame) gives per-row SHAP values and global importance, plus permutation_importance(...).
  • Report figures (src/viz.rs, via plotters-statistical, feature viz): viz::roc_svg(...) and viz::residuals_svg(...) render self-contained SVGs (pure-Rust backend, no system fonts).
  • Probabilities (src/logistic.rs, core): LogisticRegression is a native, probability-capable classifier — the first real ProbaPredictor.
  • Calibration (src/calibration.rs, feature calibration): PlattScaling / IsotonicRegression and reliability_curve, plus CalibratedClassifier, which wraps any ProbaPredictor and returns calibrated probabilities.
  • Anomaly detection (src/anomaly.rs, feature anomaly): Mahalanobis and KnnScore, unified behind an OutlierDetector trait.

Phase 4 · portability & Python — train once; run in Rust, Python, or any ONNX runtime

  • ONNX export (src/onnx.rs, via onnx-export-rs, feature onnx): model.export_onnx(path) for RandomForest (ONNX-ML tree ensemble) and LinearRegression; whole-pipeline export folds affine scalers into the estimator's graph as one .onnx.
  • Inference (via tract, feature onnx): InferenceModel::load(path) loads and runs any ONNX file. tract executes the linear/affine/pipeline graphs (a full round-trip); tree-ensemble ONNX-ML artifacts run in external runtimes like onnxruntime.
  • Python bindings (src/python.rs, via pyo3, feature python): a Pipeline class over the same Rust core, shipped on PyPI as an abi3 wheel.
pip install millwright
import millwright as mw
pipe = mw.Pipeline()
pipe.standard_scaler()
pipe.random_forest(n_trees=100, max_depth=8)
pipe.fit(rows, labels)          # list[list[float]], list[float]
preds = pipe.predict(rows)      # runs the Rust engine

To build from source (contributors), from a virtualenv: maturin develop --features python.

Phase 5 · operations — past where scikit-learn stops

  • Registry (src/registry.rs, feature registry): Registry::local(path) versions a model's ONNX artifact, content-addressed (identical models dedupe), with metadata + reference distribution, movable tags, and rollback.
  • Drift monitor (src/monitor.rs, via driftwatch, feature monitor): DriftMonitor::psi(reference) watches the prediction stream — observe + report give live PSI and a drift verdict.
  • Server (src/serve.rs, via axum, feature serve): Server::from_onnx exposes POST /predict (validated) over the tract runtime; with a monitor attached, every request feeds it and GET /metrics reports drift.
Server::from_onnx(reg.onnx_path("churn", "prod")?)?
    .route("/predict")
    .with_monitor(DriftMonitor::psi(&reference)?)
    .serve("0.0.0.0:8080").await?;

Phase 6 · specialized — the long tail of real workloads

Same contract, different data shapes — each gets its own trait.

  • Time series (src/backends/chronos.rs, via chronos-ts, feature timeseries): AutoArima implements a Forecasterfit(&series) then forecast(steps).
  • Out-of-core (src/backends/incremental.rs, via incremental-rs, feature incremental): IncrementalLinear implements PartialFit + Predictorpartial_fit(&batch) learns one batch at a time.

These two crates pin ndarray 0.15 while the rest of the stack uses 0.16; Cargo links both, and the boundary conversion happens only inside these adapters — the "two ndarray worlds" the design settles, now exercised for real.

Phase 7 · synthesis — auto-sklearn, but the output actually deploys

  • AutoML (src/automl.rs, feature automl): AutoML::classifier() / regressor() searches preprocessing × model × hyperparameters under a Budget (trials or minutes), auto-ensembles the top candidates, and returns a ranked leaderboard plus the best fitted model. No new crate — it orchestrates the model-selection, ensemble, and backend machinery already built. A single-pipeline winner flows straight into export_onnx, so unlike a TPOT object the result deploys.
let result = AutoML::classifier()
    .budget(Budget::trials(40))
    .metric(Metric::F1)
    .cv(StratifiedKFold::new(5))
    .fit(&train)?;
println!("{}", result.leaderboard());
result.export_onnx("model.onnx")?;   // deployable

Phase 8 · harden → 1.0 — a framework you can bet on

Pin, prove, document — owning the one real risk of assembling young, single-author engine crates.

  • Exact-version pins (Cargo.toml): every engine — the ecosystem crates plus the smartcore and linfa families — is pinned to an exact =x.y.z, so a stray cargo update can't move a fragile engine under the stable trait contract. General infrastructure (serde, tokio, axum, …) stays on caret ranges to avoid forcing conflicts downstream.
  • Committed Cargo.lock: the whole ~300-package graph is reproducible; CI builds with --locked.
  • Golden-output tests (tests/golden.rs): lock the numeric behaviour of the engines on fixed inputs — exact for the deterministic paths (OLS, affine transforms, metric formulas), well-separated class labels for the stochastic ones. An engine bump that moves a number shows up as a diff.
  • Feature-matrix CI (.github/workflows/ci.yml): fmt, clippy -D warnings, docs, and the test suite across the feature matrix — from --no-default-features through each feature to full — plus Windows/macOS, the runnable examples, a benchmark compile-check, a cargo publish --dry-run, and a maturin wheel. The MSRV (rust-version = 1.95, dep-dictated) is enforced by cargo for consumers.
  • The tutorial (GUIDE.md + guide.html): the design brief's lifecycle, re-cast as a hands-on guide.

Ingest & EDA — the lifecycle starts where the data does

The front of the lifecycle, behind the eda feature (via polars).

  • Table (src/table.rs): a dtype-aware, polars-backed table — Table::from_csv / from_parquet read real string/categorical/datetime/null columns. It lowers to the numeric world: table.to_frame() and table.into_dataset("target") (categoricals label-encoded, nulls → NaN), so Frame stays the numeric boundary everything else already speaks.
  • Profile (src/profile.rs): Profile::of(&table) returns a typed EDA — overview, per-column numeric/categorical profiles, missingness, Pearson correlations (high-|r| pairs flagged), IQR outliers, and target relationship (class balance or feature-target correlation). It renders a self-contained to_html(path) report, lists alerts() that name the fix, and — the loop scikit-learn can't close — suggest_pipeline() drafts the preprocessing from those findings; you just add the model.
let table = Table::from_csv("customers.csv")?;
let profile = Profile::of_with_target(&table, "churned")?;
profile.to_html("eda.html")?;

let train = table.into_dataset("churned")?;
let mut pipe = profile.suggest_pipeline()      // impute · encode · scale, from the alerts
    .estimator("rf", RandomForest::new());
pipe.fit(&train)?;

Quickstart

use millwright::grid;
use millwright::prelude::*;

let pipe = Pipeline::new()
    .step("impute", SimpleImputer::median())
    .step("scale", StandardScaler::new())
    .balance(Smote::new())                 // train-time only
    .estimator("rf", RandomForest::new());

let search = GridSearch::new(pipe, grid! { "rf__max_depth" => [4, 8, 16] })
    .cv(StratifiedKFold::new(5))
    .scoring(Metric::F1)
    .fit(&train)?;

println!("best F1 = {:.3}", search.best_score());
let preds = search.predict(&test)?;

Run the end-to-end examples:

cargo run --example spine
cargo run --example explore --features "eda smartcore-backend"
cargo run --example trust --features "calibration anomaly"
cargo run --example workflow
cargo run --example backends --features "smartcore-backend linfa-backend hpo"
cargo run --example insight --features "smartcore-backend diagnostics explain viz"
cargo run --example portability --features "smartcore-backend onnx"
cargo run --example operations --features "smartcore-backend onnx registry monitor serve"
cargo run --example specialized --features "timeseries incremental"
cargo run --example automl --features "smartcore-backend automl onnx"

Building on Windows

The default toolchain is MSVC. If a Unix link.exe (e.g. from Git/Laragon) is ahead of MSVC's on PATH, linking fails with an "extra operand" error. Build from a Developer Command Prompt / PowerShell for VS 2022, or run vcvars64.bat first, so the MSVC linker is found before the shadowing one.

Roadmap

Phases 0–8 are done — the full lifecycle plus 1.0 hardening (exact-version pins, a committed lockfile, golden-output tests, and a feature-matrix CI). The design brief lays out the arc; the tutorial (GUIDE.md) is the how.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

millwright-0.1.1.tar.gz (171.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

millwright-0.1.1-cp39-abi3-win_amd64.whl (276.5 kB view details)

Uploaded CPython 3.9+Windows x86-64

millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (424.9 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (415.9 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl (375.0 kB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl (379.9 kB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file millwright-0.1.1.tar.gz.

File metadata

  • Download URL: millwright-0.1.1.tar.gz
  • Upload date:
  • Size: 171.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for millwright-0.1.1.tar.gz
Algorithm Hash digest
SHA256 666abe2c34fa97815fe99f30d8d9ba984cfea89dba1c45264e7647f328eb1045
MD5 359b1127d6b67a258ee5a5a32954ca34
BLAKE2b-256 c626e21f586de7fb5ff884a468a773658478f4f27be212b949fb767adc410271

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1.tar.gz:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file millwright-0.1.1-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: millwright-0.1.1-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 276.5 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for millwright-0.1.1-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 16e2f8b5ee5613c9f8ad5e4d4db627eae3157f990d5410241082f689b57a5925
MD5 777b7c78f41de303db703b417a4c25dd
BLAKE2b-256 f4d5c21cdf76bc4f0b75fb56050a0c7abdf003d678ed4afaced456d52f148d01

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1-cp39-abi3-win_amd64.whl:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d85068a7683fe525b9aa363b618bc6fdf0c73fdefbe7bab44fde829baed2d438
MD5 09dd20855e3562141f765d6c67a1714e
BLAKE2b-256 4dbe8e07d3dca06a8b2a1c72a7861c014fb2526e2aa0d7d80d4c47e7fd3f4740

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 58bb33fd548e97ae739d4195442712160280dab9fb04885e9349a5fc42c83128
MD5 c82684a97ba68b514434c7e794f4ba31
BLAKE2b-256 124045ac987ea08f207c3ef7ad2177c7acf61d94e69542ed330a710b469a33e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a63aa851d8023f8dabdfc17e6c06981b51105a816102015fbc97c6b626df85d0
MD5 5ab8437a1ca72e6356b9a42311542f30
BLAKE2b-256 8d12c1210b6bd098395f19825c08f52d2d8eeda666695f8cce84ce75f9902825

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 dbd30f31367c4d4591edff277a61ddfca885907070706af86ac33afc498c06b1
MD5 825a47965051d8b8b7e0ee2c676e2cdf
BLAKE2b-256 c0d21509c6f1cb3eb2eaf698641cea3d8f372109592f28da7164f7e8c163887f

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: release-python.yml on mi7plus/millwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.1

6 files

0.2.0

6 files

This release

0.1.1 This release

6 files

0.1.0

6 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page