Transformations#

A Transform is a composable, callable mapping Expr -> Expr. Bijective transforms additionally implement inverse and log_det_jacobian, so they can double as a distribution bijector via TransformedDistribution.

Linear

a * x + b — bijective, with closed-form inverse.

Affine

A @ x + b for vector-valued x.

Polynomial

sum_i coeffs[i] * x**i.

PiecewiseLinear

smooth=0 gives the exact (subgradient) curve; smooth=tau blends neighbouring segments for a fully continuous gradient at the knots.

Decay

Exponential (exp(-x/scale)) or power-law decay, typically composed after a distance primitive.

Logistic

Dose-response / regulatory-acceptance curves.

TableLookup1D

Interpolated 1D characteristic curve (e.g. a power curve).

TableLookup2D

Bilinearly interpolated 2D lookup (e.g. sound power level over wind speed x direction).

Smoothing behavior of PiecewiseLinear#

Because a hard piecewise-linear kink has a discontinuous gradient at each knot, PiecewiseLinear lets the smoothing temperature trade exactness against gradient continuity:

_images/piecewise_linear_shapes.png

A smooth value that is too large relative to the knot spacing will blend in neighbouring segments’ slopes even at points deep inside one segment — worth checking for if a transformed probability unexpectedly drifts outside its valid range (e.g. below 0 for a probability fed into Bernoulli).

class prophys.transformations.Transform[source]#

Base class of deterministic transformations that map expressions to expressions.

A transform is called like a function, t(x), and returns a new Expr. Transforms compose with then(). Bijective transforms additionally implement inverse and log_det_jacobian, so they can be used to build a TransformedDistribution.

Example:

attenuation = prp.Decay(scale=150.0).then(prp.Linear(a=0.9, b=0.05))
exposure = attenuation(houses.closest_distance(source))
inverse(y)[source]#

Return x such that self(x) == y, as an expression (bijective transforms only).

Parameters:

y (Any)

Return type:

Expr

log_det_jacobian(x)[source]#

Return log|det J(x)| of the forward map at x, as an expression (bijective transforms only).

Parameters:

x (Any)

Return type:

Expr

then(other)[source]#

Return the composition that applies this transform first, then other.

self.then(other)(x) == other(self(x)). The inverse and the log determinant compose accordingly and raise NotImplementedError unless both parts are bijective.

Parameters:

other (Transform)

Return type:

Transform

class prophys.transformations.Linear(a=1.0, b=0.0)[source]#

Elementwise linear map y = a * x + b, bijective for a != 0.

Parameters:
  • a (Any) – Slope, a scalar or broadcastable array. It may be an Expr such as a Param, making it learnable.

  • b (Any) – Intercept, likewise.

inverse(y)[source]#

Return x such that self(x) == y, as an expression (bijective transforms only).

Parameters:

y (Any)

Return type:

Expr

log_det_jacobian(x)[source]#

Return log|det J(x)| of the forward map at x, as an expression (bijective transforms only).

Parameters:

x (Any)

Return type:

Expr

class prophys.transformations.Affine(A, b=0.0)[source]#

Affine map y = A @ x + b of a vector x, bijective for invertible A.

Parameters:
  • A (Any) – (d, d) matrix, possibly an expression.

  • b (Any) – (d,) offset (or scalar), possibly an expression.

inverse(y)[source]#

Return x such that self(x) == y, as an expression (bijective transforms only).

Parameters:

y (Any)

Return type:

Expr

log_det_jacobian(x)[source]#

Return log|det J(x)| of the forward map at x, as an expression (bijective transforms only).

Parameters:

x (Any)

Return type:

Expr

class prophys.transformations.Polynomial(coeffs)[source]#

Elementwise polynomial y = sum_i coeffs[i] * x**i, lowest order first. Not bijective.

Parameters:

coeffs (Sequence[Any]) – Coefficients c0, c1, ..., each may be an expression.

Example:

drag = prp.Polynomial([0.0, 0.0, 0.5 * rho * cd * area])  # 0.5 rho Cd A v^2
class prophys.transformations.PiecewiseLinear(knots, values, smooth=0.0)[source]#

Piecewise linear curve through (knots[i], values[i]).

smooth=0 (default) gives the exact piecewise linear function, clamped flat beyond the end knots, which reproduces a dose response table exactly. smooth=tau > 0 blends the segment lines with soft weights, so the gradient in x is continuous across the knots too. The smooth curve extends the end segments linearly beyond the end knots instead of clamping.

Parameters:
  • knots (Sequence[float]) – Increasing x coordinates of the knots (fixed numbers).

  • values (Sequence[float]) – Curve values at the knots (fixed numbers).

  • smooth (float) – Blend temperature, in the units of x, or 0 for the exact curve.

Example:

