millwright
A unified ML framework for Rust — ten crates, one lifecycle.
- Tutorial:
GUIDE.md— the hands-on walk through the whole lifecycle (also as a page:guide.html). - Design brief: the why, at https://millwright-rs.dev/.
"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 establishedsmartcore/linfa/polarsstack.
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-majorf64boundary type (src/frame.rs).- The four traits — object-safe
Transformer,Estimator,Predictor,ProbaPredictor, plus a blanketModel(src/traits.rs). - The first backend — a smartcore adapter (
RandomForest,LinearRegression) convertingFrame → DenseMatrixat 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 supervisedTargetEncoder. - Balancing (
src/balance.rs, viaimbalance-rs):Smote,RandomOverSampleras train-timeBalancers —Pipeline::balance(...), applied only duringfit. - Model selection (
src/selection/, viamodel-selection-rs):KFold/StratifiedKFold, aMetricenum (accuracy, F1, MAE, MSE, RMSE, R²), andGridSearch/RandomSearchover a whole pipeline, tuned by path.grid!macro included. - Ensembles (
src/ensemble.rs, core):Voting(hard/soft),Bagging, and leak-freeStackingriding the same CV engine — allModels themselves, so they compose, tune, and nest.
Phase 2 · backends & HPO — two backends, one contract
- The second backend (
src/backends/linfa.rs, vialinfa, featurelinfa-backend):KMeans,GaussianMixture,Dbscan(as a newClusterercontract) andPca(as aTransformer) — each convertingFrame → ndarrayat the edge, proving the boundary conversion against a whole other engine. - Bayesian search (
src/selection/, viahyperopt-rs, featurehpo):BayesSearchruns TPE search over aSearchSpaceand returns the sameSearchResultas 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 aReport(accuracy/precision/recall/F1 or MAE/MSE/RMSE/R²). - Regression diagnostics (
src/diagnostics.rs, viaregression-diagnostics, featurediagnostics):Diagnostics::of(&data)runs OLS and exposessummary(), R², per-column VIF, residuals, and Cook's distance. - Explainability (
src/explain.rs, viashap-rs, featureexplain):model.explain(&Explainer::kernel(), &frame)gives per-row SHAP values and global importance, pluspermutation_importance(...). - Report figures (
src/viz.rs, viaplotters-statistical, featureviz):viz::roc_svg(...)andviz::residuals_svg(...)render self-contained SVGs (pure-Rust backend, no system fonts). - Probabilities (
src/logistic.rs, core):LogisticRegressionis a native, probability-capable classifier — the first realProbaPredictor. - Calibration (
src/calibration.rs, featurecalibration):PlattScaling/IsotonicRegressionandreliability_curve, plusCalibratedClassifier, which wraps anyProbaPredictorand returns calibrated probabilities. - Anomaly detection (
src/anomaly.rs, featureanomaly):MahalanobisandKnnScore, unified behind anOutlierDetectortrait.
Phase 4 · portability & Python — train once; run in Rust, Python, or any ONNX runtime
- ONNX export (
src/onnx.rs, viaonnx-export-rs, featureonnx):model.export_onnx(path)forRandomForest(ONNX-ML tree ensemble) andLinearRegression; whole-pipeline export folds affine scalers into the estimator's graph as one.onnx. - Inference (via
tract, featureonnx):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, viapyo3, featurepython): aPipelineclass 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, featureregistry):Registry::local(path)versions a model's ONNX artifact, content-addressed (identical models dedupe), with metadata + reference distribution, movable tags, androllback. - Drift monitor (
src/monitor.rs, viadriftwatch, featuremonitor):DriftMonitor::psi(reference)watches the prediction stream —observe+reportgive live PSI and a drift verdict. - Server (
src/serve.rs, viaaxum, featureserve):Server::from_onnxexposesPOST /predict(validated) over the tract runtime; with a monitor attached, every request feeds it andGET /metricsreports 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, viachronos-ts, featuretimeseries):AutoArimaimplements aForecaster—fit(&series)thenforecast(steps). - Out-of-core (
src/backends/incremental.rs, viaincremental-rs, featureincremental):IncrementalLinearimplementsPartialFit+Predictor—partial_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, featureautoml):AutoML::classifier()/regressor()searches preprocessing × model × hyperparameters under aBudget(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 intoexport_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 straycargo updatecan'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-featuresthrough each feature tofull— plus Windows/macOS, the runnable examples, a benchmark compile-check, acargo 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_parquetread real string/categorical/datetime/null columns. It lowers to the numeric world:table.to_frame()andtable.into_dataset("target")(categoricals label-encoded, nulls →NaN), soFramestays 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-containedto_html(path)report, listsalerts()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
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
666abe2c34fa97815fe99f30d8d9ba984cfea89dba1c45264e7647f328eb1045
|
|
| MD5 |
359b1127d6b67a258ee5a5a32954ca34
|
|
| BLAKE2b-256 |
c626e21f586de7fb5ff884a468a773658478f4f27be212b949fb767adc410271
|
Provenance
The following attestation bundles were made for millwright-0.1.1.tar.gz:
Publisher:
release-python.yml on mi7plus/millwright
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1.tar.gz -
Subject digest:
666abe2c34fa97815fe99f30d8d9ba984cfea89dba1c45264e7647f328eb1045 - Sigstore transparency entry: 2567091550
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16e2f8b5ee5613c9f8ad5e4d4db627eae3157f990d5410241082f689b57a5925
|
|
| MD5 |
777b7c78f41de303db703b417a4c25dd
|
|
| BLAKE2b-256 |
f4d5c21cdf76bc4f0b75fb56050a0c7abdf003d678ed4afaced456d52f148d01
|
Provenance
The following attestation bundles were made for millwright-0.1.1-cp39-abi3-win_amd64.whl:
Publisher:
release-python.yml on mi7plus/millwright
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1-cp39-abi3-win_amd64.whl -
Subject digest:
16e2f8b5ee5613c9f8ad5e4d4db627eae3157f990d5410241082f689b57a5925 - Sigstore transparency entry: 2567091808
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 424.9 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d85068a7683fe525b9aa363b618bc6fdf0c73fdefbe7bab44fde829baed2d438
|
|
| MD5 |
09dd20855e3562141f765d6c67a1714e
|
|
| BLAKE2b-256 |
4dbe8e07d3dca06a8b2a1c72a7861c014fb2526e2aa0d7d80d4c47e7fd3f4740
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
d85068a7683fe525b9aa363b618bc6fdf0c73fdefbe7bab44fde829baed2d438 - Sigstore transparency entry: 2567092119
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 415.9 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58bb33fd548e97ae739d4195442712160280dab9fb04885e9349a5fc42c83128
|
|
| MD5 |
c82684a97ba68b514434c7e794f4ba31
|
|
| BLAKE2b-256 |
124045ac987ea08f207c3ef7ad2177c7acf61d94e69542ed330a710b469a33e3
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
58bb33fd548e97ae739d4195442712160280dab9fb04885e9349a5fc42c83128 - Sigstore transparency entry: 2567091965
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 375.0 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a63aa851d8023f8dabdfc17e6c06981b51105a816102015fbc97c6b626df85d0
|
|
| MD5 |
5ab8437a1ca72e6356b9a42311542f30
|
|
| BLAKE2b-256 |
8d12c1210b6bd098395f19825c08f52d2d8eeda666695f8cce84ce75f9902825
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
a63aa851d8023f8dabdfc17e6c06981b51105a816102015fbc97c6b626df85d0 - Sigstore transparency entry: 2567091700
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 379.9 kB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbd30f31367c4d4591edff277a61ddfca885907070706af86ac33afc498c06b1
|
|
| MD5 |
825a47965051d8b8b7e0ee2c676e2cdf
|
|
| BLAKE2b-256 |
c0d21509c6f1cb3eb2eaf698641cea3d8f372109592f28da7164f7e8c163887f
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
millwright-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
dbd30f31367c4d4591edff277a61ddfca885907070706af86ac33afc498c06b1 - Sigstore transparency entry: 2567092328
- Sigstore integration time:
-
Permalink:
mi7plus/millwright@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/mi7plus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@181f17604d38f6687ec1aab9ae5ca17c3f650da2 -
Trigger Event:
push
-
Statement type: