Source code for bayesflow.adapters.transforms.keras_transform
import keras.ops as ops
from bayesflow.types import Tensor
from bayesflow.utils.serialization import serializable, serialize
from .elementwise_transform import ElementwiseTransform
[docs]
@serializable("bayesflow.adapters")
class KerasTransform(ElementwiseTransform):
"""
A class to apply element-wise transformations using named `keras.ops` functions.
Attributes
----------
_forward : str
The name of the `keras.ops` function to apply in the forward transformation.
_inverse : str
The name of the `keras.ops` function to apply in the inverse transformation.
"""
#: Dict of function names that support automatic selection of their inverse.
INVERSE_METHODS = {
"arctan": "tan",
"exp": "log",
"expm1": "log1p",
"square": "sqrt",
"reciprocal": "reciprocal",
}
# ensure the map is symmetric
INVERSE_METHODS |= {v: k for k, v in INVERSE_METHODS.items()}
def __init__(self, forward: str, inverse: str = None):
"""
Initializes the KerasTransform with specified forward and inverse functions.
Parameters
----------
forward : str
The name of the `keras.ops` function to use for the forward transformation.
inverse : str, optional
The name of the `keras.ops` function to use for the inverse transformation.
By default, the inverse is inferred from the forward argument for supported methods.
"""
super().__init__()
self._forward_name = self._validate_name(forward)
if inverse is None:
if self._forward_name not in self.INVERSE_METHODS:
raise ValueError(f"Cannot infer inverse for method {forward!r}")
inverse = self.INVERSE_METHODS[self._forward_name]
self._inverse_name = self._validate_name(inverse)
@staticmethod
def _validate_name(name: str) -> str:
if not isinstance(name, str):
raise ValueError("Transformation must be specified as the name of a keras.ops function.")
if not callable(getattr(ops, name, None)):
raise ValueError(f"keras.ops has no function named {name!r}.")
return name
[docs]
def get_config(self) -> dict:
return serialize({"forward": self._forward_name, "inverse": self._inverse_name})
[docs]
def forward(self, data: Tensor, **kwargs) -> Tensor:
return getattr(ops, self._forward_name)(data)
[docs]
def inverse(self, data: Tensor, **kwargs) -> Tensor:
return getattr(ops, self._inverse_name)(data)
[docs]
def log_det_jac(self, data, inverse=False, **kwargs):
raise NotImplementedError("log determinant of the Jacobian of the keras transforms are not implemented yet")