Differentiable JAX-native magnetic field modeling with Magpylib-compatible APIs
Project description
magpylib_jax
Analytic magnetic fields that you can differentiate, compile, and optimize through.
magpylib_jax is a clean-room, JAX-native reimplementation of
Magpylib. It keeps Magpylib's ergonomic object and
functional APIs and its analytic field models, but replaces the numerical core with a
differentiable, JIT-compilable, vmap-friendly implementation — so the same field computation
you use for analysis can sit inside a jax.grad optimization loop.
Why magpylib_jax?
Magpylib gives you fast closed-form fields but no derivatives. Finite-differencing it through an
optimizer is slow and noisy. magpylib_jax closes that gap:
- End-to-end differentiable —
jax.grad,jacfwd,jacrevthroughgetB/getH/getJ/getMand through geometry, pose, and excitation. Exact gradients, no finite differences. - Compiled & vectorized — the field core runs under
jax.jit/vmap/XLA on CPU/GPU/TPU. - Exact force & torque —
getFTcomputes magnetic force and torque by autodiff (∇(m·B)), so — unlike Magpylib's finite-differencegetFT— the magnet result is independent of a step sizeepsand exact to machine precision. - Magpylib-compatible — same source classes,
Collection,Sensor, path/orientation motion, squeeze/pixel_aggsemantics, and SI units, validated against upstream in CI. - Lean & readable — the numerical core is split into small, well-named modules
(
core/kernels/,fields/) with one obvious way to compute each field.
Differences from Magpylib
| Magpylib 5.x | magpylib_jax | |
|---|---|---|
| Backend | NumPy | JAX (CPU/GPU/TPU, XLA) |
| Gradients | ✗ (finite-diff by hand) | ✓ grad/jacfwd/jacrev, exact |
jit / vmap |
✗ | ✓ field core |
getFT force/torque |
finite differences (step eps) |
autodiff, exact, eps-free |
| Precision | float64 | float64 (x64 enabled on import) |
3-D show() display |
✓ (matplotlib/plotly/pyvista) | ✓ (matplotlib) |
| Source families | all | all 12 (parity-tested) |
magpylib_jax matches magpylib's numerical surface and adds exact gradients and getFT. It ships a
matplotlib show() (below); the extra plotly/pyvista backends and the full interactive style
system are the only display features left aside. See the parity strategy.
Installation
pip install magpylib-jax
For GPU/TPU, install the matching jax/jaxlib build first, then magpylib-jax.
Development install:
pip install -e '.[test,docs]'
pytest
Quickstart
import jax, jax.numpy as jnp
import magpylib_jax as mpj # enables float64 on import
# Object API — identical feel to magpylib
src = mpj.magnet.Cuboid(polarization=(0, 0, 1.0), dimension=(1.0, 1.0, 1.0))
B = src.getB([(2.0, 0.0, 0.0), (0.0, 0.0, 2.0)]) # tesla, shape (2, 3)
# Differentiate the field w.r.t. any parameter
def bz(height):
return mpj.magnet.Cuboid(polarization=(0, 0, 1.0),
dimension=(1.0, 1.0, height)).getB((2.0, 0, 0))[2]
print(jax.grad(bz)(1.0)) # dB_z / d(height)
Inverse design in a few lines
Because the field is differentiable, fitting geometry or excitation to a target is just gradient descent — no finite differences, no wrappers:
import jax, jax.numpy as jnp
import magpylib_jax as mpj
obs = jnp.array([[0.2, 0.1, 0.4], [0.5, 0.0, 0.7]])
target = jnp.array([[2.0e-4, 0.0, 3.0e-4], [1.0e-4, 0.0, 2.0e-4]])
def loss(pol):
pred = mpj.magnet.Cuboid(dimension=(1.0, 0.8, 1.2), polarization=pol).getB(obs)
return jnp.mean((pred - target) ** 2)
grad = jax.jit(jax.grad(loss))
pol = jnp.array([0.05, -0.02, 0.08])
for _ in range(100):
pol = pol - 1e-1 * grad(pol)
The plot on the right above shows the loss for recovering a dipole moment this way.
Force & torque by autodiff — getFT
import magpylib_jax as mpj
from magpylib_jax import getFT
magnet = mpj.magnet.Cuboid(polarization=(0, 0, 1.0), dimension=(1., 1., 1.))
loop = mpj.current.Circle(diameter=2.0, current=1e3, position=(0, 0, 1.0), meshing=50)
F, T = getFT(magnet, loop) # force (N) and torque (N·m)
Force on a magnet is F = ∇(m·B) obtained with jax.jacfwd, and current force is (I dL)×B.
The magnet result carries no finite-difference step, so it is exact and eps-independent —
and getFT is itself differentiable.
Performance
The benchmark above is measured on CPU, with the field wrapped in jax.jit and compilation
paid once (the intended usage inside an optimization loop):
- Forward field (left): once jitted, magpylib_jax's XLA kernel is competitive with — and here several times faster than — Magpylib's NumPy core on large observer batches. On GPU/TPU the same kernel parallelizes further.
- Field + gradient (right): Magpylib has no autodiff, so a gradient of a field-derived quantity
w.r.t. source parameters needs finite differences — 6 extra forward evaluations for a 3-component
polarization, and only approximate. magpylib_jax returns the exact gradient from one reverse
pass (
value_and_grad), roughly an order of magnitude faster here and machine-precise.
Two caveats worth knowing: jax.jit your field function and reuse it (the eager object API
pays per-call dispatch overhead that dominates small problems), and for timing wrap results in
jax.block_until_ready(...) so you measure compute, not async dispatch. This applies to getFT
too — it is jittable and differentiable when the geometry and meshing are static (only the
excitation/pose are traced), so jax.jit(jax.grad(loss)) over a getFT-based loss compiles once.
The benchmark is CPU. The panel titles report the active backend, so re-running
python scripts/make_figures.pyon a GPU/TPU host regenerates the figure with that device's numbers, where the batched, fused kernels parallelize much further.
Visualize with show()
import magpylib_jax as mpj
scene = mpj.Collection(
mpj.magnet.Cuboid(polarization=(0, 0, 1.0), dimension=(1, 1, 1)),
mpj.current.Circle(diameter=2.0, current=100.0, position=(1.5, 0, -1)),
mpj.misc.Dipole(moment=(0, 0, 1.0), position=(1.5, 0, 1)),
mpj.Sensor(position=(0, 0, -1.2), pixel=[[0, 0, 0]]),
)
scene.show() # or mpj.show(obj_a, obj_b, ...)
Magnets render as shaded bodies with a polarization arrow, currents as loops/lines with a
direction arrow, dipoles as moment arrows, and sensors as markers with their pixel grid and local
axes; paths draw as faint trails. Pass an existing ax to compose with your own figure, or
return_fig=True for headless use.
Supported sources
magnet.Cuboid, magnet.Cylinder, magnet.CylinderSegment, magnet.Sphere,
magnet.Tetrahedron, magnet.TriangularMesh, current.Circle, current.Polyline,
current.TriangleSheet, current.TriangleStrip, misc.Dipole, misc.Triangle,
misc.CustomSource — plus Collection and Sensor.
Documentation
- Overview · Quickstart
- Equation models & derivations · Numerics & differentiability
- Examples: object API, functional API, force & torque, visualization, optimization, performance
- Architecture & source map · Testing & validation
- Performance · Parity strategy · API reference
- Refactor & roadmap plan · Changelog
Citing
If magpylib_jax supports your research, please cite this repository and the upstream Magpylib
papers it builds on (see docs/equations.md for the model references).
License
BSD-2-Clause. Built by the UW-Madison plasma group as a differentiable companion to Magpylib.
Project details
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 magpylib_jax-2.1.0.tar.gz.
File metadata
- Download URL: magpylib_jax-2.1.0.tar.gz
- Upload date:
- Size: 557.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d15f8bff21a7a08ceb9a3864c6aef7cc4f866268a0b207c961304b1787845362
|
|
| MD5 |
c1155527d10bce5035d0db64c522becf
|
|
| BLAKE2b-256 |
8dd8c8b67abc894fd8d2cfb51ce1f31f559fa7fe7d6991046cbbe9372a8e1e41
|
Provenance
The following attestation bundles were made for magpylib_jax-2.1.0.tar.gz:
Publisher:
publish-pypi.yml on uwplasma/magpylib_jax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
magpylib_jax-2.1.0.tar.gz -
Subject digest:
d15f8bff21a7a08ceb9a3864c6aef7cc4f866268a0b207c961304b1787845362 - Sigstore transparency entry: 2165296366
- Sigstore integration time:
-
Permalink:
uwplasma/magpylib_jax@c70982858986e6e06b16d81c426439aaaa11e420 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/uwplasma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c70982858986e6e06b16d81c426439aaaa11e420 -
Trigger Event:
release
-
Statement type:
File details
Details for the file magpylib_jax-2.1.0-py3-none-any.whl.
File metadata
- Download URL: magpylib_jax-2.1.0-py3-none-any.whl
- Upload date:
- Size: 97.8 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 |
d61701891fa8aabbd4f7a293748a6734a8251a48f3de6cf7f63f89b9ddd6d5fd
|
|
| MD5 |
c817d2858e0bba5ceba247d8bd5f144b
|
|
| BLAKE2b-256 |
899e19302a13cedfcb219d6319398f373db95f4e80ca4c43398a79a0cdf9f433
|
Provenance
The following attestation bundles were made for magpylib_jax-2.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on uwplasma/magpylib_jax
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
magpylib_jax-2.1.0-py3-none-any.whl -
Subject digest:
d61701891fa8aabbd4f7a293748a6734a8251a48f3de6cf7f63f89b9ddd6d5fd - Sigstore transparency entry: 2165296423
- Sigstore integration time:
-
Permalink:
uwplasma/magpylib_jax@c70982858986e6e06b16d81c426439aaaa11e420 -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/uwplasma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@c70982858986e6e06b16d81c426439aaaa11e420 -
Trigger Event:
release
-
Statement type: