Skip to main content

jNO logo

Dev Docs Tests Coverage License Citation Docker image available arXiv Paper

Features · Install · Example · Docs · Citation

jNO (jax Numerical Operators) is a JAX-native library for differentiable numerical methods. Classical solvers — finite elements, finite differences, and spectral (RCWA) — and scientific machine learning — PINNs, neural operators, Bayesian inference — are two pillars on one substrate: you write the math (a weak form, a strong-form stencil, a PDE residual, a data loss), and it lowers to a single GPU-ready, end-to-end reverse-mode-differentiable, jit-compiled graph.

Because every solve is differentiable, the things that are usually separate frameworks are the same tool here: an inverse problem, a PDE-constrained optimization, and a neural-network coefficient are one composition away from a forward solve — no glue code, no finite differences, no leaving JAX.

[!NOTE] Research-level repository under active development. The public API is stabilising but may change between minor versions. Parts of the numerical-methods stack are marked experimental below — the scope and known limitations are stated on each docs page.

What you can do with jNO

Pillar 1 — Differentiable numerical methods

Capability Maturity Notes
FEM, nodaljno.fem stable Lagrange P1 / P2 / P3+, 2-D & 3-D; steady (linear + Newton), transient (θ-method), second-order-in-time (wave / elastodynamics), complex, periodic, coupled multifield
FEM, non-nodal — H(div) / H(curl) / C¹ experimental Raviart–Thomas (H(div)) and Nédélec edge (H(curl) — Maxwell, eddy currents) elements; C¹ Hermite / Argyris / Morley (plates, biharmonic)
FDMjno.fdm stable Strong-form collocation from a term list; structured grids + geometric multigrid; unstructured meshes; periodic, coupled, flux BCs; 2-D & 3-D
Spectral / RCWAjno.rcwa stable Vector-Maxwell RCWA, anisotropic media, Jones / polarization readout
Linear & nonlinear solversjno.solve / jno.precond stable Sparse-direct LU, Jacobi-BiCGStab, GMRES, CG, MINRES, Chebyshev, geometric multigrid, optional GPU AMG — matrix-free and differentiable
Generalized eigenproblemsfem.eigs beta K x = λ M x, differentiable, M-orthonormal
Time integration stable θ-method (backward-Euler / Crank–Nicolson), exponential integrators, adaptive step size
Adaptive meshingjno.solve.remesh / relocate, and coord.d(t) - v as a term beta Hessian-metric remeshing (AFEM), r-adaptivity, moving meshes stated in the term list
Differentiable inverse / PDE-constrained stable Recover a scalar, a field k(x), the geometry, or a neural coefficient through any solve — the gradient flows through the whole march
Geometryjno.Shape / jno.Path stable CSG via gmsh-OCC; conforming multi-material regions

Pillar 2 — Scientific machine learning

Capability Maturity Notes
Forward PINNs (residual minimisation) stable Hard or soft BC enforcement
Variational PINNs (weak-form losses) stable Network trial functions against the FEM weak form
Operator learning (DeepONet, FNO, U-Net, PROSE via foundax) stable PDE-residual or data-driven
Adaptive resampling (RAD, RARD, CR3, R3, pinnfluence) stable
Stochastic PDEs & noise nodes (gaussian / uniform / laplace) stable Fokker–Planck, stochastic forcing
Bayesian PINNs (NUTS, HMC, MALA, SGLD, SGHMC, VI) stable model.bayesian(kernel_factory) mirrors .optimizer() — per-parameter, mixed freely
Parameter-efficient fine-tuning (LoRA, DoRA, rsLoRA, PiSSA, VeRA, LoKr, OFT, IA3) stable Chain .lora(...) on any wrapped model
Training explainability (gradient conflict, NTK, Hessian, loss landscape, input sensitivity) stable
Foundation-model integration (foundax MLPs, transformers, DeepONet, FNO, PROSE) stable Wrap any Equinox module via jno.nn(...)
Hybrid data + model parallelism stable jno.core(..., mesh=(batch, model))
W&B logging + Orbax checkpointing stable
IREE / MLIR compiled inference for deployment stable

