Rate this Page

Source code for torchrl.modules.functional

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
"""Functional operations for TorchRL modules."""
from __future__ import annotations

import torch


[docs] def symlog(value: torch.Tensor) -> torch.Tensor: """Apply the element-wise symmetric logarithm transform. The transform is defined as ``sign(value) * log(1 + abs(value))`` and compresses both positive and negative values while remaining approximately linear around zero. Args: value (torch.Tensor): Input tensor. Returns: A tensor with the same shape, dtype, and device as ``value``. Examples: >>> import torch >>> from torchrl.modules import functional as F >>> value = torch.tensor([-100.0, 0.0, 100.0]) >>> F.symlog(value) tensor([-4.6151, 0.0000, 4.6151]) .. seealso:: :func:`symexp` for the inverse operation and :class:`~torchrl.modules.SymLogValueTransform` for the module form. """ transformed = value.sign() * value.abs().log1p() # ``sign`` has a zero derivative at the origin even though symlog has a # derivative of one there. Keep the mathematically correct local gradient. return torch.where(value == 0, value, transformed)
[docs] def symexp(value: torch.Tensor) -> torch.Tensor: """Apply the inverse symmetric exponential transform element-wise. Args: value (torch.Tensor): Input tensor in symmetric-log space. Returns: A tensor with the same shape, dtype, and device as ``value``. Examples: >>> import torch >>> from torchrl.modules import functional as F >>> value = torch.tensor([-100.0, 0.0, 100.0]) >>> transformed = F.symlog(value) >>> transformed tensor([-4.6151, 0.0000, 4.6151]) >>> F.symexp(transformed) tensor([-100.0000, 0.0000, 100.0000]) .. seealso:: :func:`symlog` for the forward operation and :class:`~torchrl.modules.SymLogValueTransform` for the module form. """ transformed = value.sign() * value.abs().expm1() return torch.where(value == 0, value, transformed)
[docs] def signed_hyperbolic(value: torch.Tensor, epsilon: float = 1e-3) -> torch.Tensor: """Apply the signed hyperbolic value transform. This is the scale-compressing transform introduced by Pohlen et al. and used by algorithms in the MuZero and Muesli families: ``sign(value) * (sqrt(abs(value) + 1) - 1) + epsilon * value``. Args: value (torch.Tensor): Input tensor. epsilon (float, optional): Positive linear correction that keeps the inverse Lipschitz continuous. Defaults to ``1e-3``. Returns: A tensor with the same shape, dtype, and device as ``value``. Examples: >>> import torch >>> from torchrl.modules import functional as F >>> value = torch.tensor([-100.0, 0.0, 100.0]) >>> F.signed_hyperbolic(value) tensor([-9.1499, 0.0000, 9.1499]) .. seealso:: :func:`signed_parabolic` for the inverse operation and :class:`~torchrl.modules.SignedHyperbolicValueTransform` for the module form. .. note:: See `Observe and Look Further: Achieving Consistent Performance on Atari <https://arxiv.org/abs/1805.11593>`_ (Pohlen et al., 2018). """ if epsilon <= 0: raise ValueError(f"epsilon must be positive, got {epsilon}.") transformed = value.sign() * (torch.sqrt(value.abs() + 1) - 1) + epsilon * value origin = value * (0.5 + epsilon) return torch.where(value == 0, origin, transformed)
[docs] def signed_parabolic(value: torch.Tensor, epsilon: float = 1e-3) -> torch.Tensor: """Apply the inverse of :func:`signed_hyperbolic` element-wise. Args: value (torch.Tensor): Input tensor in signed-hyperbolic space. epsilon (float, optional): Positive linear correction used by the corresponding :func:`signed_hyperbolic` call. Defaults to ``1e-3``. Returns: A tensor with the same shape, dtype, and device as ``value``. Examples: >>> import torch >>> from torchrl.modules import functional as F >>> value = torch.tensor([-100.0, 0.0, 100.0]) >>> transformed = F.signed_hyperbolic(value) >>> transformed tensor([-9.1499, 0.0000, 9.1499]) >>> F.signed_parabolic(transformed) tensor([-100.0000, 0.0000, 100.0000]) .. seealso:: :func:`signed_hyperbolic` for the forward operation and :class:`~torchrl.modules.SignedHyperbolicValueTransform` for the module form. """ if epsilon <= 0: raise ValueError(f"epsilon must be positive, got {epsilon}.") magnitude = value.abs() discriminant = torch.sqrt(1 + 4 * epsilon * (magnitude + 1 + epsilon)) # This rationalized form of (discriminant - 1) / (2 * epsilon) # avoids cancellation when epsilon is small. root = 2 * (magnitude + 1 + epsilon) / (discriminant + 1) transformed = value.sign() * (root.square() - 1) origin = value / (0.5 + epsilon) return torch.where(value == 0, origin, transformed)
__all__ = ["signed_hyperbolic", "signed_parabolic", "symexp", "symlog"]