This release is a pre-release and may not be stable for production use.
mononet — Constrained Monotonic Neural Networks
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/allextras pull PyTorch's default CUDA wheel. Under uv, use theall-cpu(ortorch-cpu) extra for a CUDA-free install. Plainpipcannot force CPU torch via an extra — see the installation docs. Thedefaultdevcontainer already usesall-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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7efd2cba1cd7b145e86c83185d8c1d70954e35b2f73dd195496b771736f127cf
|
|
| MD5 |
9fd725de0291733be4f2c7624c37dad1
|
|
| BLAKE2b-256 |
4dc897ccd5e9e2f46dc043ac94e7e3d04bf26a0ac266221a017913a2ca4731ee
|
Provenance
The following attestation bundles were made for mononet-0.0.0a1.tar.gz:
Publisher:
publish.yml on davorrunje/mononet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mononet-0.0.0a1.tar.gz -
Subject digest:
7efd2cba1cd7b145e86c83185d8c1d70954e35b2f73dd195496b771736f127cf - Sigstore transparency entry: 2163662673
- Sigstore integration time:
-
Permalink:
davorrunje/mononet@630bb630f2773c47d54ab50b5a935483103733be -
Branch / Tag:
refs/heads/main - Owner: https://github.com/davorrunje
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@630bb630f2773c47d54ab50b5a935483103733be -
Trigger Event:
workflow_dispatch
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ef024268daf0df2dfc34f0d7951c890f725440b6ffda4e662e6a99979eeeb36
|
|
| MD5 |
147ce9e76a80674332844ba3ebfd230a
|
|
| BLAKE2b-256 |
64e2887ca590104c055bb0757513da28c084cc923ba064f618f3b46ef52c57b3
|
Provenance
The following attestation bundles were made for mononet-0.0.0a1-py3-none-any.whl:
Publisher:
publish.yml on davorrunje/mononet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mononet-0.0.0a1-py3-none-any.whl -
Subject digest:
0ef024268daf0df2dfc34f0d7951c890f725440b6ffda4e662e6a99979eeeb36 - Sigstore transparency entry: 2163662690
- Sigstore integration time:
-
Permalink:
davorrunje/mononet@630bb630f2773c47d54ab50b5a935483103733be -
Branch / Tag:
refs/heads/main - Owner: https://github.com/davorrunje
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@630bb630f2773c47d54ab50b5a935483103733be -
Trigger Event:
workflow_dispatch
-
Statement type: