Source code for bayesflow.networks.inference.coupling.transforms.transform
import keras
from bayesflow.masking import MaskHandler
from bayesflow.types import Tensor
from ..invertible_layer import InvertibleLayer
[docs]
class Transform(InvertibleLayer):
"""Base class for the elementwise bijections applied inside a coupling layer.
A transform declares how many parameters it needs per target dimension, splits
the flat parameter vector predicted by the coupling subnet into named parts,
constrains those parts to their valid ranges, and applies the resulting
bijection.
Subclasses implement ``params_per_dim``, ``split_parameters``,
``constrain_parameters``, ``_forward`` and ``_inverse``. The latter two return
the transformed values and their elementwise log-Jacobian;
``call`` reduces it to the log-determinant.
"""
@property
def params_per_dim(self) -> int:
raise NotImplementedError
[docs]
def split_parameters(self, parameters: Tensor) -> dict[str, Tensor]:
raise NotImplementedError
[docs]
def constrain_parameters(self, parameters: dict[str, Tensor]) -> dict[str, Tensor]:
raise NotImplementedError
[docs]
def call(
self,
xz: Tensor,
parameters: dict[str, Tensor],
inverse: bool = False,
target_mask: Tensor = None,
) -> tuple[Tensor, Tensor]:
if inverse:
out, log_jac = self._inverse(xz, parameters)
else:
out, log_jac = self._forward(xz, parameters)
mask = target_mask
out = MaskHandler.maybe_apply(out, mask, xz)
log_jac = MaskHandler.maybe_apply(log_jac, mask)
return out, keras.ops.sum(log_jac, axis=-1)
def _forward(self, x: Tensor, parameters: dict[str, Tensor]) -> tuple[Tensor, Tensor]:
raise NotImplementedError
def _inverse(self, z: Tensor, parameters: dict[str, Tensor]) -> tuple[Tensor, Tensor]:
raise NotImplementedError