One tracing language bridges the two. A weak form, a strong-form stencil, a PDE residual for a network, and a supervised loss all lower to the same differentiable, jit-compiled graph — so a classical solve, a PINN, and an inverse problem compose rather than living in separate stacks. 29 worked tutorials span elliptic, parabolic, hyperbolic, coupled, inverse, integral, stochastic, FEM / variational, Bayesian, and operator-learning problems — browse the tutorials index.

Install

pip install jax-numerical-operators

One install — FEM, FDM, the solver stack, PINNs, and the scientific-ML tooling all come in the box, running on CPU out of the box. An NVIDIA GPU is one extra away — pip install "jax-numerical-operators[cuda]" — and the heavy, self-contained backends stay behind extras too: [fem] (adaptive remeshing + the PARDISO/cuDSS sparse-direct backends), [rcwa] (the Fourier-modal EM solver), [amg] (GPU algebraic multigrid), [iree], combinable as [cuda,fem]. The Installation guide has the full table, plus Pixi, Docker, and pinning a specific CUDA build.

PS: I recommend pulling the latest main branch to always be up to date!

Example

Differentiable FEM & FDM — one term list, weak or strong form
import jno

d = jno.Shape.rect(0, 0, 1, 1, size=0.05).domain()
xi, yi, _ = d.variable("interior", split=True)
xb, yb, _ = d.variable("boundary", split=True)

# The same BVP two ways — −Δu = 1 on the unit square, u = 0 on the boundary.

# FEM — the WEAK form is the term list (with a test function v):
u, v = d.fem_symbols()
ui, vi = u.bind(x=xi, y=yi), v.bind(x=xi, y=yi)
u_fem = jno.fem([ui.x * vi.x + ui.y * vi.y - 1.0 * vi,   # ∫∇u·∇v − ∫f·v = 0
                 u(xb, yb) - 0.0]).solve()

# FDM — the STRONG form: the same term list, no test function, collocated at the nodes:
w  = d.unknown()
wi = w.bind(x=xi, y=yi)
u_fdm = jno.fdm([-wi.d2(xi) - wi.d2(yi) - 1.0,           # −Δu = 1
                 w(xb, yb) - 0.0]).solve()

# Both are sparse, matrix-free, GPU-ready, and end-to-end differentiable —
# wrap either in an objective to recover a coefficient, a source, or the geometry.
RCWA — a periodic metasurface, from the same constraint list (click to expand)
import jno
import jax.numpy as jnp

# A periodic metasurface unit cell — a patterned high-index slab between two ambients.
K0 = 2 * jnp.pi                                            # vacuum wavenumber (wavelength λ = 1)
d = jno.Shape.box(0, 0, 0, 0.6, 0.6, 1.0, size=0.12).domain()
d.tag("bottom", lambda x, y, z: z < 0.01);  d.tag("top",   lambda x, y, z: z > 0.99)   # z ambients
d.tag("left",   lambda x, y, z: x < 0.01);  d.tag("right", lambda x, y, z: x > 0.59)   # x-periodic
d.tag("front",  lambda x, y, z: y < 0.01);  d.tag("back",  lambda x, y, z: y > 0.59)   # y-periodic

u, v = d.fem_symbols()
xi, yi, zi, _ = d.variable("interior", split=True)
ui, vi = u.bind(x=xi, y=yi, z=zi), v.bind(x=xi, y=yi, z=zi)
def on(tag):                                              # bind u, v on a named face
    s = d.variable(tag, split=True)
    return u.bind(x=s[0], y=s[1], z=s[2]), v.bind(x=s[0], y=s[1], z=s[2])
(ut, vt), (ub, vb) = on("top"), on("bottom")
ul, ur, uf, ubk = on("left")[0], on("right")[0], on("front")[0], on("back")[0]

slab = jno.fn(lambda x, y, z: jnp.where((0.4 < z) & (z < 0.6), 1.0, 0.0), [xi, yi, zi])
eps  = 1.0 + 10.0 * slab      # a patterned slab (swap in jno.np.parameter(...) for inverse design)

