boundlab.zono.exp_linearizer#

boundlab.zono.exp_linearizer(ub, lb)[source]#

Minimal-area exp relaxation (DeepT, Section 4.5).

The returned zonotope y = slope·x + mu + beta·ε (with ε ∈ [-1, 1]) over-approximates exp(x) on [lb, ub] and guarantees mu - beta 0 so downstream handlers (reciprocal / softmax) never see a non-positive lower bound.

Strategy:

  • For each element, try the single-slope “minimal area” relaxation: tangent at t_opt as the lower envelope, parallel secant line through (ub_c, exp(ub_c)) as the upper envelope.

  • If the tangent gives a non-positive offset (e.g. lb > 0) or the fp32 precision would lose that offset against exp(ub), fall back to the interval relaxation slope = 0, mu = beta = exp(ub)/2, which yields [0, exp(ub)] — loose but guaranteed mu - beta = 0 bit-exactly (so downstream ublb cannot go negative).

  • Clamp lb/ub to ±30 to keep exp numerically safe; the underflow/overflow branches use the interval fallback.

Examples

>>> import torch
>>> import boundlab.expr as expr
>>> from boundlab.zono.exp import exp_linearizer
>>> x = expr.ConstVal(torch.tensor([0.0])) + 0.1 * expr.LpEpsilon([1])
>>> ub, lb = x.ublb()
>>> b = exp_linearizer(ub, lb)
>>> b.bias.shape
torch.Size([1])