Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

mononet — Constrained Monotonic Neural Networks

PyPI version Python versions License codecov Build Docs arXiv

Multi-backend implementation of the constrained monotonic neural network construction from:

Runje, D., Shankaranarayana, S. M. (2023). Constrained Monotonic Neural Networks. ICML 2023. https://arxiv.org/abs/2205.11775

with the optional activation-switch refinement (mode="switch") from:

Sartor, D. et al. (2025). Advancing Constrained Monotonic Neural Networks. ICML 2025. https://arxiv.org/abs/2505.02537

First-class support for PyTorch, JAX (Flax NNX), and Keras 3.

Install

pip install "mononet[torch]"      # PyTorch
pip install "mononet[jax]"        # JAX + Flax NNX
pip install "mononet[keras]"      # Keras 3
pip install "mononet[all]"        # all three

CPU-only torch: on linux the torch/all extras pull PyTorch's default CUDA wheel. Under uv, use the all-cpu (or torch-cpu) extra for a CUDA-free install. Plain pip cannot force CPU torch via an extra — see the installation docs. The default devcontainer already uses all-cpu.

Quick start

mononet ships layers, not composed models — stack them with your framework's native Sequential (or equivalent). Each backend exposes MonoResidual, MonoInput, and the framework-idiomatic dense layer: MonoLinear for PyTorch and JAX, MonoDense for Keras.

A mixed-feature example: monotone in 3 features (2 non-decreasing, 1 non-increasing) via MonoInput, and unconstrained in 2 non-monotone features, which are embedded through a plain MLP. MonoLinear and MonoResidual default to mode="absolute".

"""Mixed-feature monotone network (PyTorch).

Monotone in 3 features (2 non-decreasing, 1 non-increasing) via ``MonoInput``,
and unconstrained in 2 non-monotone features, which are embedded through a
plain MLP. The embedding absorbs the non-monotonicity, so the composite map is
monotone in ``x_mono`` and free in ``x_free``. Absolute mode is the default.
"""

from __future__ import annotations

import numpy as np
import torch
from torch import nn

from mononet import MonotonicityMask
from mononet.torch import MonoInput, MonoLinear, MonoResidual


class RiskNet(nn.Module):
    """Monotone in ``x_mono`` (directions +1, +1, -1); free in ``x_free``."""

    def __init__(self) -> None:
        super().__init__()
        self.embed = nn.Sequential(
            nn.Linear(2, 16),
            nn.ReLU(),
            nn.Linear(16, 8),
            nn.ReLU(),
        )
        self.mono_in = MonoInput(MonotonicityMask(np.array([1, 1, -1], dtype=np.int8)))
        self.net = nn.Sequential(
            MonoLinear(11, 64, activation="elu"),
            MonoResidual(64, 64, activation="elu"),
            MonoResidual(64, 64, activation="elu"),
            MonoLinear(64, 1),
        )

    def forward(self, x_mono: torch.Tensor, x_free: torch.Tensor) -> torch.Tensor:
        """Combine the sign-flipped monotone features with the free embedding."""
        z = torch.cat([self.mono_in(x_mono), self.embed(x_free)], dim=-1)
        return self.net(z)

For per-feature monotonicity directions, pass a mononet.core.types.MonotonicityMask (a 1-D array of {-1, +1}) to MonoInput. The same layers exist under mononet.jax and mononet.keras; see the per-backend guides.

Benchmark results

Held-out accuracy on the paper's five tabular datasets, comparing the switch and absolute monotone constructions at shallow (plain) and deep (residual) depth. Cells report IQM (interquartile mean; robust) and mean ± std over seeds, with the effective monotone-layer count L and a collapse flag ⚠ (shown only when some seeds degenerated). Metric per dataset: MSE (auto), RMSE (blog), accuracy (heart/compas/loan); ↓ lower / ↑ higher is better. Bold = best per dataset. Full methodology and the per-flavor robustness table are in the benchmark docs.

