millwright 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- millwright-0.2.0/.github/workflows/release-crate.yml +50 -0
- {millwright-0.1.0 → millwright-0.2.0}/.github/workflows/release-python.yml +5 -5
- millwright-0.2.0/CHANGELOG.md +118 -0
- millwright-0.2.0/CONTRIBUTING.md +55 -0
- {millwright-0.1.0 → millwright-0.2.0}/Cargo.lock +2 -1
- {millwright-0.1.0 → millwright-0.2.0}/Cargo.toml +8 -5
- millwright-0.2.0/GUIDE.md +41 -0
- {millwright-0.1.0 → millwright-0.2.0}/PKG-INFO +19 -9
- {millwright-0.1.0 → millwright-0.2.0}/README.md +18 -8
- millwright-0.2.0/RELEASING.md +94 -0
- millwright-0.2.0/docs/data.html +114 -0
- millwright-0.2.0/docs/deploy.html +134 -0
- millwright-0.2.0/docs/index.html +156 -0
- millwright-0.2.0/docs/insight.html +102 -0
- millwright-0.2.0/docs/pipelines.html +187 -0
- millwright-0.2.0/docs/python.html +106 -0
- millwright-0.2.0/docs/site.css +152 -0
- millwright-0.2.0/guide.html +9 -0
- {millwright-0.1.0 → millwright-0.2.0}/index.html +30 -26
- {millwright-0.1.0 → millwright-0.2.0}/pyproject.toml +1 -1
- millwright-0.2.0/scripts/release.sh +54 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/automl.rs +136 -14
- millwright-0.2.0/src/backends/smartcore.rs +624 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/ensemble.rs +231 -9
- {millwright-0.1.0 → millwright-0.2.0}/src/lib.rs +2 -2
- {millwright-0.1.0 → millwright-0.2.0}/src/onnx.rs +56 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/profile.rs +232 -15
- millwright-0.2.0/src/python.rs +1010 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/selection/cv.rs +15 -10
- {millwright-0.1.0 → millwright-0.2.0}/src/selection/mod.rs +39 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/selection/scoring.rs +35 -1
- {millwright-0.1.0 → millwright-0.2.0}/src/table.rs +24 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/traits.rs +4 -4
- millwright-0.2.0/tests/ensemble_zoo.rs +42 -0
- millwright-0.1.0/CHANGELOG.md +0 -63
- millwright-0.1.0/GUIDE.md +0 -621
- millwright-0.1.0/RELEASING.md +0 -61
- millwright-0.1.0/guide.html +0 -574
- millwright-0.1.0/src/backends/smartcore.rs +0 -290
- millwright-0.1.0/src/python.rs +0 -179
- {millwright-0.1.0 → millwright-0.2.0}/.github/workflows/ci.yml +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/.gitignore +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/CNAME +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/LICENSE +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/benches/throughput.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/automl.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/backends.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/explore.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/insight.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/operations.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/portability.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/specialized.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/spine.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/trust.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/examples/workflow.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/anomaly.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/backends/chronos.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/backends/incremental.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/backends/linfa.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/backends/mod.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/balance.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/calibration.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/diagnostics.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/error.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/evaluate.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/explain.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/frame.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/logistic.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/monitor.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/pipeline.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/registry.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/rng.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/selection/search.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/serve.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/transform.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/src/viz.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/tests/golden.rs +0 -0
- {millwright-0.1.0 → millwright-0.2.0}/tests/real_data.rs +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: release-crate
|
|
2
|
+
|
|
3
|
+
# Publish the Rust crate to crates.io when a version tag is pushed
|
|
4
|
+
# (`git tag v0.1.1 && git push --tags`), using crates.io Trusted Publishing
|
|
5
|
+
# (OIDC) — no stored token. A manual run (workflow_dispatch) does a `--dry-run`.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
name: publish to crates.io
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
id-token: write # required for crates.io Trusted Publishing (OIDC)
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v5
|
|
23
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
24
|
+
- uses: Swatinem/rust-cache@v2
|
|
25
|
+
|
|
26
|
+
- name: check tag matches Cargo.toml version
|
|
27
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
28
|
+
run: |
|
|
29
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
30
|
+
version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')"
|
|
31
|
+
echo "tag=$tag Cargo.toml=$version"
|
|
32
|
+
if [ "$tag" != "$version" ]; then
|
|
33
|
+
echo "::error::tag v$tag does not match Cargo.toml version $version"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
- name: authenticate to crates.io (OIDC)
|
|
38
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
39
|
+
id: auth
|
|
40
|
+
uses: rust-lang/crates-io-auth-action@v1
|
|
41
|
+
|
|
42
|
+
- name: publish
|
|
43
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
44
|
+
env:
|
|
45
|
+
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
|
|
46
|
+
run: cargo publish --locked
|
|
47
|
+
|
|
48
|
+
- name: dry-run (manual runs only)
|
|
49
|
+
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
50
|
+
run: cargo publish --locked --dry-run
|
|
@@ -99,6 +99,8 @@ jobs:
|
|
|
99
99
|
needs: [linux, windows, macos, sdist]
|
|
100
100
|
# Only publish on a version tag; a manual run just builds the wheels above.
|
|
101
101
|
if: startsWith(github.ref, 'refs/tags/')
|
|
102
|
+
permissions:
|
|
103
|
+
id-token: write # PyPI Trusted Publishing (OIDC) — no stored token
|
|
102
104
|
steps:
|
|
103
105
|
- uses: actions/download-artifact@v4
|
|
104
106
|
with:
|
|
@@ -106,9 +108,7 @@ jobs:
|
|
|
106
108
|
merge-multiple: true
|
|
107
109
|
path: dist
|
|
108
110
|
- name: publish
|
|
109
|
-
uses:
|
|
110
|
-
env:
|
|
111
|
-
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
111
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
112
112
|
with:
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
packages-dir: dist
|
|
114
|
+
skip-existing: true
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Millwright are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/), and the project aims at
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-08-23
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **rayon parallelism.** Cross-validation folds and search candidates now
|
|
13
|
+
evaluate in parallel: `cross_val_score` is fold-parallel, `Bagging` fits its
|
|
14
|
+
base estimators in parallel, and `AutoML::parallel()` adds candidate-level
|
|
15
|
+
parallelism. The core contract traits gained `Send + Sync` bounds (every
|
|
16
|
+
concrete model already satisfied them). Results stay seed-reproducible.
|
|
17
|
+
- **`Boosting`** — SAMME adaptive boosting over any weak learner (an
|
|
18
|
+
`alpha`-weighted vote of models each reweighted toward the last round's
|
|
19
|
+
mistakes), joining `Voting`/`Bagging`/`Stacking`.
|
|
20
|
+
- **Three more models over the smartcore backend:** `Knn` (k-nearest-neighbours),
|
|
21
|
+
`Svc` (support vector classifier, linear or RBF, one-vs-one for multiclass),
|
|
22
|
+
and `NaiveBayes` (Gaussian). All implement the same `Estimator`/`Predictor`
|
|
23
|
+
contract, so they drop into pipelines, ensembles, and search unchanged — and
|
|
24
|
+
they are exposed to Python too (`mw.Knn`, `mw.Svc` / `mw.Svc.rbf()`,
|
|
25
|
+
`mw.NaiveBayes`, plus `pipe.knn()` / `pipe.svc()` / `pipe.naive_bayes()`).
|
|
26
|
+
- **Python: a scikit-learn-shaped object API.** `mw.Frame` with `from_pandas` /
|
|
27
|
+
`from_numpy` / `from_rows` ingest; composable transformer/estimator objects
|
|
28
|
+
(`StandardScaler`, `MinMaxScaler`, `SimpleImputer`, `OneHotEncoder`,
|
|
29
|
+
`RandomForest`, `LinearRegression`) added via `pipe.step(name, obj)` /
|
|
30
|
+
`pipe.estimator(name, obj)`; `fit`/`predict`/`evaluate` accept a `Frame`.
|
|
31
|
+
- **Python: `pipeline.explain(...)`** returns SHAP feature importance
|
|
32
|
+
(`Explainer.kernel()` configurable), and **`pipeline.export_onnx(path)`**
|
|
33
|
+
writes the fitted pipeline to a single ONNX file. The `python` wheel now
|
|
34
|
+
bundles the `model-selection`, `explain`, and `onnx` engines.
|
|
35
|
+
- **Python: `GridSearch` / `KFold` / `StratifiedKFold`** over a pipeline, with
|
|
36
|
+
a `SearchResult` (`best_score`, `best_params()`, `predict()`).
|
|
37
|
+
- **`InferenceModel` is now an `Estimator` + `Predictor`**, so a pre-trained
|
|
38
|
+
ONNX model (from scikit-learn, PyTorch, …) can be dropped into a `Pipeline`
|
|
39
|
+
as a frozen estimator behind Millwright's preprocessing — in Rust and, via
|
|
40
|
+
`mw.OnnxModel(path)`, from Python.
|
|
41
|
+
- **Python: `mw.Table` + `mw.Profile`** — dtype-aware CSV/Parquet ingest and
|
|
42
|
+
automated EDA (`Profile.of(table_or_frame).to_html(path)`). The wheel now
|
|
43
|
+
bundles the `eda` (polars) engine, so it is larger than the pure-model build.
|
|
44
|
+
- **`Table::from_frame`** (Rust): build a numeric `Table` from a `Frame`, so the
|
|
45
|
+
numeric world can round-trip back into the typed one (e.g. to profile it).
|
|
46
|
+
- **Richer EDA in `Profile`** — excess `kurtosis` and z-score outlier counts per
|
|
47
|
+
numeric column; a **Spearman** rank-correlation matrix beside the Pearson one;
|
|
48
|
+
a **co-missing** map (columns whose null patterns correlate); high-cardinality
|
|
49
|
+
categoricals now suggest `TargetEncoder`; and `suggest_pipeline` adds a
|
|
50
|
+
train-time **SMOTE** balancer on class imbalance (with `preprocessing`).
|
|
51
|
+
- **AutoML is seeded by EDA.** With the `eda` engine on, the search fixes its
|
|
52
|
+
preprocessing to `Profile::suggest_pipeline()` and varies only the model,
|
|
53
|
+
pruning the space (it falls back to a scaler sweep without `eda`).
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- **Cross-validated F1 no longer returns `NaN`.** A fold whose predictions
|
|
57
|
+
contain no true positives left smartcore's F1 evaluating `0/0`; a single NaN
|
|
58
|
+
fold poisoned the CV mean and a search's `best_score`. An undefined F1 is now
|
|
59
|
+
0.0 (scikit-learn's `zero_division=0` convention), so `GridSearch(...).scoring(F1)`
|
|
60
|
+
yields finite, comparable scores.
|
|
61
|
+
|
|
62
|
+
## [0.1.1]
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- **`LogisticRegression`** — a native, core binary classifier with genuine
|
|
67
|
+
`predict_proba`: the framework's first real `ProbaPredictor`.
|
|
68
|
+
- **`calibration` feature** — `PlattScaling`, `IsotonicRegression`,
|
|
69
|
+
`reliability_curve`, and `CalibratedClassifier`, which wraps any
|
|
70
|
+
`ProbaPredictor` and returns calibrated probabilities.
|
|
71
|
+
- **`anomaly` feature** — `Mahalanobis` and `KnnScore`, unified behind an
|
|
72
|
+
`OutlierDetector` trait.
|
|
73
|
+
- **`eda` feature** — a polars-backed, dtype-aware `Table` (CSV/Parquet ingest
|
|
74
|
+
that lowers to the numeric `Frame`) and a typed `Profile` with an HTML report,
|
|
75
|
+
actionable alerts, and `suggest_pipeline()`.
|
|
76
|
+
- **Transformers** — `Winsorize`, `PowerTransform` (Yeo-Johnson),
|
|
77
|
+
`ColumnTransformer`, and the supervised `TargetEncoder`.
|
|
78
|
+
- **Convenience** — `Frame::from_csv` (dependency-free numeric loader),
|
|
79
|
+
`Table::head`.
|
|
80
|
+
- **Python** — `min_max_scaler`, `simple_imputer`, `one_hot`,
|
|
81
|
+
`linear_regression`, and `evaluate()`.
|
|
82
|
+
- **Examples** — `explore` (ingest → profile → pipeline) and `trust`
|
|
83
|
+
(calibration → reliability → anomaly detection).
|
|
84
|
+
- **Benchmarks** — `benches/throughput.rs` (criterion): the boundary conversion
|
|
85
|
+
and core fit/predict, backing the "Rust speed" claim.
|
|
86
|
+
- **One-hot ingest** — `Table::to_frame_with` / `into_dataset_with` and a
|
|
87
|
+
`CategoryEncoding` enum: lower nominal categories to 0/1 indicator columns
|
|
88
|
+
instead of ordinal codes.
|
|
89
|
+
- **Schema-aware preprocessing** — `Frame` carries a per-column `Dtype`; `Table`
|
|
90
|
+
marks categoricals as it lowers; scalers / `Winsorize` / `PowerTransform` pass
|
|
91
|
+
categorical columns through untouched, and `OneHotEncoder` encodes by dtype
|
|
92
|
+
rather than a value heuristic when the schema is known.
|
|
93
|
+
- **Real-data validation** — an end-to-end integration test on Quinlan's
|
|
94
|
+
PlayTennis (`tests/real_data.rs`): CSV → profile → suggested pipeline → fit.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- Exact-version pins on every engine crate; `Cargo.lock` committed; a
|
|
99
|
+
feature-matrix CI (fmt, clippy `-D warnings`, docs, matrix, OS, examples,
|
|
100
|
+
benches, publish dry-run, wheel).
|
|
101
|
+
- `selection.rs` split into `selection/{scoring,cv,search}`.
|
|
102
|
+
- MSRV is **1.95** (dep-dictated — `sysinfo` via tract, and polars); enforced by
|
|
103
|
+
cargo via `rust-version` rather than a dedicated CI job (which would break on
|
|
104
|
+
every transitive bump). The default install needs 1.85.
|
|
105
|
+
- De-staled the crate and module docs (no more "Phase 0 · the spine").
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- Golden tests and the crate doctest build under every feature subset (they were
|
|
110
|
+
unconditionally referencing backend-gated types).
|
|
111
|
+
- Float sorts use `f64::total_cmp`, closing a NaN-driven panic class.
|
|
112
|
+
|
|
113
|
+
## [0.1.0]
|
|
114
|
+
|
|
115
|
+
- Phases 0–8: the `Frame`/trait spine and smartcore backend, preprocessing and
|
|
116
|
+
model selection, a second backend (linfa) and HPO, evaluation/diagnostics/
|
|
117
|
+
explainability, ONNX export and inference, serving + drift monitoring + a model
|
|
118
|
+
registry, time-series and out-of-core estimators, AutoML, and 1.0 hardening.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Contributing to Millwright
|
|
2
|
+
|
|
3
|
+
Thanks for your interest! Millwright assembles proven Rust crates into one
|
|
4
|
+
composable ML lifecycle. Bug reports, small fixes, and focused features are all
|
|
5
|
+
welcome.
|
|
6
|
+
|
|
7
|
+
## Getting set up
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/mi7plus/millwright
|
|
11
|
+
cd millwright
|
|
12
|
+
cargo test # default features
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Rust **1.95+** is required (the floor is dictated by transitive engine deps).
|
|
16
|
+
|
|
17
|
+
## Before you open a PR
|
|
18
|
+
|
|
19
|
+
The CI is a feature matrix, so run the same checks locally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cargo fmt --all --check
|
|
23
|
+
cargo clippy --features full --all-targets -- -D warnings
|
|
24
|
+
cargo test --features full
|
|
25
|
+
cargo test --no-default-features # the bare-core build must pass too
|
|
26
|
+
cargo doc --no-deps --features full # with RUSTDOCFLAGS="-D warnings"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **Every capability is a cargo feature.** New functionality behind a young
|
|
30
|
+
single-author crate goes behind its own feature; the core stays lean. See the
|
|
31
|
+
feature list in `Cargo.toml`.
|
|
32
|
+
- **Engines are pinned to exact versions** (`=x.y.z`) and `Cargo.lock` is
|
|
33
|
+
committed — bump them deliberately, one line, one commit.
|
|
34
|
+
- **Add tests.** Prefer a `#[test]` next to the code; lock numeric behaviour in
|
|
35
|
+
`tests/golden.rs` when it matters. Feature-gate tests that need a backend.
|
|
36
|
+
- **Run the examples** you touch: `cargo run --features full --example <name>`.
|
|
37
|
+
|
|
38
|
+
## Try it out
|
|
39
|
+
|
|
40
|
+
- Examples: [`examples/`](examples) — one runnable program per feature group.
|
|
41
|
+
- Benchmarks: `cargo bench --features smartcore-backend`.
|
|
42
|
+
- Docs/tutorial: <https://millwright-rs.dev/docs/>.
|
|
43
|
+
|
|
44
|
+
## Releasing
|
|
45
|
+
|
|
46
|
+
Maintainers: bump `Cargo.toml` + `pyproject.toml`, roll `CHANGELOG.md`, and tag
|
|
47
|
+
`vX.Y.Z` — the workflows publish to crates.io and PyPI via OIDC. The helper
|
|
48
|
+
`scripts/release.sh X.Y.Z` does the mechanical steps. See
|
|
49
|
+
[`RELEASING.md`](RELEASING.md).
|
|
50
|
+
|
|
51
|
+
## Scope & conduct
|
|
52
|
+
|
|
53
|
+
Millwright is a thin facade over proven engines — it orchestrates, it doesn't
|
|
54
|
+
reimplement numerics. Please keep PRs focused, and be kind and constructive in
|
|
55
|
+
issues and reviews.
|
|
@@ -1935,7 +1935,7 @@ dependencies = [
|
|
|
1935
1935
|
|
|
1936
1936
|
[[package]]
|
|
1937
1937
|
name = "millwright"
|
|
1938
|
-
version = "0.
|
|
1938
|
+
version = "0.2.0"
|
|
1939
1939
|
dependencies = [
|
|
1940
1940
|
"axum",
|
|
1941
1941
|
"chronos-ts",
|
|
@@ -1957,6 +1957,7 @@ dependencies = [
|
|
|
1957
1957
|
"plotters-statistical",
|
|
1958
1958
|
"polars",
|
|
1959
1959
|
"pyo3",
|
|
1960
|
+
"rayon",
|
|
1960
1961
|
"regression-diagnostics",
|
|
1961
1962
|
"serde",
|
|
1962
1963
|
"serde_json",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "millwright"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
rust-version = "1.95"
|
|
6
6
|
description = "A unified ML framework for Rust — proven Rust crates, assembled into one machine."
|
|
@@ -56,6 +56,7 @@ polars = { version = "=0.55.2", default-features = false, features = ["csv", "pa
|
|
|
56
56
|
# --- general infrastructure: caret ranges, reproduced via Cargo.lock ---
|
|
57
57
|
pyo3 = { version = "0.24", features = ["extension-module", "abi3-py39"], optional = true }
|
|
58
58
|
ndarray = { version = "0.16", optional = true }
|
|
59
|
+
rayon = { version = "1", optional = true }
|
|
59
60
|
plotters = { version = "0.3.7", default-features = false, features = ["svg_backend", "all_series"], optional = true }
|
|
60
61
|
tract-onnx = { version = "0.23", optional = true }
|
|
61
62
|
axum = { version = "0.8", optional = true }
|
|
@@ -90,11 +91,13 @@ smartcore-backend = ["dep:smartcore"]
|
|
|
90
91
|
preprocessing = ["dep:imbalance-rs", "dep:ndarray"]
|
|
91
92
|
|
|
92
93
|
# Cross-validation splitters, scoring, and grid/random search over a pipeline.
|
|
93
|
-
|
|
94
|
+
# CV folds and search candidates are evaluated in parallel via rayon.
|
|
95
|
+
model-selection = ["dep:model-selection-rs", "dep:ndarray", "dep:rayon"]
|
|
94
96
|
|
|
95
97
|
# Voting / bagging / stacking meta-estimators, composed over the four traits.
|
|
96
|
-
# Leak-free stacking uses the model-selection CV engine when it is enabled
|
|
97
|
-
|
|
98
|
+
# Leak-free stacking uses the model-selection CV engine when it is enabled;
|
|
99
|
+
# bagging fits its base estimators in parallel via rayon.
|
|
100
|
+
ensemble = ["dep:rayon"]
|
|
98
101
|
|
|
99
102
|
# Ingest & EDA — the front of the lifecycle. A polars-backed, dtype-aware
|
|
100
103
|
# `Table` (CSV/Parquet in, real string/categorical/datetime/null columns) that
|
|
@@ -151,7 +154,7 @@ onnx = [
|
|
|
151
154
|
# Deliberately NOT part of `full`: pyo3's `extension-module` defers libpython
|
|
152
155
|
# symbols, so a plain `cargo test`/`clippy` cannot link a test binary with it.
|
|
153
156
|
# Build & test this feature through maturin instead (see pyproject.toml).
|
|
154
|
-
python = ["dep:pyo3", "smartcore-backend"]
|
|
157
|
+
python = ["dep:pyo3", "smartcore-backend", "model-selection", "explain", "onnx", "eda"]
|
|
155
158
|
|
|
156
159
|
# Phase 5 · OPERATIONS — past where scikit-learn stops.
|
|
157
160
|
# A versioned model registry: content-addressed ONNX artifact + metadata +
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# The Millwright Guide
|
|
2
|
+
|
|
3
|
+
*A unified ML framework for Rust — ten crates, one lifecycle.*
|
|
4
|
+
|
|
5
|
+
The full hands-on tutorial now lives as a browsable, multi-page site:
|
|
6
|
+
|
|
7
|
+
### → **[millwright-rs.dev/docs/](https://millwright-rs.dev/docs/)** (source in [`docs/`](docs/index.html))
|
|
8
|
+
|
|
9
|
+
It walks the whole lifecycle, one topic per page: **Data & EDA** · **Pipelines &
|
|
10
|
+
Models** · **Insight** (evaluate, explain, calibrate, detect) · **Deploy**
|
|
11
|
+
(ONNX, serving, registry, AutoML) · **Python**.
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```toml
|
|
16
|
+
[dependencies]
|
|
17
|
+
millwright = "0.1" # or features = ["full"] for the whole lifecycle
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```rust
|
|
21
|
+
use millwright::prelude::*;
|
|
22
|
+
|
|
23
|
+
// features as rows + a target -> a Dataset
|
|
24
|
+
let train = Dataset::new(x, y)?;
|
|
25
|
+
|
|
26
|
+
// standardize, then a random forest — one composable object
|
|
27
|
+
let mut pipe = Pipeline::new()
|
|
28
|
+
.step("scale", StandardScaler::new())
|
|
29
|
+
.estimator("rf", RandomForest::new());
|
|
30
|
+
|
|
31
|
+
pipe.fit(&train)?;
|
|
32
|
+
let preds = pipe.predict(&test)?;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Where to go
|
|
36
|
+
|
|
37
|
+
- **Tutorial:** <https://millwright-rs.dev/docs/> — the full, hands-on walk-through.
|
|
38
|
+
- **API reference:** [docs.rs/millwright](https://docs.rs/millwright).
|
|
39
|
+
- **Design brief (the *why*):** <https://millwright-rs.dev/>.
|
|
40
|
+
- **Examples:** [`examples/`](examples) — a runnable program for each feature group.
|
|
41
|
+
- **Python:** [`pip install millwright`](https://pypi.org/project/millwright/).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: millwright
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -16,10 +16,19 @@ Project-URL: Repository, https://github.com/mi7plus/millwright
|
|
|
16
16
|
|
|
17
17
|
# millwright
|
|
18
18
|
|
|
19
|
+
[](https://crates.io/crates/millwright)
|
|
20
|
+
[](https://docs.rs/millwright)
|
|
21
|
+
[](https://pypi.org/project/millwright/)
|
|
22
|
+
[](https://github.com/mi7plus/millwright/actions/workflows/ci.yml)
|
|
23
|
+
[](https://crates.io/crates/millwright)
|
|
24
|
+
[](LICENSE)
|
|
25
|
+
|
|
19
26
|
A unified ML framework for Rust — *ten crates, one lifecycle.*
|
|
20
27
|
|
|
21
|
-
- **Tutorial:**
|
|
22
|
-
|
|
28
|
+
- **Tutorial:** the hands-on docs site at <https://millwright-rs.dev/docs/>
|
|
29
|
+
(source in [`docs/`](docs/index.html); a short [`GUIDE.md`](GUIDE.md) has the
|
|
30
|
+
quickstart).
|
|
31
|
+
- **Contributing:** see [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
23
32
|
- **Design brief:** the *why*, at **<https://millwright-rs.dev/>**.
|
|
24
33
|
|
|
25
34
|
> *"Ten crates"* is the ecosystem this project assembles —
|
|
@@ -108,7 +117,12 @@ A unified ML framework for Rust — *ten crates, one lifecycle.*
|
|
|
108
117
|
(a full round-trip); tree-ensemble ONNX-ML artifacts run in external runtimes
|
|
109
118
|
like onnxruntime.
|
|
110
119
|
- **Python bindings** (`src/python.rs`, via [`pyo3`], feature `python`): a
|
|
111
|
-
`Pipeline` class over the same Rust core,
|
|
120
|
+
`Pipeline` class over the same Rust core, shipped on [PyPI](https://pypi.org/project/millwright/)
|
|
121
|
+
as an abi3 wheel.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install millwright
|
|
125
|
+
```
|
|
112
126
|
|
|
113
127
|
```python
|
|
114
128
|
import millwright as mw
|
|
@@ -119,11 +133,7 @@ pipe.fit(rows, labels) # list[list[float]], list[float]
|
|
|
119
133
|
preds = pipe.predict(rows) # runs the Rust engine
|
|
120
134
|
```
|
|
121
135
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
maturin develop --features python
|
|
126
|
-
```
|
|
136
|
+
To build from source (contributors), from a virtualenv: `maturin develop --features python`.
|
|
127
137
|
|
|
128
138
|
[`regression-diagnostics`]: https://crates.io/crates/regression-diagnostics
|
|
129
139
|
[`shap-rs`]: https://crates.io/crates/shap-rs
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
# millwright
|
|
2
2
|
|
|
3
|
+
[](https://crates.io/crates/millwright)
|
|
4
|
+
[](https://docs.rs/millwright)
|
|
5
|
+
[](https://pypi.org/project/millwright/)
|
|
6
|
+
[](https://github.com/mi7plus/millwright/actions/workflows/ci.yml)
|
|
7
|
+
[](https://crates.io/crates/millwright)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
3
10
|
A unified ML framework for Rust — *ten crates, one lifecycle.*
|
|
4
11
|
|
|
5
|
-
- **Tutorial:**
|
|
6
|
-
|
|
12
|
+
- **Tutorial:** the hands-on docs site at <https://millwright-rs.dev/docs/>
|
|
13
|
+
(source in [`docs/`](docs/index.html); a short [`GUIDE.md`](GUIDE.md) has the
|
|
14
|
+
quickstart).
|
|
15
|
+
- **Contributing:** see [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
7
16
|
- **Design brief:** the *why*, at **<https://millwright-rs.dev/>**.
|
|
8
17
|
|
|
9
18
|
> *"Ten crates"* is the ecosystem this project assembles —
|
|
@@ -92,7 +101,12 @@ A unified ML framework for Rust — *ten crates, one lifecycle.*
|
|
|
92
101
|
(a full round-trip); tree-ensemble ONNX-ML artifacts run in external runtimes
|
|
93
102
|
like onnxruntime.
|
|
94
103
|
- **Python bindings** (`src/python.rs`, via [`pyo3`], feature `python`): a
|
|
95
|
-
`Pipeline` class over the same Rust core,
|
|
104
|
+
`Pipeline` class over the same Rust core, shipped on [PyPI](https://pypi.org/project/millwright/)
|
|
105
|
+
as an abi3 wheel.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pip install millwright
|
|
109
|
+
```
|
|
96
110
|
|
|
97
111
|
```python
|
|
98
112
|
import millwright as mw
|
|
@@ -103,11 +117,7 @@ pipe.fit(rows, labels) # list[list[float]], list[float]
|
|
|
103
117
|
preds = pipe.predict(rows) # runs the Rust engine
|
|
104
118
|
```
|
|
105
119
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
maturin develop --features python
|
|
110
|
-
```
|
|
120
|
+
To build from source (contributors), from a virtualenv: `maturin develop --features python`.
|
|
111
121
|
|
|
112
122
|
[`regression-diagnostics`]: https://crates.io/crates/regression-diagnostics
|
|
113
123
|
[`shap-rs`]: https://crates.io/crates/shap-rs
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
Millwright ships as one Rust crate (crates.io) and one Python wheel (PyPI), from
|
|
4
|
+
the same source.
|
|
5
|
+
|
|
6
|
+
## The easy way
|
|
7
|
+
|
|
8
|
+
With `main` green and `CHANGELOG.md`'s `[Unreleased]` section filled in, run:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bash scripts/release.sh 0.1.2
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
It bumps both manifests, rolls the changelog, syncs `Cargo.lock`, verifies the
|
|
15
|
+
package, commits, tags `v0.1.2`, and (after a prompt) pushes — which triggers the
|
|
16
|
+
publish workflows below. The rest of this file is the manual equivalent.
|
|
17
|
+
|
|
18
|
+
## Preflight (manual)
|
|
19
|
+
|
|
20
|
+
1. `main` is green in CI and `CHANGELOG.md`'s `[Unreleased]` section is current.
|
|
21
|
+
2. Bump the version in **both** `Cargo.toml` and `pyproject.toml`, move the
|
|
22
|
+
changelog's `[Unreleased]` entries under the new version, and commit.
|
|
23
|
+
3. Prove the packaged crate builds exactly as crates.io will build it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cargo publish --dry-run --locked
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Rust crate → crates.io
|
|
30
|
+
|
|
31
|
+
Automated by the **`release-crate.yml`** workflow, which publishes on a `v*` tag
|
|
32
|
+
via **Trusted Publishing (OIDC)** — no token is stored anywhere. One-time setup,
|
|
33
|
+
on crates.io (crate → Settings → Trusted Publishing → add GitHub):
|
|
34
|
+
|
|
35
|
+
| field | value |
|
|
36
|
+
| ------------------- | ------------------ |
|
|
37
|
+
| Repository owner | `mi7plus` |
|
|
38
|
+
| Repository name | `millwright` |
|
|
39
|
+
| Workflow filename | `release-crate.yml`|
|
|
40
|
+
| Environment | *(leave blank)* |
|
|
41
|
+
|
|
42
|
+
Then a tag (below) publishes; a manual run (Actions → release-crate → Run
|
|
43
|
+
workflow) does a `--dry-run`. The workflow also fails fast if the tag doesn't
|
|
44
|
+
match `Cargo.toml`'s version. To publish by hand instead: `cargo login <token>`
|
|
45
|
+
then `cargo publish --locked`.
|
|
46
|
+
|
|
47
|
+
docs.rs builds the documentation automatically with the `full` feature set (see
|
|
48
|
+
`[package.metadata.docs.rs]` in `Cargo.toml`); the `python` feature is excluded
|
|
49
|
+
there because it needs a Python interpreter.
|
|
50
|
+
|
|
51
|
+
## Python wheel → PyPI
|
|
52
|
+
|
|
53
|
+
The recommended path is the **`release-python.yml`** workflow, which builds
|
|
54
|
+
Linux (x86_64 + aarch64), macOS (x86_64 + aarch64), and Windows wheels plus an
|
|
55
|
+
sdist, and publishes them together via **Trusted Publishing (OIDC)** — no stored
|
|
56
|
+
token. One-time setup, on PyPI:
|
|
57
|
+
|
|
58
|
+
- **Existing project:** PyPI → Your projects → `millwright` → Manage →
|
|
59
|
+
Publishing → Add a new publisher (GitHub Actions).
|
|
60
|
+
- **Before the first publish:** account → Publishing → add a *pending* publisher.
|
|
61
|
+
|
|
62
|
+
| field | value |
|
|
63
|
+
| ------------------- | ------------------- |
|
|
64
|
+
| Owner | `mi7plus` |
|
|
65
|
+
| Repository name | `millwright` |
|
|
66
|
+
| Workflow name | `release-python.yml`|
|
|
67
|
+
| Environment | *(leave blank)* |
|
|
68
|
+
|
|
69
|
+
*(A pending publisher also needs the PyPI Project Name, `millwright`.)*
|
|
70
|
+
|
|
71
|
+
Then every release is just a tag:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
git tag vX.Y.Z && git push --tags
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The workflow builds all platforms and publishes on the tag. A manual run
|
|
78
|
+
(Actions → release-python → Run workflow) builds the wheels *without* publishing,
|
|
79
|
+
for a dry run.
|
|
80
|
+
|
|
81
|
+
To publish a single platform's wheel by hand instead (only installable on that
|
|
82
|
+
OS), from a Developer PowerShell/Prompt with a Python toolchain:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
maturin publish # needs MATURIN_PYPI_TOKEN set
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Notes
|
|
89
|
+
|
|
90
|
+
- The crate pins its engines to exact versions and commits `Cargo.lock`, so a
|
|
91
|
+
publish is fully reproducible.
|
|
92
|
+
- MSRV is declared in `Cargo.toml` (`rust-version`) and enforced by cargo for
|
|
93
|
+
consumers; it is dictated by transitive engine deps, so expect it to rise over
|
|
94
|
+
time.
|