Example: Manual Bounds on an Affine + ReLU Graph#

This example builds the abstract value by hand — no model export — and invokes the zonotope ReLU handler directly.

import torch

from boundlab import Error
from boundlab.utils import ShapeDtype
from boundlab.zono import Zono, interpret

# x in center + [-1, 1]^4
center = torch.tensor([0.2, -0.5, 1.0, -1.2])
err = Error("input", ShapeDtype(center.shape, center.dtype))
x = Zono.error(err) + center

# One affine layer y = x @ W + b, exact on the zonotope.
W = torch.tensor([
    [1.0, -0.5],
    [-0.2, 0.8],
    [0.4, 0.0],
    [0.0, 1.1],
])
b = torch.tensor([0.1, -0.3])
y_lin = x @ W + b

# Apply the registered zonotope ReLU handler directly.
y = interpret.relu(y_lin)

lb, ub = y.lbub()
print("lb =", lb)
print("ub =", ub)
print("width =", ub - lb)

What this demonstrates#

  • Modeling uncertainty with an Error symbol lifted by Zono.error.

  • Composing affine maps with @ and + — exact for zonotopes.

  • Invoking a nonlinear handler from the interpreter by attribute (interpret.relu).

  • Concretizing a final expression via lbub().