This release is a pre-release and may not be stable for production use.
mlxlearn
Classical machine learning on Apple silicon, accelerated with MLX, with a scikit-learn compatible API.
mlxlearn is not officially associated with scikit-learn or PROBABL, nor with Apple.
mlxlearn has two layers.
Layer 1 — the library. Explicit imports, typed solver configuration and state, strict
errors, eager fit, no mutation of scikit-learn, no data-keyed caches:
from mlxlearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train)
y_pred = clf.predict(X_test)
Layer 2 — the patching shell. A thin adapter that points scikit-learn's own names at Layer 1 estimators, so existing code — including code an LLM generated against the scikit-learn API — runs accelerated without being edited:
from mlxlearn import patch_sklearn
patch_sklearn()
from sklearn.svm import SVC # now resolves to the accelerated class
The numerical library is fully usable and fully tested with the patching mechanism absent.
Status
0.1.0a1 — alpha. The API may change. See CHANGELOG.md.
| Estimator | Status |
|---|---|
mlxlearn.neighbors.NearestNeighbors |
0.1.0 |
mlxlearn.neighbors.KNeighborsClassifier |
0.1.0 |
mlxlearn.neighbors.KNeighborsRegressor |
0.1.0 |
mlxlearn.linear_model.LogisticRegression |
0.1.0 |
mlxlearn.svm.SVC (exact) |
0.1.0 |
LinearRegression, Ridge, PCA, KMeans, DBSCAN, TSNE |
planned, 0.2.x |
SVR, NuSVR, NuSVC |
deferred — will not ship until they implement the true objective |
| tree ensembles | out of scope |
Requirements
- macOS on Apple silicon (arm64)
- Python 3.10 – 3.13
- scikit-learn ≥ 1.7, MLX ≥ 0.29
Python 3.10 is supported against scikit-learn ≤ 1.8; scikit-learn 1.9 requires Python ≥ 3.11.
CI tests only valid pairs — see docs/support_matrix.md.
Install
pip install mlx-learn
0.1.0a1 ships as a pure-Python wheel — no compiler, no build step, no ABI matrix.
Import-order semantics
Patching replaces attributes on scikit-learn modules. Attribute access
(sklearn.svm.SVC) therefore resolves to the mlxlearn class whether import sklearn
happened before or after patch_sklearn().
Symbols captured before patching are local bindings and cannot be rebound:
from sklearn.svm import SVC # binds the stock class into your namespace
patch_sklearn() # cannot reach back and change SVC
SVC() # still the stock class
That is Python name binding, not a bug in mlxlearn. The supported pattern is patch-first.
patch_sklearn() is idempotent, and unpatch_sklearn() fully restores scikit-learn.
When mlxlearn falls back
Layer 1 raises a precise error when it cannot honor a request. Layer 2 falls back to stock scikit-learn instead — but only for capability mismatches (sparse input, an unsupported parameter, an unsupported dtype), never to hide a bug:
| Situation | Direct import | Patched |
|---|---|---|
| Capability mismatch | precise MLXLearnError |
falls back, warns once per class, records diagnostics |
| Problem below the measured crossover | internal CPU path | dispatches to scikit-learn |
| Invalid user input | sklearn-equivalent validation error | scikit-learn's own exception, unmasked |
| Unexpected MLX runtime failure | raises | raises — never silently rerun on scikit-learn |
Set the policy with fallback_policy="warn" | "raise" | "silent". Diagnostics are recorded
in every mode:
import mlxlearn
with mlxlearn.config_context(fallback_policy="raise"):
... # any capability fallback becomes an error
mlxlearn.get_last_backend_event() # what the last fit/predict actually did
mlxlearn.get_backend_diagnostics() # everything recorded this session
Sticky backend. The backend is chosen during fit and recorded as
estimator._execution_backend_ ("mlx", "cpu", or "sklearn"). Every subsequent
predict / predict_proba / transform / kneighbors uses the backend the model was
fitted with. A model fitted on MLX can never wander into scikit-learn inference with
incompatible state. A new fit clears state and may pick a different backend.
Configuration
The public surface is deliberately small:
mlxlearn.set_config(
device="auto", # "auto" | "gpu" | "cpu"
fallback_policy="warn", # "warn" | "raise" | "silent"
output_type="numpy", # 0.1.0: NumPy in, NumPy out
deterministic=True,
random_state=0,
diagnostics=True,
)
Crossover thresholds, block sizes, and solver tuning are private, typed, and overridable
only through MLXLEARN_* environment variables for CI and debugging. They are not part of
the public API.
Performance
"Never slower than scikit-learn" is the design goal. The shipped gate is measurable: on
each benchmarked workload class, patched dispatch shows no statistically significant
regression against stock scikit-learn. Crossover points are measured per algorithm and per
operation, published in docs/benchmarks.md, and wired into the
dispatch thresholds — small problems are handed to scikit-learn on purpose.
What that means in practice for 0.1.0a1, on an M4 Max:
| Neighbor queries | 2.9×–17× from ~250 samples up. This is the reason to use mlxlearn. |
Neighbor fit |
0.17×–0.36×. mlxlearn uploads to the device; scikit-learn stores a reference. The first query repays it several times over. |
SVC |
2.93× on wide data (4 000 × 256, rbf), but slower than LIBSVM on narrow data — 0.47× at 4 000 × 32, worse below. kernel="linear" never uses MLX. Width decides, and patched dispatch routes accordingly. |
LogisticRegression |
Slower than scikit-learn below 1 024 features, by a lot. The crossover is set high so patched dispatch hands those to scikit-learn; see docs/benchmarks.md for why and what would change it. |
Reproduce them:
python -m benchmarks.run --profile smoke
Precision
MLX computes in float32 on the GPU. mlxlearn's parity tests are written to float32
tolerances, and the cases where float32 makes strict scikit-learn equivalence impossible
are enumerated — not waved away — in docs/fp32_policy.md.
Development
pip install -e ".[dev]"
pytest
Contributor rules, the estimator gate checklist, and the compliance checks are in
docs/development.md.
Provenance
mlxlearn succeeds a private research fork of an Intel-maintained scikit-learn accelerator.
That history is not carried here: this repository was bootstrapped from an audited
allowlist, and no source file was copied. The audit, the recorded behavioral baseline of
the ancestor, and the authorship attestation are in phase0/. Full lineage,
disclaimers, and third-party notices are in ACKNOWLEDGMENTS.md and
THIRD_PARTY_NOTICES.md.
License
Apache-2.0. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 mlx_learn-0.1.0a1.tar.gz.
File metadata
- Download URL: mlx_learn-0.1.0a1.tar.gz
- Upload date:
- Size: 93.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d44bcdf49e0829b4f58742836abb3d130be9b412d870ad4f5a03c1d6dfd1c1ea
|
|
| MD5 |
ccce63de500979a4e33db622563179b8
|
|
| BLAKE2b-256 |
8580eac71ab8613bc34904188ef2224a8290434e5532e89f446740ae350fb1d9
|
Provenance
The following attestation bundles were made for mlx_learn-0.1.0a1.tar.gz:
Publisher:
release.yml on tabulai/mlxlearn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mlx_learn-0.1.0a1.tar.gz -
Subject digest:
d44bcdf49e0829b4f58742836abb3d130be9b412d870ad4f5a03c1d6dfd1c1ea - Sigstore transparency entry: 2687386589
- Sigstore integration time:
-
Permalink:
tabulai/mlxlearn@b886a918be144e700c5bd30f74c528807835ed13 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/tabulai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b886a918be144e700c5bd30f74c528807835ed13 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mlx_learn-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: mlx_learn-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 109.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65390db042e57b23457109f959195fd79ea40680915a3b382c1fafc637534adb
|
|
| MD5 |
f5dec771a415eedd1a72aea847f8a7dd
|
|
| BLAKE2b-256 |
119fbce2f22727818033c5d0f158d089e94f40dd555549cd196710185fc4e5fc
|
Provenance
The following attestation bundles were made for mlx_learn-0.1.0a1-py3-none-any.whl:
Publisher:
release.yml on tabulai/mlxlearn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mlx_learn-0.1.0a1-py3-none-any.whl -
Subject digest:
65390db042e57b23457109f959195fd79ea40680915a3b382c1fafc637534adb - Sigstore transparency entry: 2687386631
- Sigstore integration time:
-
Permalink:
tabulai/mlxlearn@b886a918be144e700c5bd30f74c528807835ed13 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/tabulai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b886a918be144e700c5bd30f74c528807835ed13 -
Trigger Event:
push
-
Statement type: