Skip to content

transforms

Differentiable bijections between unconstrained and constrained space.

Calibration is performed in an unconstrained latent space: the optimizer holds raw real-valued variables z and a bijector maps them onto the feasible region of the physical parameter on every forward pass. Because the map is applied inside the autograd graph, gradients flow cleanly back to z and every iterate is feasible by construction — there is no need for penalties or post-step projection.

Transforms are looked up through a small registry so new ones can be added without touching ~torchcrop.calibration.manager.CalibrationManager (open/closed principle).

Bijector dataclass

A differentiable map between latent z and a constrained value.

Attributes:

Name Type Description
forward Callable[[torch.Tensor], torch.Tensor]

z -> value (unconstrained → constrained / physical).

inverse Callable[[torch.Tensor], torch.Tensor]

value -> z (constrained → unconstrained), used to seed the latent from an initial physical value.

Source code in torchcrop/calibration/transforms.py
@dataclass(frozen=True)
class Bijector:
    """A differentiable map between latent ``z`` and a constrained value.

    Attributes:
        forward: ``z -> value`` (unconstrained → constrained / physical).
        inverse: ``value -> z`` (constrained → unconstrained), used to seed
            the latent from an initial physical value.
    """

    forward: Callable[[torch.Tensor], torch.Tensor]
    inverse: Callable[[torch.Tensor], torch.Tensor]

available_transforms()

Return the names of all registered transforms.

Source code in torchcrop/calibration/transforms.py
def available_transforms() -> tuple[str, ...]:
    """Return the names of all registered transforms."""
    return tuple(sorted(_TRANSFORMS))

build_transform(name, bounds)

Instantiate a registered transform.

Parameters:

Name Type Description Default
name str

Registered transform name (e.g. "affine_sigmoid").

required
bounds tuple[float, float] | None

(lo, hi) bounds, interpreted per transform (some require both, some only the lower bound, some ignore them).

required

Returns:

Type Description
Bijector

The configured Bijector.

Exceptions:

Type Description
KeyError

If name is not registered.

Source code in torchcrop/calibration/transforms.py
def build_transform(name: str, bounds: tuple[float, float] | None) -> Bijector:
    """Instantiate a registered transform.

    Args:
        name: Registered transform name (e.g. ``"affine_sigmoid"``).
        bounds: ``(lo, hi)`` bounds, interpreted per transform (some require
            both, some only the lower bound, some ignore them).

    Returns:
        The configured `Bijector`.

    Raises:
        KeyError: If ``name`` is not registered.
    """
    if name not in _TRANSFORMS:
        raise KeyError(
            f"unknown transform {name!r}; registered: {sorted(_TRANSFORMS)}"
        )
    return _TRANSFORMS[name](bounds)

register_transform(name)

Register a transform builder under name.

Parameters:

Name Type Description Default
name str

Key used in torchcrop.calibration.spec.ParameterSpec.

required

Returns:

Type Description
Callable[[Callable[[tuple[float, float] | None], Bijector]], Callable[[tuple[float, float] | None], Bijector]]

A decorator registering the wrapped builder(bounds) -> Bijector.

Source code in torchcrop/calibration/transforms.py
def register_transform(
    name: str,
) -> Callable[
    [Callable[[tuple[float, float] | None], Bijector]],
    Callable[[tuple[float, float] | None], Bijector],
]:
    """Register a transform builder under ``name``.

    Args:
        name: Key used in `torchcrop.calibration.spec.ParameterSpec`.

    Returns:
        A decorator registering the wrapped ``builder(bounds) -> Bijector``.
    """

    def _decorator(
        builder: Callable[[tuple[float, float] | None], Bijector],
    ) -> Callable[[tuple[float, float] | None], Bijector]:
        _TRANSFORMS[name] = builder
        return builder

    return _decorator

round_ste(x)

Round with a straight-through gradient estimator.

Forward returns round(x); backward passes the gradient through unchanged (as if the op were the identity). This keeps an integer-valued parameter inside the gradient pipeline so a continuous surrogate can still be nudged by the optimizer while the value used in the forward pass stays integral.

Parameters:

Name Type Description Default
x torch.Tensor

Continuous surrogate value.

required

Returns:

Type Description
torch.Tensor

round(x) in the forward pass, identity gradient in the backward.

Source code in torchcrop/calibration/transforms.py
def round_ste(x: torch.Tensor) -> torch.Tensor:
    """Round with a straight-through gradient estimator.

    Forward returns ``round(x)``; backward passes the gradient through
    unchanged (as if the op were the identity). This keeps an integer-valued
    parameter inside the gradient pipeline so a continuous surrogate can still
    be nudged by the optimizer while the value used in the forward pass stays
    integral.

    Args:
        x: Continuous surrogate value.

    Returns:
        ``round(x)`` in the forward pass, identity gradient in the backward.
    """
    return (torch.round(x) - x).detach() + x