Skip to main content

Prophys, a differentiable, JAX-native DSL for spatial probability and risk models

Project description

prophys: probabilistic-physics modelling toolkit

A differentiable, JAX-native DSL for spatial probability and risk models.

python license status

Overview

prophys lets you describe an uncertainty structure (geometry, physics, distributions) as a symbolic Python model, and compiles it into a single differentiable JAX graph with log_prob, sample, expectation, tail-risk metrics, gradient-based calibration, and design optimization.

It is not a distribution library like NumPyro or Distrax. It is the layer above one. The distinguishing capability is that geometry, interpolated fields, physics surrogates, and probability live in the same differentiable graph: a pipeline route vertex, an emission abatement fraction, or a copula correlation can all be trained or optimized with the same jax.grad pass that evaluates the model.

It combines:

  • A symbolic expression core (Expr, Param, Input, RandomVariable) with NumPy-style operator overloading. Models are built with ordinary arithmetic and compile to pure jax.numpy.
  • Differentiable geometry structures in shared coordinate frames: PointList, LineList, Polygon/PolygonList (signed distance, containment, area), Polyhedron (3D halfspace volumes), raster Fields with bilinear interpolation, and evaluation Grids.
  • Transformations: linear/affine/polynomial, smoothable PiecewiseLinear, Decay, Logistic dose-response, and interpolated TableLookup1D/2D characteristic curves.
  • Distributions: 12+ univariate families, circular (VonMises, WrappedGaussian), multivariate Gaussian (Cholesky-parameterized), mixtures, empirical/KDE, and Gaussian copulas with always-valid learnable correlation.
  • Probabilistic inputs anywhere in the graph: a RandomVariable (e.g. wind speed/direction) can feed the physics, not just the output noise. The compiled model marginalizes it by reparameterized Monte Carlo with gradients intact.
  • A training engine (Optax): calibrate one attribute, finetune the whole model jointly across attributes with shared parameters, fit_distribution for standalone/multivariate distribution learning, with trainable/frozen parameter control.
  • Design optimization with penalty constraints, where bounded parameters are constrained structurally (sigmoid/softplus bijectors) rather than by projection.
  • A standardized export format (ModelPackage): quantized PMF, histogram, or JAX-free sampler representations, JSON round-trippable, consumer-agnostic.

Installation

pip install prophys

Ships as a prebuilt binary wheel, including the compiled native core (prophys._core), so no Rust toolchain or compilation step is needed.

Free tier / licensing: model calls touching up to 250 structural objects (points, polygon vertices, segments, raster cells, ...) run without any license. This does not count Monte-Carlo sample counts, observation counts, or training iterations, only declared model structure. Larger models require a signed license (set via the PROPHYS_LICENSE environment variable). Contact RhineQC GmbH to obtain one.

With optional extras:

pip install "prophys[viz]"        # matplotlib plotting helpers (prophys.plot)
pip install "prophys[geo]"        # shapely/pyproj geodata import
pip install "prophys[inference]"  # NumPyro bridge

Requirements: Python ≥ 3.11 (JAX and Optax install automatically as dependencies). Python 3.10 is not supported: jaxlib has not published a cp310 wheel since 0.9.0.

Quick Start

import jax
import jax.numpy as jnp
import optax
import prophys as prp

# Geometry in a shared frame: the pipeline route (optimizable) and a receptor
site = prp.Frame("site", units="m")
route = prp.Polyline(
    prp.Param("route", shape=(4, 2), init=jnp.array([[0.0, 0.0], [300.0, 60.0], [650.0, -30.0], [900.0, 0.0]])),
    site,
)
house = prp.Point(jnp.array([500.0, 250.0]), site)

# Physics: a leak can occur anywhere along the route, so its location is a
# continuous, reparameterized random variable pushed through the route's
# differentiable point_at(t), not a hand-picked set of candidate points
leak_frac = prp.RandomVariable("leak_frac", prp.Uniform(low=0.0, high=1.0))
leak_point = route.point_at(leak_frac)

# Gas dispersion from the leak to the house, then concentration -> a
# probability of health issues (dose-response curve)
distance = prp.norm(house.coords[0] - leak_point)
concentration = prp.Param("emission_rate", init=1.5) * prp.exp(-distance / 150.0)
health_risk = prp.PiecewiseLinear([0.0, 0.05, 0.2, 0.5], [0.0, 0.05, 0.3, 0.85], smooth=0.02)(concentration)

# Probabilistic attribute + standardized model object
incident = prp.UncertainAttribute("health_incident", prp.Bernoulli(prob=prp.clip(health_risk, 1e-4, 1 - 1e-4)))
model = prp.ProbabilityModel(incident)
compiled = model.compile(mode="opt", n_samples=200)

# Probability of a health incident, marginalized over every leak location
# along the route, with a gradient with respect to every vertex of the route
p_incident = compiled.expectation("health_incident")
grad_wrt_route = jax.grad(
    lambda params: compiled.expectation("health_incident", params=params)
)(compiled.default_params())