dataset mode variant layers IQM mean ± std ⚠
auto (MSE ↓) switch plain 2 9.78 9.76 ± 0.18 ·
switch residual 4 9.89 10.11 ± 0.62 2/20
absolute plain 2 10.91 10.90 ± 0.21 ·
absolute residual 4 9.92 9.94 ± 0.33 ·
heart (acc ↑) switch plain 4 0.836 0.711 ± 0.249 4/20
switch residual 14 0.831 0.829 ± 0.012 2/20
absolute plain 3 0.836 0.839 ± 0.012 ·
absolute residual 4 0.821 0.825 ± 0.008 ·
compas (acc ↑) switch plain 2 0.679 0.679 ± 0.002 ·
switch residual 14 0.641 0.632 ± 0.033 4/20
absolute plain 4 0.683 0.683 ± 0.002 ·
absolute residual 10 0.684 0.684 ± 0.002 ·
loan (acc ↑) switch plain 3 0.647 0.647 ± 0.001 ·
switch residual 6 0.647 0.646 ± 0.001 ·
absolute plain 3 0.648 0.648 ± 0.000 ·
absolute residual 14 0.649 0.650 ± 0.001 ·
blog (RMSE ↓) switch plain 2 0.185 0.185 ± 0.002 ·
switch residual 4 0.182 0.182 ± 0.000 1/10
absolute plain 2 0.189 0.189 ± 0.000 ·
absolute residual 4 0.173 0.173 ± 0.001 ·

residual collapses the better of the residual/deep depth bands (by CV); L = 2·blocks + 2 effective monotone layers. Deep absolute residual is nominally best on loan (the largest dataset) above, but a controlled size-ladder study — deep vs shallow residual, tuned independently at each training-set size — finds that edge is within noise and does not grow with scale, so depth is neutral even on loan; elsewhere ≤ 4 layers is best. absolute wins 4 of 5 datasets; the ⚠ instabilities are all shallow switch.

License

Apache License 2.0 — see LICENSE and NOTICE.md. Commercial use is permitted. The technique is described in U.S. Patent 11,551,063 (assignee: AIRT Technologies Ltd.); the Apache-2.0 license grants the patent rights needed to use this code. For academic use, please cite the paper (see NOTICE.md).

Formal proofs

The theory underpinning mononet is mechanized in Lean 4 + mathlib4 (sorry-free) in the companion repo neural-network-proofs — browse the proofs, blueprint, and API docs at https://davorrunje.github.io/neural-network-proofs/.

Documentation

Full docs at https://davorrunje.github.io/mononet/. Source for guides and benchmarks lives in docs/.

Contributing

See CONTRIBUTING.md for the development workflow: devcontainer choice, uv sync, pre-commit, per-backend test commands.

Citation

If you use mononet in academic work, please cite the paper:

@inproceedings{runje2023constrained,
  title         = {Constrained Monotonic Neural Networks},
  author        = {Runje, Davor and Shankaranarayana, Sharath M.},
  booktitle     = {Proceedings of the 40th International Conference on Machine Learning},
  series        = {Proceedings of Machine Learning Research},
  volume        = {202},
  year          = {2023},
  publisher     = {PMLR},
  url           = {https://proceedings.mlr.press/v202/runje23a.html},
  eprint        = {2205.11775},
  archivePrefix = {arXiv}
}

Metadata

Release files for mononet 0.0.0a1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mononet 0.0.0a1
File Size Uploaded
mononet-0.0.0a1.tar.gz 34.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mononet 0.0.0a1
File Interpreter ABI Platform
mononet-0.0.0a1-py3-none-any.whl Python 3 none any Details

Total release size: 76.6 kB

Release files / mononet-0.0.0a1.tar.gz

Download URL mononet-0.0.0a1.tar.gz
Size 34.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7efd2cba1cd7b145e86c83185d8c1d70954e35b2f73dd195496b771736f127cf
BLAKE2b-256 checksum
How to use checksums
4dc897ccd5e9e2f46dc043ac94e7e3d04bf26a0ac266221a017913a2ca4731ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 13, 2026.

Transparency log

Release files / mononet-0.0.0a1-py3-none-any.whl

Download URL mononet-0.0.0a1-py3-none-any.whl
Size 42.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ef024268daf0df2dfc34f0d7951c890f725440b6ffda4e662e6a99979eeeb36
BLAKE2b-256 checksum
How to use checksums
64e2887ca590104c055bb0757513da28c084cc923ba064f618f3b46ef52c57b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.0a1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page