Skip to main content

millwright

crates.io docs.rs PyPI CI downloads license

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.2.0.tar.gz (187.4 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.2.0-cp39-abi3-win_amd64.whl (14.7 MB view details)

Uploaded CPython 3.9+Windows x86-64

millwright-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.7 MB view details)

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

millwright-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (17.2 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

millwright-0.2.0-cp39-abi3-macosx_11_0_arm64.whl (14.2 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

millwright-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl (15.4 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

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

File metadata

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

File hashes

Hashes for millwright-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4acc80190e1fb613f117824847887231d82c798b55860f3b5e28a7398ac1dc6d
MD5 c3da89fa344d32101fe4a1b85b39704e
BLAKE2b-256 94a24e4792f16558dd922883d8f25c0fedf88eaf3d30c875604f437926609dc9

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0.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.2.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: millwright-0.2.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 14.7 MB
  • 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.2.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 09c4128115e5e8ed114a89bbeb428b8e40b9c3e759a1dc8045237ecc489776c1
MD5 aca741a92938632326f0f8b99d9cf5b0
BLAKE2b-256 56873889fc9e75abbb496b17512a2c4905dc6bc37c4a3c1ffc0764b73be19d9f

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0-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.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for millwright-0.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e6c9dce36ee97a805a321f995ed38a963b11ffdbad83f56c2900702553de1e26
MD5 085e32e9b9e6aff2bfc7fa999f44adba
BLAKE2b-256 437e50669a61d29ea1feca0d61a4d9f745f87bbf96e718e5a5923f162655765f

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0-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.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for millwright-0.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 d3b546275d5b07c7a607f4d2b627048cd6bd1b749f59c67e3a74c55a904ea871
MD5 478a27cd2b94bb46adbe773d080aabec
BLAKE2b-256 692c13f258ac5f49d9852cfa26e5afd6ae7fcb176f597f9a375008afcfd6f775

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0-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.2.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for millwright-0.2.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9e98af7d8c2d60e4a80f9a5f92c2a8d39c566574b208298c9bc1fb7df8047bcc
MD5 27810ad0516e277d09036e392163bd5f
BLAKE2b-256 3544a52ac35006e32ad3b4c87ddfbafd6928bb4303e7e1d5becd3cc35b19f1c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0-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.2.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for millwright-0.2.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 93fd7a0941e3c9f0fc2828c3cd16d5de299dc053d91f1bce27b0ac62103eef2b
MD5 d340d9b4b584593c2bb628a67b2c7b7d
BLAKE2b-256 529e194b21207e7073e520deec3baa06584fbb80ab348e6d3efacc57230ac586

See more details on using hashes here.

Provenance

The following attestation bundles were made for millwright-0.2.0-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

This release

0.2.0 This release

6 files

0.1.1

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