Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

scikit-learn

A zopen community port of scikit-learn for z/OS.

Install

zopen install scikit-learn

What this port builds

Wheels for each supported interpreter, built from the upstream sources with meson. It depends on the numpy and scipy ports at runtime and bundles joblib, threadpoolctl and narwhals, which are pure Python and have no ports of their own.

Notes for this platform

scikit-learn is built without OpenMP, and is therefore single-threaded.

z/OS has no OpenMP runtime. scikit-learn is written for this case: when _OPENMP is undefined its _openmp_helpers.pxd supplies no-op replacements for the OpenMP routines -- omp_get_max_threads() becomes 1 -- and Cython lowers prange to an ordinary serial loop. Results are unchanged; work that would be spread over threads runs on one.

You can confirm what a given install was built with:

>>> from sklearn.utils._openmp_helpers import _openmp_parallelism_enabled
>>> _openmp_parallelism_enabled()
False

Estimators that parallelise through joblib rather than OpenMP -- anything taking n_jobs -- are unaffected and still use multiple processes.

This requires a meson that link-tests OpenMP before reporting it available. The compiler here accepts -fopenmp, defines _OPENMP and ships omp.h while providing no runtime, so an unpatched meson reports OpenMP as found and the build then fails at the bind with omp_get_max_threads unresolved. The meson port carries that check.

Known limitation: LogisticRegression's default solver

LogisticRegression().fit(X, y) returns a model that scores at chance.

Its default solver is lbfgs, which calls scipy.optimize.minimize(method="L-BFGS-B"). On this platform that returns success=True after performing no iterations, handing back the starting point. Nothing raises and nothing warns, so the model looks fitted and is not.

Use any other solver:

LogisticRegression(solver="liblinear").fit(X, y)

liblinear, newton-cg, newton-cholesky, sag and saga are all unaffected -- none of them routes through scipy.optimize. The same applies to any other code that chooses L-BFGS-B, whether explicitly or by default.

This is a scipy problem, not a scikit-learn one, and it is specific to big-endian: an integer whose width does not match what the BLAS expects reads as zero here and reads correctly on little-endian machines, so 1/‖d‖ becomes infinite and the first line-search step is clamped to its 1e10 ceiling. Every other scipy solver tested is exact -- BFGS, CG, TNC, SLSQP, Powell and Nelder-Mead.

The port's own check therefore fits with liblinear, and records on every build whether the default solver has started working.

Upstream version caps

scikit-learn's pyproject.toml caps its build requirements at numpy<2.5.0 and scipy<1.18.0, both below what the zopen wheel index carries. Those caps are overridden deliberately, with --skip-dependency-check on the build call. (--no-isolation on its own is not enough: build still validates the requirements it finds and refuses to call the backend when a cap is violated.)

What still enforces versions is scikit-learn's own meson.build, which checks its declared minimums and errors out if they are not met. Only the upper bounds are overridden, and those are upstream declining to promise something it has not tested rather than a known incompatibility -- scipy 1.18 still ships the cython_blas and cython_special interfaces scikit-learn cimports.

About

zopen port of scikit-learn

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages