Source code for boundlab.poly.square
"""Square linearizer for polytope abstract interpretation."""
from __future__ import annotations
import torch
from . import PolyBounds, _register_linearizer
[docs]
@_register_linearizer("Square")
def square_linearizer(ub: torch.Tensor, lb: torch.Tensor) -> PolyBounds:
r"""CROWN relaxation of :math:`x^2`.
Uses the secant line as the upper envelope and the tangent at the
interval midpoint as the lower envelope.
"""
degen = torch.abs(ub - lb) < 1e-12
# Upper envelope (secant through (lb, lb^2) and (ub, ub^2)).
upper_lam = ub + lb
upper_bias = -ub * lb
# Lower envelope (tangent at midpoint).
mid = 0.5 * (ub + lb)
lower_lam = 2.0 * mid
lower_bias = -(mid * mid)
# Degenerate interval: exact line at x = lb.
exact_lam = 2.0 * lb
exact_bias = -(lb * lb)
upper_lam = torch.where(degen, exact_lam, upper_lam)
upper_bias = torch.where(degen, exact_bias, upper_bias)
lower_lam = torch.where(degen, exact_lam, lower_lam)
lower_bias = torch.where(degen, exact_bias, lower_bias)
return PolyBounds(
upper_lam=upper_lam,
upper_bias=upper_bias,
lower_lam=lower_lam,
lower_bias=lower_bias,
)