damage = prp.PiecewiseLinear(knots=[0.0, 0.5, 2.0], values=[0.0, 0.2, 1.0])  # depth [m] -> damage ratio
class prophys.transformations.Decay(scale=1.0, kind='exp')[source]#

Exponential or power law decay with distance, typically composed after a distance primitive.

kind="exp" gives exp(-x / scale) and kind="power" gives x ** (-scale), so scale is then the exponent of the power law.

Parameters:
  • scale (Any) – Decay length ("exp") or exponent ("power"), may be a Param.

  • kind (str) – "exp" or "power". Any other value raises ValueError when the transform is applied.

class prophys.transformations.Logistic(x0=0.0, k=1.0, L=1.0)[source]#

Logistic dose response curve L / (1 + exp(-k * (x - x0))).

Parameters:
  • x0 (Any) – Midpoint, where the curve reaches L / 2.

  • k (Any) – Steepness (per unit of x).

  • L (Any) – Upper asymptote.

class prophys.transformations.TableLookup1D(grid, values)[source]#

Linearly interpolated 1D lookup table, e.g. a wind turbine power curve.

Equivalent to an exact PiecewiseLinear, clamped flat beyond the table ends.

Parameters:
  • grid (Sequence[float]) – Increasing input values of the table.

  • values (Sequence[float]) – Table values at grid.

class prophys.transformations.TableLookup2D(x_grid, y_grid, values)[source]#

Bilinearly interpolated 2D lookup table, e.g. sound power level over (wind speed, direction).

Call it as table(x, y).

Parameters:
  • x_grid (Sequence[float]) – Increasing grid of the first input, length nx.

  • y_grid (Sequence[float]) – Increasing grid of the second input, length ny.

  • values (Sequence[Sequence[float]]) – Table of shape (ny, nx). Row j holds the values at y_grid[j].

Periodic & recurring phenomena#

For seasonal, diurnal, tidal, or otherwise recurring signals — and for the “return period” framing common in extreme-event risk (e.g. a “100-year flood”).

class prophys.transformations.Phase(period=1.0)[source]#

Wrap a continuous variable into [0, period), e.g. hours since start into hour of day.

Uses a floor modulo, which is continuous except at the wrap points (measure zero), the same character as the library’s other hard boundary primitives (PiecewiseLinear with smooth=0, polygon containment).

Parameters:

period (Any) – Wrap period, in the units of the input.

class prophys.transformations.Periodic(period, amplitude, phase=None, offset=0.0)[source]#

Learnable finite Fourier series for seasonal, diurnal or tidal signals.

y = offset + sum_k amplitude[k] * cos(2*pi*k*x/period - phase[k])

amplitude and phase are sequences with one coefficient per harmonic, and their length is the number of harmonics. All coefficients may be Expr (typically Param), so the shape of a seasonal cycle can be learned from data with fit_distribution, calibrate or finetune like any other parameter. Harmonic k runs from 1 to the number of harmonics.

Parameters:
  • period (Any) – Period of the fundamental, in the units of the input.

  • amplitude (Sequence[Any]) – Amplitude of each harmonic.

  • phase (Sequence[Any] | None) – Phase shift of each harmonic in radians (zeros by default).

  • offset (Any) – Constant term.

Raises:

ValueError – When amplitude and phase differ in length.

Example:

season = prp.Periodic(365.25, amplitude=[prp.Param("a1", init=3.0)],
                      phase=[prp.Param("p1", init=0.0)], offset=12.0)
temperature_mean = season(day_of_year)
n_harmonics()[source]#

Return the number of harmonics in the series.

Return type:

int

class prophys.transformations.ReturnPeriod[source]#

Convert an annual exceedance probability to a return period 1 / p and back.

This is the familiar “flood of 100 years” framing.

return_period = 1 / exceedance_probability

Bijective and defined for p in (0, 1], with p clipped at 1e-12. Use inverse to go from a target return period back to the probability an event or annual model needs to reproduce it.

inverse(y)[source]#

Return the exceedance probability 1 / T for return period(s) y.

Parameters:

y (Any)

Return type:

Expr

log_det_jacobian(x)[source]#

Return log|d(1/p)/dp| = -2 log p at probability x.

Parameters:

x (Any)

Return type:

Expr

class prophys.transformations.RecurrenceRate[source]#

Convert a mean time between events to an event rate 1 / interval and back.

The rate is the natural parameter for a Poisson process modeling recurring hazards (equipment failures, extreme weather events and so on).

rate = 1 / mean_interval. Modulating the rate with a Periodic signal gives an inhomogeneous Poisson process, whose counts over a window of length w follow prp.Poisson(rate * w). There is no log_det_jacobian, so it is not usable as a distribution bijector.

inverse(y)[source]#

Return the mean time between events 1 / rate for rate(s) y.

Parameters:

y (Any)

Return type:

Expr