Source code for sionna.phy.channel.awgn

#
# SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Block for simulating an AWGN channel"""

from typing import Optional, Union
import torch

from sionna.phy import Block
from sionna.phy.config import Precision
from sionna.phy.utils import expand_to_rank, complex_normal

__all__ = ["AWGN"]


[docs] class AWGN(Block): r"""Add complex AWGN to the inputs with a certain variance. This block adds complex AWGN noise with variance ``no`` to the input. The noise has variance ``no/2`` per real dimension. It can be either a scalar or a tensor which can be broadcast to the shape of the input. :param precision: Precision used for internal calculations and outputs. If set to `None`, :attr:`~sionna.phy.config.Config.precision` is used. :param device: Device for computation. If `None`, :attr:`~sionna.phy.config.Config.device` is used. :input x: [...], `torch.complex`. Channel input. :input no: Scalar or Tensor, `torch.float`. Scalar or tensor whose shape can be broadcast to the shape of ``x``. The noise power ``no`` is per complex dimension. If ``no`` is a scalar, noise of the same variance will be added to the input. If ``no`` is a tensor, it must have a shape that can be broadcast to the shape of ``x``. This allows, e.g., adding noise of different variance to each example in a batch. If ``no`` has a lower rank than ``x``, then ``no`` will be broadcast to the shape of ``x`` by adding dummy dimensions after the last axis. :output y: Tensor with same shape as ``x``, `torch.complex`. Channel output. .. rubric:: Examples .. code-block:: python import torch from sionna.phy.channel import AWGN awgn_channel = AWGN() x = torch.randn(64, 16, dtype=torch.complex64) no = 0.1 y = awgn_channel(x, no) print(y.shape) # torch.Size([64, 16]) """ def __init__( self, precision: Optional[Precision] = None, device: Optional[str] = None, **kwargs, ) -> None: super().__init__(precision=precision, device=device, **kwargs) def call( self, x: torch.Tensor, no: Union[float, torch.Tensor], ) -> torch.Tensor: """Apply AWGN to the input.""" # Create tensor of complex-valued Gaussian noise with unit variance # Uses smart random that switches to global RNG in compiled mode for graph fusion noise = complex_normal(x.shape, precision=self.precision, device=self.device, generator=self.torch_rng) # Convert no to tensor if it's a scalar if not isinstance(no, torch.Tensor): no = torch.tensor(no, dtype=self.dtype, device=self.device) # Add extra dimensions for broadcasting no = expand_to_rank(no, x.dim(), axis=-1) # Apply variance scaling no = no.to(dtype=self.dtype, device=self.device) noise = noise * no.sqrt().to(dtype=self.cdtype) # Add noise to input return x + noise