PolarSCLDecoder#

class sionna.phy.fec.polar.PolarSCLDecoder(frozen_pos: numpy.ndarray, n: int, list_size: int = 8, crc_degree: str | None = None, ind_iil_inv: numpy.ndarray | None = None, return_crc_status: bool = False, *, precision: str | None = None, device: str | None = None, **kwargs)[source]#

Bases: sionna.phy.block.Block

Fast successive cancellation list (SCL) decoder for Polar and Polar-like codes [Tal_SCL] [Hashemi_SSCL]. Rate-1 nodes use a single-flip (M=1) shortcut rather than exact list decoding; see Notes.

Parameters:
  • frozen_pos (numpy.ndarray) – Array of int defining the n-k indices of the frozen positions.

  • n (int) – Defining the codeword length.

  • list_size (int) – Defining the list size L of the decoder. Must be a power of 2.

  • crc_degree (str | None) – Defining the CRC polynomial to be used. Can be any value from {CRC24A, CRC24B, CRC24C, CRC16, CRC11, CRC6}.

  • ind_iil_inv (numpy.ndarray | None) – If not None, the sequence is used as inverse input-bit interleaver before the CRC is evaluated. This only affects CRC evaluation; the output sequence is not permuted.

  • return_crc_status (bool) – If True, the decoder additionally returns the CRC status indicating if a codeword was (most likely) correctly recovered. This is only available if crc_degree is not None.

  • precision (str | None) – Precision used for internal calculations and outputs. If None, precision is used.

  • device (str | None) – Device for computation (e.g., ‘cpu’, ‘cuda:0’). If None, device is used.

Inputs:

llr_ch – […, n], torch.float. Tensor containing the channel LLR values (as logits).

Outputs:
  • b_hat – […, k], torch.float. Binary tensor containing hard-decided estimations of all k information bits.

  • crc_status – […], torch.bool. CRC status indicating if a codeword was (most likely) correctly recovered. This is only returned if return_crc_status is True. Note that false positives are possible.

Notes

This block implements the successive cancellation list (SCL) decoder as described in [Tal_SCL] using LLR-domain message updates [Stimming_LLR]. At construction time, the decoder performs a single depth-first traversal of the Polar decoding tree and generates a fixed sequence of message-passing instructions (the decoder tape). The traversal also applies the fast-SCL node shortcuts of [Hashemi_SSCL], which collapse common sub-trees into single ops to avoid descending all the way to per-bit leaves.

Each tape entry is therefore one of F (check-node update), G (variable-node update), COMBINE (upward XOR combine), a single frozen or info leaf decision, or one of the fast-SCL node shortcuts R0 / REP / R1. Decoding replays this tape over shared workspace tensors, so the sequence of launched kernels does not depend on the channel input and is compatible with torch.compile and CUDA graph capture.

The fast-SCL shortcuts cover three special sub-tree shapes: an all-frozen sub-tree is replaced by a single R0 op, a repetition sub-tree (all leaves frozen except the last) by a single REP op, and a rate-1 sub-tree (no frozen leaves) by a single R1 op. R1 uses the single-flip approximation (M=1), i.e., it keeps two alternatives per surviving path: the maximum-likelihood codeword and the codeword with the least-reliable bit flipped.

Only the currently active region of the Polar tree is materialized per path: LLRs occupy a flat buffer of size n - 1 indexed by stage, hard decisions for stages 1 .. n_stages - 1 occupy 2n - 4 slots, and stage-0 leaf decisions live in a persistent [B, 2L, n] tensor. The per-op workspace offsets are pre-computed during tape construction so that decoding issues plain tensor slices without any per-op address arithmetic.

With CRC-aided decoding, the decoder selects the surviving path with the smallest path metric after adding a large CRC-failure penalty to every path that does not check. If no path passes the CRC, the path with the smallest underlying metric is returned and its crc_status is False.

As commonly done, we assume frozen bits are set to 0. Please note that - although its practical relevance is only little - setting frozen bits to 1 may result in affine codes instead of a linear code as the all-zero codeword is not necessarily part of the code any more.

Examples

import torch
from sionna.phy.fec.polar import PolarSCLDecoder, PolarEncoder
from sionna.phy.fec.polar.utils import generate_5g_ranking

k, n = 100, 256
frozen_pos, _ = generate_5g_ranking(k, n)
encoder = PolarEncoder(frozen_pos, n)
decoder = PolarSCLDecoder(frozen_pos, n, list_size=8)

bits = torch.randint(0, 2, (10, k), dtype=torch.float32)
codewords = encoder(bits)
llr_ch = 20.0 * (2.0 * codewords - 1)  # BPSK without noise
decoded = decoder(llr_ch)
print(torch.equal(bits, decoded))
# True

Attributes

property frozen_pos#

Frozen positions for Polar decoding.

property info_pos#

Information bit positions for Polar encoding.

property k#

Number of information bits (including any outer CRC).

property k_crc#

Length of the outer CRC (0 if none).

property list_size#

Decoder list size L.

property llr_max#

Internal LLR clipping magnitude.

property n#

Codeword length.