Metadata-Version: 2.4
Name: glmnetpp
Version: 0.1.0
Summary: Native gaussian elnet (glmnet C++ core) binding for Python
License: GPL-2.0-or-later
License-File: LICENSE
License-File: LICENSES/LICENSE-GLMNET
Keywords: glmnet,ridge,elastic-net,regression,eigen,numerical
Author: Junior Dantas
Author-email: juniordante01@gmail.com
Requires-Python: >=3.9
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Dist: numpy (>=1.20)
Project-URL: Homepage, https://github.com/danttis/glmnetpp
Project-URL: Issues, https://github.com/danttis/glmnetpp/issues
Project-URL: Repository, https://github.com/danttis/glmnetpp
Description-Content-Type: text/markdown

# glmnetpp

Native gaussian elnet (glmnet C++ core) binding for Python.

This package wraps the header-only C++ core of R's `glmnet` (`glmnetpp`, the
>=4.0 reimplementation) behind a small `extern "C"` ABI and a `ctypes` binding.
It fits ridge / elastic-net gaussian models that are **numerically identical to
R `glmnet`** (including the `intercept=FALSE` refit path) **without depending
on the R runtime**.

## Installation

```
pip install glmnetpp
```

> **Heads-up — compiled extension.** The numerical work is done by a native
> C++ extension (`glmnetpp/_core.*`), built from the vendored `glmnetpp` C++
> core + [Eigen](https://eigen.tuxfamily.org) (header-only). When a prebuilt
> **wheel** is available for your platform/Python, `pip install glmnetpp`
> Just Works. When it is not, pip builds from the **sdist**, which requires a
> **C++17 compiler** and the **Eigen headers** on your machine (see
> *Building from source* below). To ship wheels for every platform, build them
> in CI — see the *Publishing wheels* note at the end.

## Why

R's `glmnet(family="gaussian", alpha=0, ...)` is the reference. The old
`glmnet_python` wrapper ships a 2013 Fortran `GLMnet.f` whose `intercept=FALSE`
path diverges from R's current (C++) glmnet by ~0.2–0.3%. The C++ core
(`glmnetpp`) is the *same* code R 4.1-8 runs, so it matches exactly.

## Use

```python
from glmnetpp import fit_gaussian_ridge

intercept, coef, jerr = fit_gaussian_ridge(
    X, y, lam=0.1, alpha=0.0,
    lower_limits=[0.0]*p, upper_limits=[None]*p,
    standardize=True, intercept=True,
)
```

`coef` is on the original (unstandardized) scale, matching
`as.numeric(coef(glmnet(...)))`.

## Building from source

This is a Poetry package. poetry-core owns packaging/metadata/dependencies;
it does **not** compile C extensions itself — instead it runs
[`scripts/build-extension.py`](https://github.com/danttis/glmnetpp/blob/main/scripts/build-extension.py),
which drives setuptools' `build_ext` (Eigen discovery + compiler-conditional
flags) and builds the extension in-place under `src/glmnetpp/`.

Build-only dependency: [Eigen](https://eigen.tuxfamily.org) (header-only).

```
# either (with Poetry installed):
poetry install
# or (plain pip — uses the poetry-core backend, runs the build script):
pip install -e .
```

The build script searches for Eigen in this order and picks the first one that
contains `Eigen/Core`:

1. `third_party_eigen/eigen-<version>/` inside the project (what CI uses —
   drop the header-only Eigen release there and it builds anywhere, no system
   package needed)
2. `$EIGEN_INCLUDE_DIR`
3. `/usr/include/eigen3` (`libeigen3-dev`)
4. `/usr/lib/R/site-library/RcppEigen/include` (`r-cran-rcppeigen`)
5. macOS Homebrew (`/usr/local/include/eigen3`, `/opt/homebrew/include/eigen3`)
6. Windows — vcpkg (`$VCPKG_ROOT/installed/x64-windows/include/eigen3`) or
   Conda (`$CONDA_PREFIX/Library/include/eigen3`)

## Provenance

The vendored-from-R headers (the C++ `glmnetpp` core, copied from CRAN
`glmnet`) and the full list of every part copied from R are documented in
[`cpp/glmnetpp_include/README.md`](https://github.com/danttis/glmnetpp/blob/main/cpp/glmnetpp_include/README.md)
in the source tree.

## Publishing wheels

Prebuilt wheels are produced by the GitHub Actions workflow in
`.github/workflows/python-publish.yml`. It uses
[cibuildwheel](https://cibuildwheel.pypa.io) to build **manylinux** (Linux),
**Windows** and **macOS** (x86_64 + arm64) wheels for CPython 3.9–3.13, plus an
sdist, and publishes them all to PyPI.

Because the C++ extension needs Eigen at build time, the workflow downloads the
header-only Eigen release into `third_party_eigen/` inside the project tree
before building; `scripts/build-extension.py` finds it there relative to the
repo root, so the same path works on every OS and inside cibuildwheel's
manylinux container. (That directory is gitignored — it's a build input, not
source.) Each wheel is smoke-tested (import + `fit_gaussian_ridge`) in CI before
it ships, so end users of these platforms never need a C++ compiler or Eigen.

## License

GPL-2.0-or-later, inherited from the upstream `glmnet` / `glmnetpp` C++ core
(these headers are a derivative of that code). See the
[`LICENSE`](https://github.com/danttis/glmnetpp/blob/main/LICENSE) file.