# Calibrate against observations ...
result = prp.calibrate(compiled, "health_incident", observations, n_steps=300)

# ... or optimize the route itself, directly against the marginalized
# probability, with plain JAX/optax (prp.optimize is only a convenience
# wrapper around the same pattern, not required)
opt = optax.adam(0.05)
raw_params = compiled.default_params()
opt_state = opt.init(raw_params)

for _ in range(200):
    loss, grads = jax.value_and_grad(
        lambda params: compiled.expectation("health_incident", params=params)
    )(raw_params)
    updates, opt_state = opt.update(grads, opt_state)
    raw_params = optax.apply_updates(raw_params, updates)

Uncertainty at the inputs, not just the output

wind_speed = prp.RandomVariable("v", prp.Weibull(scale=6.0, concentration=2.0))
wind_dir   = prp.RandomVariable("phi", prp.VonMises(loc=prp.deg2rad(35.0), kappa=3.0))

# Any expression downstream of these is a random quantity. The compiled
# model marginalizes them via reparameterized Monte Carlo, inside one
# JAX trace, so gradients flow back through the marginalization.

Training the model

# Fit one attribute
prp.calibrate(compiled, "exposure", observations)

# Jointly train the entire model. Shared parameters pool information
# across every attribute's observations
prp.finetune(compiled, {"exposure": obs_e, "complaints": obs_c}, frozen=["emission_rate"])

# Learn a standalone (also multivariate) distribution from data
mvn = prp.MultivariateGaussian(
    mean=prp.Param("mu", shape=(2,), init=jnp.zeros(2)),
    cholesky=prp.Param("L", shape=(2, 2), init=jnp.eye(2)),
)
prp.fit_distribution(mvn, data)

Export

pkg = compiled.export()      # quantized PMF / histogram / JAX-free sampler
pkg.save("model.json")       # versioned, consumer-agnostic interchange format

Downstream consumers implement their own import against the documented ModelPackage shape. prophys carries no consumer-specific dependencies.

Examples

  • examples/gas_dispersion/: a complete community-health-risk model. Gaussian plume physics, wind as random inputs, terrain-corrected effective stack height, dose-response calibration, and constrained abatement optimization. Every primitive family appears once.

Documentation

Full docs are at docs.rhineqc.com/distribution/prophys, covering concepts (symbolic graphs, frames, eval/opt modes, marginalization), the full structure/distribution/transformation catalogs with plots, calibration and design optimization, a step-by-step worked example, and the export format specification.

License

prophys is proprietary software by RhineQC GmbH. It is intended for internal or explicitly licensed distribution.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

prophys-0.1.0-cp311-abi3-win_amd64.whl (610.8 kB view details)

Uploaded CPython 3.11+Windows x86-64

prophys-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (675.7 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

prophys-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (620.3 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

prophys-0.1.0-cp311-abi3-macosx_11_0_arm64.whl (591.1 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

prophys-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl (623.1 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file prophys-0.1.0-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: prophys-0.1.0-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 610.8 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for prophys-0.1.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 ddcd2083c8fb4374b4fd693b92ec3a557fa3309486f23f74903008ea914e24d1
MD5 e1f4cbc4ba9b5d53e813edfa962a5111
BLAKE2b-256 610e582c406bf03508981a2f22b4097b9609ac045b09748fa2005a06b8f904c2

See more details on using hashes here.

File details

Details for the file prophys-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for prophys-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 69d9787f30d17cb134278d8558bb967dcce29db33f47d782ed4eb7fdc1796ebe
MD5 d4f74d5c9a11b63be5b67ecaf73dc6e3
BLAKE2b-256 f98f4f19ea3b517552886ff8fee721e3d9a5456b83881e33e811d87d5cd60a2e

See more details on using hashes here.

File details

Details for the file prophys-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for prophys-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a0895b2d570a3bfddab4989aace09d157f6316a650142ff0a8c91a71f7edbaca
MD5 f0ed923e9d473c545f30ed22acba22f8
BLAKE2b-256 86001b342493c59d3aa6a98532ce1d1a64e9780d56e809b5545390b3727eef2a

See more details on using hashes here.

File details

Details for the file prophys-0.1.0-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for prophys-0.1.0-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a61bd9ed57e2c429fc302c7fd90db1976d58b96673efd9d01316ff401036daf0
MD5 f831e582e210dc540566aa7cdc7a6a1d
BLAKE2b-256 a74cfce79f7fb28699bc55453380affd471d52101fb37355031ad48407bedb7e

See more details on using hashes here.

File details

Details for the file prophys-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for prophys-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 447b562282cf873e591fc830fb9ed1393584680a65e879c6834dad78e6833197
MD5 9e161eb939ed4432c4133a1934dccf30
BLAKE2b-256 cdd81afa09a79312dd8a7393326e6be5d561bf5f075a2a73232893f4a7770943

See more details on using hashes here.

Supported by

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