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}
}

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mononet-0.0.0a1.tar.gz (34.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mononet-0.0.0a1-py3-none-any.whl (42.2 kB view details)

Uploaded Python 3

File details

Details for the file mononet-0.0.0a1.tar.gz.

File metadata

  • Download URL: mononet-0.0.0a1.tar.gz
  • Upload date:
  • Size: 34.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mononet-0.0.0a1.tar.gz
Algorithm Hash digest
SHA256 7efd2cba1cd7b145e86c83185d8c1d70954e35b2f73dd195496b771736f127cf
MD5 9fd725de0291733be4f2c7624c37dad1
BLAKE2b-256 4dc897ccd5e9e2f46dc043ac94e7e3d04bf26a0ac266221a017913a2ca4731ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for mononet-0.0.0a1.tar.gz:

Publisher: publish.yml on davorrunje/mononet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mononet-0.0.0a1-py3-none-any.whl.

File metadata

  • Download URL: mononet-0.0.0a1-py3-none-any.whl
  • Upload date:
  • Size: 42.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mononet-0.0.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 0ef024268daf0df2dfc34f0d7951c890f725440b6ffda4e662e6a99979eeeb36
MD5 147ce9e76a80674332844ba3ebfd230a
BLAKE2b-256 64e2887ca590104c055bb0757513da28c084cc923ba064f618f3b46ef52c57b3

See more details on using hashes here.

Provenance

The following attestation bundles were made for mononet-0.0.0a1-py3-none-any.whl:

Publisher: publish.yml on davorrunje/mononet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.0.0a1 This release

2 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