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] |
|
inverse |
Callable[[torch.Tensor], torch.Tensor] |
|
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. |
required |
bounds |
tuple[float, float] | None |
|
required |
Returns:
| Type | Description |
|---|---|
Bijector |
The configured |
Exceptions:
| Type | Description |
|---|---|
KeyError |
If |
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 |
required |
Returns:
| Type | Description |
|---|---|
Callable[[Callable[[tuple[float, float] | None], Bijector]], Callable[[tuple[float, float] | None], Bijector]] |
A decorator registering the wrapped |
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 |
|
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