boundlab.polysp.legendre.Relu#

class boundlab.polysp.legendre.Relu[source]#

Bases: LegendreHandler

Legendre relaxation of ReLU (orders 1–6, closed-form coefficients).

On a crossing interval the projection integrals split at the kink; the resulting coefficients are polynomial in \(c/a\), evaluated in closed form per order. quartic_bound is zero — ReLU is piecewise linear, so the spline residual evaluation is exact up to the kink handling — and the optional Adam refinement tightens the per-element coefficients further.

Methods

__init__

approx_lbub

Elementwise range of the residual fn - p over [c - hw, c + hw].

condition

Whether this handler applies to these operands (default: always).

fn

The scalar function being approximated (elementwise, differentiable).

handle

Transform the operands; sub-operations go through interp.<op> so the enclosing domain's handlers apply to them too.

legendre_coeffs

Implemented based on.

optimized_polynomial

Legendre coefficients plus a sound symmetric bound on the residual.

override_handler

A copy of this handler that takes precedence over other when both are ready for the same call.

quartic_bound

Elementwise bound on |fn''''| over [lb, ub] (for spline3_ibp).

op: str = 'relu'#
order: int = 2#
nintvl: int = None#
optimizer: AdamConfig | None#
opt_iters: int = 500#
fn(x)[source]#

The scalar function being approximated (elementwise, differentiable).

quartic_bound(lb, ub)[source]#

Elementwise bound on |fn''''| over [lb, ub] (for spline3_ibp).

legendre_coeffs(c, hw, **kwargs)[source]#

Implemented based on:

l(i) = (2n+1)/(2a^n) integrate(relu(a x + b) legendrep(n,x), {x,-1,1})
l(0) = -1/4 (cb - 1) (a + 2 b + a cb)
l(1) = -1/4 (cb2m1(1) * 3 b + 2 (cb - 1) (1 + cb + cb^2))
l(2) = -5/16 cb2m1(2) (a + 4 b cb + 3 a cb^2)
l(3) = -7/16 cb2m1(3) (-b + 5 b cb^2 + 4 a cb^3)
l(4) = -3/32 cb2m1(4) (-a - 18 b cb - 10 a cb^2 + 42 b cb^3 + 35 a cb^4)
l(5) = -11/32 cb2m1(5) (b - 14 b cb^2 - 10 a cb^3 + 21 b cb^4 + 18 a cb^5)
l(6) = -13/256 cb2m1(6) (a + 40 b cb + 21 a cb^2 - 240 b cb^3 - 189 a cb^4 + 264 b cb^5 + 231 a cb^6)

where cb      = clip(-b/a, -1, 1)
      cb2m1(n) = (cb^2 - 1) / a^n

cb and cb2m1 are handled safely for a == 0.

approx_lbub(polyc, c, hw)[source]#

Elementwise range of the residual fn - p over [c - hw, c + hw].

The interpolant p has degree <= 3, so the residual’s fourth derivative is fn'''' and quartic_bound applies unchanged.

__init__(op='relu', order=2, nintvl=None, optimizer=<factory>, opt_iters=500)#
condition(*args, **kwargs)#

Whether this handler applies to these operands (default: always).

handle(interp, x, **kwargs)#

Transform the operands; sub-operations go through interp.<op> so the enclosing domain’s handlers apply to them too.

optimized_polynomial(c, hw, **kwargs)#

Legendre coefficients plus a sound symmetric bound on the residual.

The residual’s center is folded into li[0] so the returned noise is the tightest symmetric enclosure of fn - p.

Non-finite values saturate soundly instead of leaking NaN into the bounds: bad coefficients (overflow, out-of-domain inputs) are zeroed before the residual is bounded against them, and an overflowing residual bound becomes an infinite noise with a finite center.

With an optimizer, every element independently keeps the coefficients of the tightest residual width seen anywhere along the trajectory — the closed form is iterate 0, so no element ever ends looser than it — and the noise is recomputed from that selection.

override_handler(other)#

A copy of this handler that takes precedence over other when both are ready for the same call.

overrides: list[type[OpHandler]] = []#