# the SAME scalar-Helmholtz list you'd hand jno.fem — rcwa infers period, layers, ε, and incidence:
sol = jno.rcwa([
    ui.x * vi.x + ui.y * vi.y + ui.z * vi.z - K0**2 * eps * (u * vi),   # ∇u·∇v − k₀²·ε·u·v
    -(1j * K0 * ut) * vt,                   # outgoing radiation (top ambient)
    -(1j * K0 * ub - 2j * K0) * vb,         # incident plane wave + radiation (bottom)
    ul - ur,  uf - ubk,                     # Floquet periodicity (x and y)
], orders=200).solve()

sol.efficiency("T")     # transmitted power fraction (needs the [rcwa] backend)
sol.order(+1, 0)        # a chosen diffraction order
Operator learning — a DeepONet trained on a PDE residual (click to expand)
import jno
import jax
import optax
import foundax

dir = jno.setup("./runs/test")

# Domain: `500 *` batches 500 random-coefficient samples of the same geometry
dom = 500 * jno.Shape.rect(0, 0, 2, 1, size=0.05).domain()
x, y, _ = dom.variable("interior")
xb, yb, _ = dom.variable("boundary")
k = dom.variable("k", jax.random.uniform(jax.random.PRNGKey(0), shape=(500, 1, 1), minval=0.5, maxval=1.5))

# Network + optimizer
fx = foundax.deeponet(n_sensors=1, coord_dim=2, basis_functions=32, hidden_dim=128, activation=jax.numpy.tanh)
net = jno.nn(fx)
net.optimizer(optax.adam(optax.schedules.cosine_decay_schedule(1e-3, 20_000, alpha=1e-5)))

# Hard BC enforcement via an output transform; the PDE residual is the loss
u = net(k, jno.np.concat([x, y], axis=-1)) * x * (2 - x) * y * (1 - y)
pde = k * (u.dd(x) + u.dd(y)) + 1.0

crux = jno.core(constraints=[pde.mse], domain=dom)
crux.solve(epochs=20_000, batchsize=32).plot(f"{dir}/training.png")
jno.save(crux, f"{dir}/model.pkl")

Citation

If you use jNO in academic work, please cite:

@article{armbruster2026jno,
  title   = {jNO: A JAX Library for Neural Operator and Foundation Model Training},
  author  = {Armbruster, Leon and Ramesh, Rathan and Kruse, Georg and Straub, Christopher},
  journal = {arXiv preprint arXiv:2605.10159},
  year    = {2026},
  doi     = {10.48550/arXiv.2605.10159},
  url     = {https://arxiv.org/abs/2605.10159}
}

AI Disclosure

Parts of this codebase — including model ports, tests, and documentation — were developed with the assistance of AI coding tools. All contributions are reviewed and tested to the best of our ability, but mistakes may remain; please open an issue if you spot one.

Download files

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

Source Distribution

jax_numerical_operators-0.3.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

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

jax_numerical_operators-0.3.0-py3-none-any.whl (1.2 MB view details)

Uploaded Python 3

File details

Details for the file jax_numerical_operators-0.3.0.tar.gz.

File metadata

  • Download URL: jax_numerical_operators-0.3.0.tar.gz
  • Upload date:
  • Size: 1.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for jax_numerical_operators-0.3.0.tar.gz
Algorithm Hash digest
SHA256 321842e111334b037145ad83fe36578f15b48bfeb6f8fe97c4dd4bd0de05978d
MD5 dcae71671d98b81ad3ac06898440dd33
BLAKE2b-256 5a5f0cf3392e6ffcd46721264398a37d94c9bdeaf8cb674448909832242b6d06

See more details on using hashes here.

Provenance

The following attestation bundles were made for jax_numerical_operators-0.3.0.tar.gz:

Publisher: publish-pypi.yml on FhG-IISB/jNO

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

File details

Details for the file jax_numerical_operators-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for jax_numerical_operators-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09378e8f6bc36e2dbea15f6e3483e0dce57ae88cd5ae14fc9af6b3ea0075eb0d
MD5 86efbaf3a9e47f6759eacfab8eec5a88
BLAKE2b-256 ca9b638865bc5604f51a70d84bdec21b4857331a746f086712420c97082c0b39

See more details on using hashes here.

Provenance

The following attestation bundles were made for jax_numerical_operators-0.3.0-py3-none-any.whl:

Publisher: publish-pypi.yml on FhG-IISB/jNO

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page