5G Channel Coding and Rate-Matching: Polar vs. LDPC Codes#

“For block lengths of about 500, an IBM 7090 computer requires about 0.1 seconds per iteration to decode a block by probabilistic decoding scheme. Consequently, many hours of computation time are necessary to evaluate even a \(P(e)\) in the order of \({10^{-4}}\) .” Robert G. Gallager, 1963 [7]

In this notebook, you will learn about the different coding schemes in 5G NR and how rate-matching works (cf. 3GPP TS 38.212 [3]). The coding schemes are compared under different length/rate settings and for different decoders.

You will learn about the following components:

  • 5G low-density parity-checks (LDPC) codes [7]. These codes support - without further segmentation - up to k=8448 information bits per codeword [3] for a wide range of coderates.

  • Polar codes [1] including CRC concatenation and rate-matching for 5G compliant en-/decoding is implemented for the Polar uplink control channel (UCI) [3]. Besides Polar codes, Reed-Muller (RM) codes and several decoders are available:

    • Successive cancellation (SC) decoding [1]

    • Successive cancellation list (SCL) decoding [2]

    • Iterative belief propagation (BP) decoding [6]

Further, we will demonstrate the basic functionality of the Sionna forward error correction (FEC) module which also includes support for:

  • Convolutional codes with non-recursive encoding and Viterbi/BCJR decoding

  • Turbo codes and iterative BCJR decoding

  • Ordered statistics decoding (OSD) for any binary, linear code

  • Interleaving and scrambling

For additional technical background we refer the interested reader to [4,5,8].

Please note that block segmentation is not considered in the following as it only concatenates multiple code blocks without increasing the effective codewords length from decoder’s perspective.

Some simulations in this notebook require severe simulation time, in particular if parameter sweeps are involved (e.g., different length comparisons). Please keep in mind that each cell in this notebook already contains the pre-computed outputs and no new execution is required to understand the examples.

Table of Contents#

Configuration and Imports#

[1]:
# Import Sionna
try:
    import sionna.phy
except ImportError as e:
    import os
    import sys
    if 'google.colab' in sys.modules:
       # Install Sionna in Google Colab
       print("Installing Sionna and restarting the runtime. Please run the cell again.")
       os.system("pip install sionna")
       os.kill(os.getpid(), 5)
    else:
       raise e

import torch

# Set random seed for reproducibility
sionna.phy.config.seed = 42

# Load the required Sionna components
from sionna.phy import Block
from sionna.phy.mapping import Constellation, Mapper, Demapper, BinarySource
from sionna.phy.fec.polar import PolarEncoder, Polar5GEncoder, PolarSCLDecoder, Polar5GDecoder
from sionna.phy.fec.ldpc import LDPC5GEncoder, LDPC5GDecoder
from sionna.phy.fec.polar.utils import generate_5g_ranking, generate_rm_code
from sionna.phy.fec.conv import ConvEncoder, ViterbiDecoder
from sionna.phy.fec.turbo import TurboEncoder, TurboDecoder
from sionna.phy.fec.linear import OSDecoder
from sionna.phy.utils import count_block_errors, ebnodb2no, PlotBER
from sionna.phy.channel import AWGN
[2]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import time # for throughput measurements

# Suppress the informational UserWarnings emitted by Polar5GDecoder when
# dec_type is set to "SC" or "BP"; these warnings would otherwise repeat
# in the cell outputs every time a non-list decoder is built.
import warnings
warnings.filterwarnings(
    "ignore",
    message=r"5G Polar codes use an integrated CRC.*",
    category=UserWarning,
)

BER Performance of 5G Coding Schemes#

Let us first focus on short length coding, e.g., for internet of things (IoT) and ultra-reliable low-latency communications (URLLC). We aim to reproduce similar results as in [9] for the coding schemes supported by Sionna.

For a detailed explanation of the PlotBER class, we refer to the example notebook on Bit-Interleaved Coded Modulation.

The Sionna API allows to pass an encoder object/layer to the decoder initialization for the 5G decoders. This means that the decoder is directly associated to a specific encoder and knows all relevant code parameters. Please note that - of course - no data or information bits are exchanged between these two associated components. It just simplifies handling of the code parameters, in particular, if rate-matching is used.

Let us define the system model first. We use encoder and decoder as input parameter such that the model remains flexible w.r.t. the coding scheme.

[3]:
class System_Model(Block):
    """System model for channel coding BER simulations.

    This model allows to simulate BERs over an AWGN channel with
    QAM modulation. Arbitrary FEC encoder/decoder layers can be used to
    initialize the model.

    Parameters
    ----------
        k: int
            number of information bits per codeword.

        n: int
            codeword length.

        num_bits_per_symbol: int
            number of bits per QAM symbol.

        encoder: Sionna Block
            A Sionna Block that encodes information bit tensors.

        decoder: Sionna Block
            A Sionna Block layer that decodes llr tensors.

        demapping_method: str
            A string denoting the demapping method. Can be either "app" or "maxlog".

        sim_esno: bool
            A boolean defaults to False. If true, no rate-adjustment is done for the SNR calculation.

         cw_estiamtes: bool
            A boolean defaults to False. If true, codewords instead of information estimates are returned.
    Input
    -----
        batch_size: int or torch.int
            The batch_size used for the simulation.

        ebno_db: float or torch.float
            A float defining the simulation SNR.

    Output
    ------
        (u, u_hat):
            Tuple:

        u: torch.float
            A tensor of shape `[batch_size, k] of 0s and 1s containing the transmitted information bits.

        u_hat: torch.float
            A tensor of shape `[batch_size, k] of 0s and 1s containing the estimated information bits.
    """
    def __init__(self,
                 k,
                 n,
                 num_bits_per_symbol,
                 encoder,
                 decoder,
                 demapping_method="app",
                 sim_esno=False,
                 cw_estimates=False):

        super().__init__()

        # store values internally
        self.k = k
        self.n = n
        self.sim_esno = sim_esno # disable rate-adjustment for SNR calc
        self.cw_estimates=cw_estimates # if true codewords instead of info bits are returned

        # number of bit per QAM symbol
        self.num_bits_per_symbol = num_bits_per_symbol

        # init components
        self.source = BinarySource()

        # initialize mapper and demapper for constellation object
        self.constellation = Constellation("qam",
                                num_bits_per_symbol=self.num_bits_per_symbol)
        self.mapper = Mapper(constellation=self.constellation)
        self.demapper = Demapper(demapping_method,
                                 constellation=self.constellation)

        # the channel can be replaced by more sophisticated models
        self.channel = AWGN()

        # FEC encoder / decoder
        self.encoder = encoder
        self.decoder = decoder

    def call(self, batch_size, ebno_db):

        # calculate noise variance
        if self.sim_esno:
                no = ebnodb2no(ebno_db,
                       num_bits_per_symbol=1,
                       coderate=1)
        else:
            no = ebnodb2no(ebno_db,
                           num_bits_per_symbol=self.num_bits_per_symbol,
                           coderate=self.k/self.n)

        u = self.source([batch_size, self.k]) # generate random data
        c = self.encoder(u) # explicitly encode

        x = self.mapper(c) # map c to symbols x

        y = self.channel(x, no) # transmit over AWGN channel

        llr_ch = self.demapper(y, no) # demap y to LLRs

        u_hat = self.decoder(llr_ch) # run FEC decoder (incl. rate-recovery)

        if self.cw_estimates:
            return c, u_hat

        return u, u_hat

And let us define the codes to be simulated.

[4]:
# code parameters
k = 64 # number of information bits per codeword
n = 128 # desired codeword length

# Create list of encoder/decoder pairs to be analyzed.
# This allows automated evaluation of the whole list later.
codes_under_test = []

# 5G LDPC codes with 20 BP iterations
enc = LDPC5GEncoder(k=k, n=n)
dec = LDPC5GDecoder(enc, num_iter=20)
name = "5G LDPC BP-20"
codes_under_test.append([enc, dec, name])

# Polar Codes (SC decoding)
enc = Polar5GEncoder(k=k, n=n)
dec = Polar5GDecoder(enc, dec_type="SC")
name = "5G Polar+CRC SC"
codes_under_test.append([enc, dec, name])

# Polar Codes (SCL decoding) with list size 8.
# The CRC is automatically added by the layer.
enc = Polar5GEncoder(k=k, n=n)
dec = Polar5GDecoder(enc, dec_type="SCL", list_size=8)
name = "5G Polar+CRC SCL-8"
codes_under_test.append([enc, dec, name])

### non-5G coding schemes

# RM codes with SCL decoding
f,_,_,_,_ = generate_rm_code(3,7) # equals k=64 and n=128 code
enc = PolarEncoder(f, n)
dec = PolarSCLDecoder(f, n, list_size=8)
name = "Reed Muller (RM) SCL-8"
codes_under_test.append([enc, dec, name])

# Conv. code with Viterbi decoding
enc = ConvEncoder(rate=1/2, constraint_length=8)
dec = ViterbiDecoder(gen_poly=enc.gen_poly, method="soft_llr")
name = "Conv. Code Viterbi (constraint length 8)"
codes_under_test.append([enc, dec, name])

# Turbo. codes
enc = TurboEncoder(rate=1/2, constraint_length=4, terminate=False) # no termination used due to the rate loss
dec = TurboDecoder(enc, num_iter=8)
name = "Turbo Code (constraint length 4)"
codes_under_test.append([enc, dec, name])

Remark: some of the coding schemes are not 5G relevant, but are included in this comparison for the sake of completeness.

Generate a new BER plot figure to save and plot simulation results efficiently.

[5]:
ber_plot128 = PlotBER(f"Performance of Short Length Codes (k={k}, n={n})")

And run the BER simulation for each code.

[6]:
num_bits_per_symbol = 2 # QPSK
ebno_db = np.arange(0, 5, 0.5) # sim SNR range

# run ber simulations for each code we have added to the list
for code in codes_under_test:
    print("\nRunning: " + code[2])

    # generate a new model with the given encoder/decoder
    model = System_Model(k=k,
                         n=n,
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=code[0],
                         decoder=code[1])

    # the first argument must be a callable (function) that yields u and u_hat for batch_size and ebno
    ber_plot128.simulate(model, # the function have defined previously
                         ebno_dbs=ebno_db, # SNR to simulate
                         legend=code[2], # legend string for plotting
                         max_mc_iter=100, # run 100 Monte Carlo runs per SNR point
                         num_target_block_errors=1000, # continue with next SNR point after 1000 bit errors
                         batch_size=10000, # batch-size per Monte Carlo run
                         soft_estimates=False, # the model returns hard-estimates
                         early_stop=True, # stop simulation if no error has been detected at current SNR point
                         show_fig=False, # we show the figure after all results are simulated
                         add_bler=True, # in case BLER is also interesting
                         forward_keyboard_interrupt=True, # should be True in a loop
                         compile_mode="default");

    torch.cuda.empty_cache()
    torch._dynamo.reset()

# and show the figure
ber_plot128(ylim=(1e-5, 1), show_bler=False) # we set the ylim to 1e-5 as otherwise more extensive simulations would be required for accurate curves.


Running: 5G LDPC BP-20
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6821e-01 | 8.6460e-01 |      107653 |      640000 |         8646 |       10000 |        15.4 |reached target block errors
      0.5 | 1.2717e-01 | 7.0900e-01 |       81391 |      640000 |         7090 |       10000 |         3.1 |reached target block errors
      1.0 | 8.5353e-02 | 4.9720e-01 |       54626 |      640000 |         4972 |       10000 |         0.0 |reached target block errors
      1.5 | 5.3012e-02 | 3.1060e-01 |       33928 |      640000 |         3106 |       10000 |         0.0 |reached target block errors
      2.0 | 2.4139e-02 | 1.4770e-01 |       15449 |      640000 |         1477 |       10000 |         0.0 |reached target block errors
      2.5 | 1.0451e-02 | 6.5650e-02 |       13377 |     1280000 |         1313 |       20000 |         0.0 |reached target block errors
      3.0 | 3.5434e-03 | 2.2100e-02 |       11339 |     3200000 |         1105 |       50000 |         0.0 |reached target block errors
      3.5 | 9.4715e-04 | 6.0412e-03 |       10305 |    10880000 |         1027 |      170000 |         0.2 |reached target block errors
      4.0 | 2.0342e-04 | 1.2696e-03 |       10285 |    50560000 |         1003 |      790000 |         0.8 |reached target block errors
      4.5 | 3.6453e-05 | 2.3800e-04 |        2333 |    64000000 |          238 |     1000000 |         1.0 |reached max iterations

Running: 5G Polar+CRC SC
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 4.0707e-01 | 9.5050e-01 |      260522 |      640000 |         9505 |       10000 |        31.9 |reached target block errors
      0.5 | 3.7400e-01 | 9.0650e-01 |      239362 |      640000 |         9065 |       10000 |        10.5 |reached target block errors
      1.0 | 3.0821e-01 | 7.8720e-01 |      197257 |      640000 |         7872 |       10000 |         0.0 |reached target block errors
      1.5 | 2.4196e-01 | 6.4960e-01 |      154853 |      640000 |         6496 |       10000 |         0.0 |reached target block errors
      2.0 | 1.7554e-01 | 4.8330e-01 |      112346 |      640000 |         4833 |       10000 |         0.0 |reached target block errors
      2.5 | 1.0713e-01 | 3.0890e-01 |       68561 |      640000 |         3089 |       10000 |         0.0 |reached target block errors
      3.0 | 6.0444e-02 | 1.7500e-01 |       38684 |      640000 |         1750 |       10000 |         0.0 |reached target block errors
      3.5 | 2.7729e-02 | 8.2150e-02 |       35493 |     1280000 |         1643 |       20000 |         0.0 |reached target block errors
      4.0 | 9.9055e-03 | 3.0450e-02 |       25358 |     2560000 |         1218 |       40000 |         0.0 |reached target block errors
      4.5 | 3.0332e-03 | 9.4000e-03 |       21354 |     7040000 |         1034 |      110000 |         0.0 |reached target block errors

Running: 5G Polar+CRC SCL-8
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 3.4075e-01 | 7.9510e-01 |      218078 |      640000 |         7951 |       10000 |        56.1 |reached target block errors
      0.5 | 2.6145e-01 | 6.3100e-01 |      167326 |      640000 |         6310 |       10000 |        34.0 |reached target block errors
      1.0 | 1.7330e-01 | 4.3690e-01 |      110910 |      640000 |         4369 |       10000 |         0.0 |reached target block errors
      1.5 | 9.3823e-02 | 2.4710e-01 |       60047 |      640000 |         2471 |       10000 |         0.0 |reached target block errors
      2.0 | 3.9795e-02 | 1.0710e-01 |       25469 |      640000 |         1071 |       10000 |         0.0 |reached target block errors
      2.5 | 1.2889e-02 | 3.6067e-02 |       24746 |     1920000 |         1082 |       30000 |         0.0 |reached target block errors
      3.0 | 2.8276e-03 | 8.3833e-03 |       21716 |     7680000 |         1006 |      120000 |         0.2 |reached target block errors
      3.5 | 4.8486e-04 | 1.4838e-03 |       21101 |    43520000 |         1009 |      680000 |         1.0 |reached target block errors
      4.0 | 5.8906e-05 | 1.8800e-04 |        3770 |    64000000 |          188 |     1000000 |         1.5 |reached max iterations
      4.5 | 3.3594e-06 | 1.0000e-05 |         215 |    64000000 |           10 |     1000000 |         1.5 |reached max iterations

Running: Reed Muller (RM) SCL-8
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 2.6935e-01 | 6.4410e-01 |      172384 |      640000 |         6441 |       10000 |        40.4 |reached target block errors
      0.5 | 1.9199e-01 | 4.7370e-01 |      122873 |      640000 |         4737 |       10000 |        11.0 |reached target block errors
      1.0 | 1.1760e-01 | 2.9870e-01 |       75265 |      640000 |         2987 |       10000 |         0.0 |reached target block errors
      1.5 | 5.5808e-02 | 1.4710e-01 |       35717 |      640000 |         1471 |       10000 |         0.0 |reached target block errors
      2.0 | 2.2286e-02 | 5.9000e-02 |       28526 |     1280000 |         1180 |       20000 |         0.0 |reached target block errors
      2.5 | 7.7353e-03 | 2.0460e-02 |       24753 |     3200000 |         1023 |       50000 |         0.1 |reached target block errors
      3.0 | 1.7719e-03 | 4.8429e-03 |       23815 |    13440000 |         1017 |      210000 |         0.4 |reached target block errors
      3.5 | 2.7925e-04 | 7.6500e-04 |       17872 |    64000000 |          765 |     1000000 |         2.1 |reached max iterations
      4.0 | 2.8562e-05 | 8.0000e-05 |        1828 |    64000000 |           80 |     1000000 |         2.1 |reached max iterations
      4.5 | 3.3437e-06 | 1.0000e-05 |         214 |    64000000 |           10 |     1000000 |         2.1 |reached max iterations

Running: Conv. Code Viterbi (constraint length 8)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 4.7197e-01 | 1.0000e+00 |      302058 |      640000 |        10000 |       10000 |         4.2 |reached target block errors
      0.5 | 4.7041e-01 | 1.0000e+00 |      301062 |      640000 |        10000 |       10000 |         0.8 |reached target block errors
      1.0 | 4.6459e-01 | 9.9990e-01 |      297335 |      640000 |         9999 |       10000 |         0.1 |reached target block errors
      1.5 | 4.5974e-01 | 1.0000e+00 |      294236 |      640000 |        10000 |       10000 |         0.1 |reached target block errors
      2.0 | 4.5528e-01 | 9.9970e-01 |      291379 |      640000 |         9997 |       10000 |         0.1 |reached target block errors
      2.5 | 4.5026e-01 | 9.9980e-01 |      288164 |      640000 |         9998 |       10000 |         0.1 |reached target block errors
      3.0 | 4.4340e-01 | 9.9910e-01 |      283778 |      640000 |         9991 |       10000 |         0.1 |reached target block errors
      3.5 | 4.3819e-01 | 9.9840e-01 |      280441 |      640000 |         9984 |       10000 |         0.1 |reached target block errors
      4.0 | 4.2964e-01 | 9.9830e-01 |      274967 |      640000 |         9983 |       10000 |         0.1 |reached target block errors
      4.5 | 4.1981e-01 | 9.9760e-01 |      268680 |      640000 |         9976 |       10000 |         0.1 |reached target block errors

Running: Turbo Code (constraint length 4)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.0968e-01 | 7.9510e-01 |       70198 |      640000 |         7951 |       10000 |         2.2 |reached target block errors
      0.5 | 7.7464e-02 | 6.1270e-01 |       49577 |      640000 |         6127 |       10000 |         1.1 |reached target block errors
      1.0 | 4.6291e-02 | 3.9800e-01 |       29626 |      640000 |         3980 |       10000 |         0.5 |reached target block errors
      1.5 | 2.3995e-02 | 2.1800e-01 |       15357 |      640000 |         2180 |       10000 |         0.5 |reached target block errors
      2.0 | 9.5492e-03 | 9.0900e-02 |       12223 |     1280000 |         1818 |       20000 |         1.0 |reached target block errors
      2.5 | 3.2336e-03 | 3.2125e-02 |        8278 |     2560000 |         1285 |       40000 |         2.0 |reached target block errors
      3.0 | 8.0440e-04 | 9.5909e-03 |        5663 |     7040000 |         1055 |      110000 |         5.6 |reached target block errors
      3.5 | 1.6490e-04 | 2.4537e-03 |        4327 |    26240000 |         1006 |      410000 |        21.0 |reached target block errors
      4.0 | 3.3531e-05 | 6.7300e-04 |        2146 |    64000000 |          673 |     1000000 |        51.6 |reached max iterations
      4.5 | 6.9219e-06 | 2.1000e-04 |         443 |    64000000 |          210 |     1000000 |        51.4 |reached max iterations
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_12_1.png

And let’s also look at the block-error-rate.

[7]:
ber_plot128(ylim=(1e-5, 1), show_ber=False)
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_14_0.png

Please keep in mind that the decoding complexity differs significantly and should be also included in a fair comparison as shown in Section Throughput and Decoding Complexity.

Performance under Optimal Decoding#

The achievable error-rate performance of a coding scheme depends on the strength of the code construction and the performance of the actual decoding algorithm. We now approximate the maximum-likelihood performance of all previous coding schemes by using the ordered statistics decoder (OSD) [12].

[8]:
# overwrite existing legend entries for OSD simulations
legends = ["5G LDPC", "5G Polar+CRC", "5G Polar+CRC", "RM", "Conv. Code", "Turbo Code"]

# run ber simulations for each code we have added to the list
for idx, code in enumerate(codes_under_test):

    if idx==2: # skip second polar code (same code only different decoder)
        continue

    print("\nRunning: " + code[2])

    # initialize encoder
    encoder = code[0]
    # encode dummy bits to init conv encoders (otherwise k is not defined)
    encoder(torch.zeros((1, k)))

    # OSD can be directly associated to an encoder
    decoder = OSDecoder(encoder=encoder, t=4)

    # generate a new model with the given encoder/decoder
    model = System_Model(k=k,
                         n=n,
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=encoder,
                         decoder=decoder,
                         cw_estimates=True) # OSD returns codeword estimates and not info bit estimates

    # the first argument must be a callable (function) that yields u and u_hat for batch_size and ebno
    ber_plot128.simulate(model,
                         ebno_dbs=ebno_db, # SNR to simulate
                         legend=legends[idx]+f" OSD-{decoder.t} ", # legend string for plotting
                         max_mc_iter=1000, # run 100 Monte Carlo runs per SNR point
                         num_target_block_errors=1000, # continue with next SNR point after 1000 bit errors
                         batch_size=32, # batch-size per Monte Carlo run
                         soft_estimates=False, # the model returns hard-estimates
                         early_stop=True, # stop simulation if no error has been detected at current SNR point
                         show_fig=False, # we show the figure after all results are simulated
                         add_bler=True, # in case BLER is also interesting
                         compile_mode="default",
                         forward_keyboard_interrupt=True); # should be True in a loop

    torch.cuda.empty_cache()
    torch._dynamo.reset()


Running: 5G LDPC BP-20
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.0030e-01 | 4.3924e-01 |       29580 |      294912 |         1012 |        2304 |       264.7 |reached target block errors
      0.5 | 5.7758e-02 | 2.6536e-01 |       27916 |      483328 |         1002 |        3776 |        15.2 |reached target block errors
      1.0 | 2.5908e-02 | 1.2231e-01 |       27166 |     1048576 |         1002 |        8192 |        33.0 |reached target block errors
      1.5 | 8.5671e-03 | 4.2444e-02 |       25862 |     3018752 |         1001 |       23584 |        95.3 |reached target block errors
      2.0 | 2.1389e-03 | 1.1062e-02 |        8761 |     4096000 |          354 |       32000 |       129.5 |reached max iterations
      2.5 | 3.7695e-04 | 2.1250e-03 |        1544 |     4096000 |           68 |       32000 |       129.5 |reached max iterations
      3.0 | 7.9346e-05 | 4.6875e-04 |         325 |     4096000 |           15 |       32000 |       129.5 |reached max iterations
      3.5 | 9.0332e-06 | 6.2500e-05 |          37 |     4096000 |            2 |       32000 |       129.5 |reached max iterations
      4.0 | 0.0000e+00 | 0.0000e+00 |           0 |     4096000 |            0 |       32000 |       129.5 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 4.0 dB.


Running: 5G Polar+CRC SC
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.0599e-01 | 4.4190e-01 |       30824 |      290816 |         1004 |        2272 |       264.8 |reached target block errors
      0.5 | 5.7971e-02 | 2.4729e-01 |       30156 |      520192 |         1005 |        4064 |        16.4 |reached target block errors
      1.0 | 2.3966e-02 | 1.0368e-01 |       29646 |     1236992 |         1002 |        9664 |        39.0 |reached target block errors
      1.5 | 7.6334e-03 | 3.4004e-02 |       28734 |     3764224 |         1000 |       29408 |       119.1 |reached target block errors
      2.0 | 1.6938e-03 | 7.7812e-03 |        6938 |     4096000 |          249 |       32000 |       129.6 |reached max iterations
      2.5 | 2.7148e-04 | 1.3437e-03 |        1112 |     4096000 |           43 |       32000 |       129.7 |reached max iterations
      3.0 | 1.6602e-05 | 9.3750e-05 |          68 |     4096000 |            3 |       32000 |       129.7 |reached max iterations
      3.5 | 0.0000e+00 | 0.0000e+00 |           0 |     4096000 |            0 |       32000 |       129.6 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 3.5 dB.


Running: Reed Muller (RM) SCL-8
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.0135e-01 | 4.8846e-01 |       26984 |      266240 |         1016 |        2080 |        68.1 |reached target block errors
      0.5 | 6.0596e-02 | 3.1250e-01 |       24820 |      409600 |         1000 |        3200 |        13.0 |reached target block errors
      1.0 | 2.7744e-02 | 1.5305e-01 |       23296 |      839680 |         1004 |        6560 |        26.6 |reached target block errors
      1.5 | 9.5584e-03 | 5.7714e-02 |       21220 |     2220032 |         1001 |       17344 |        70.3 |reached target block errors
      2.0 | 2.7520e-03 | 1.7531e-02 |       11272 |     4096000 |          561 |       32000 |       129.7 |reached max iterations
      2.5 | 6.4551e-04 | 4.6250e-03 |        2644 |     4096000 |          148 |       32000 |       130.1 |reached max iterations
      3.0 | 8.2031e-05 | 5.6250e-04 |         336 |     4096000 |           18 |       32000 |       129.8 |reached max iterations
      3.5 | 2.3438e-05 | 1.8750e-04 |          96 |     4096000 |            6 |       32000 |       129.8 |reached max iterations
      4.0 | 0.0000e+00 | 0.0000e+00 |           0 |     4096000 |            0 |       32000 |       129.8 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 4.0 dB.


Running: Conv. Code Viterbi (constraint length 8)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 9.6902e-02 | 7.1042e-01 |       17861 |      184320 |         1023 |        1440 |       372.6 |reached target block errors
      0.5 | 6.2066e-02 | 5.3125e-01 |       14999 |      241664 |         1003 |        1888 |         8.5 |reached target block errors
      1.0 | 3.7562e-02 | 4.1667e-01 |       11539 |      307200 |         1000 |        2400 |        10.7 |reached target block errors
      1.5 | 1.9492e-02 | 2.8322e-01 |        8862 |      454656 |         1006 |        3552 |        15.9 |reached target block errors
      2.0 | 1.0224e-02 | 1.8382e-01 |        7119 |      696320 |         1000 |        5440 |        24.3 |reached target block errors
      2.5 | 5.0953e-03 | 1.1927e-01 |        5468 |     1073152 |         1000 |        8384 |        37.5 |reached target block errors
      3.0 | 2.6560e-03 | 7.8993e-02 |        4308 |     1622016 |         1001 |       12672 |        56.7 |reached target block errors
      3.5 | 1.4899e-03 | 4.8573e-02 |        3930 |     2637824 |         1001 |       20608 |        92.2 |reached target block errors
      4.0 | 8.1876e-04 | 3.1758e-02 |        3300 |     4030464 |         1000 |       31488 |       141.1 |reached target block errors
      4.5 | 3.8965e-04 | 1.7094e-02 |        1596 |     4096000 |          547 |       32000 |       143.3 |reached max iterations

Running: Turbo Code (constraint length 4)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.0344e-01 | 5.1562e-01 |       26270 |      253952 |         1023 |        1984 |        17.9 |reached target block errors
      0.5 | 6.3742e-02 | 3.4001e-01 |       24020 |      376832 |         1001 |        2944 |        13.9 |reached target block errors
      1.0 | 3.3226e-02 | 1.9210e-01 |       22183 |      667648 |         1002 |        5216 |        24.7 |reached target block errors
      1.5 | 1.3055e-02 | 8.5553e-02 |       19571 |     1499136 |         1002 |       11712 |        55.5 |reached target block errors
      2.0 | 3.9678e-03 | 3.0344e-02 |       16252 |     4096000 |          971 |       32000 |       151.5 |reached max iterations
      2.5 | 1.0469e-03 | 1.0219e-02 |        4288 |     4096000 |          327 |       32000 |       151.5 |reached max iterations
      3.0 | 2.8735e-04 | 4.0000e-03 |        1177 |     4096000 |          128 |       32000 |       151.6 |reached max iterations
      3.5 | 6.0059e-05 | 1.0625e-03 |         246 |     4096000 |           34 |       32000 |       151.5 |reached max iterations
      4.0 | 1.4404e-05 | 3.1250e-04 |          59 |     4096000 |           10 |       32000 |       151.5 |reached max iterations
      4.5 | 1.2695e-05 | 3.1250e-04 |          52 |     4096000 |           10 |       32000 |       151.5 |reached max iterations

And let’s plot the results.

Remark: we define a custom plotting function to enable a nicer visualization of OSD vs. non-OSD results.

[9]:
# for simplicity, we only plot a subset of the simulated curves
# focus on BLER
plots_to_show = ['5G LDPC BP-20 (BLER)', '5G LDPC OSD-4  (BLER)', '5G Polar+CRC SCL-8 (BLER)', '5G Polar+CRC OSD-4  (BLER)', 'Reed Muller (RM) SCL-8 (BLER)', 'RM OSD-4  (BLER)', 'Conv. Code Viterbi (constraint length 8) (BLER)', 'Conv. Code OSD-4  (BLER)', 'Turbo Code (constraint length 4) (BLER)', 'Turbo Code OSD-4  (BLER)']

# find indices of relevant curves
idx = []
for p in plots_to_show:
    for i,l in enumerate(ber_plot128._legends):
        if p==l:
            idx.append(i)

# generate new figure
fig, ax = plt.subplots(figsize=(16,12))
plt.xticks(fontsize=18)
plt.yticks(fontsize=18)
plt.title(f"Performance under Ordered Statistic Decoding (k={k},n={n})", fontsize=25)
plt.grid(which="both")
plt.xlabel(r"$E_b/N_0$ (dB)", fontsize=25)
plt.ylabel(r"BLER", fontsize=25)

# plot pairs of BLER curves (non-osd vs. osd)
for i in range(int(len(idx)/2)):

    # non-OSD
    plt.semilogy(ebno_db,
                 ber_plot128._bers[idx[2*i]],
                 c='C%d'%(i),
                 label=ber_plot128._legends[idx[2*i]].replace(" (BLER)", ""), #remove "(BLER)" from label
                 linewidth=2)
    # OSD
    plt.semilogy(ebno_db,
                 ber_plot128._bers[idx[2*i+1]],
                 c='C%d'%(i),
                 label= ber_plot128._legends[idx[2*i+1]].replace(" (BLER)", ""), #remove "(BLER)" from label
                 linestyle = "--",
                 linewidth=2)

plt.legend(fontsize=20)
plt.xlim([0, 4.5])
plt.ylim([1e-4, 1]);

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_19_0.png

As can be seen, the performance of Polar and Convolutional codes is in practice close to their ML performance. For other codes such as LDPC codes, there is a practical performance gap under BP decoding which tends to be smaller for longer codes.

Performance of Longer LDPC Codes#

Now, let us have a look at the performance gains due to longer codewords. For this, we scale the length of the LDPC code and compare the results (same rate, same decoder, same channel).

[10]:
# init new figure
ber_plot_ldpc = PlotBER(f"BER/BLER Performance of LDPC Codes @ Fixed Rate=0.5")
[11]:
# code parameters to simulate
ns = [128, 256, 512, 1000, 2000, 4000, 8000, 16000]  # number of codeword bits per codeword
rate = 0.5 # fixed coderate

# create list of encoder/decoder pairs to be analyzed
codes_under_test = []

# 5G LDPC codes
for n in ns:
    k = int(rate*n) # calculate k for given n and rate
    enc = LDPC5GEncoder(k=k, n=n)
    dec = LDPC5GDecoder(enc, num_iter=20)
    name = f"5G LDPC BP-20 (n={n})"
    codes_under_test.append([enc, dec, name, k, n])

[12]:
# and simulate the results
num_bits_per_symbol = 2 # QPSK

ebno_db = np.arange(0, 5, 0.25) # sim SNR range
# note that the waterfall for long codes can be steep and requires a fine
# SNR quantization

# run ber simulations for each case
for code in codes_under_test:
    print("Running: " + code[2])
    model = System_Model(k=code[3],
                         n=code[4],
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=code[0],
                         decoder=code[1])

    # the first argument must be a callable (function) that yields u and u_hat
    # for given batch_size and ebno
    # we fix the target number of BLOCK errors instead of the BER to
    # ensure that same accurate results for each block lengths is simulated
    ber_plot_ldpc.simulate(model, # the function have defined previously
                           ebno_dbs=ebno_db,
                           legend=code[2],
                           max_mc_iter=100,
                           num_target_block_errors=500, # we fix the target block errors
                           batch_size=1000,
                           soft_estimates=False,
                           early_stop=True,
                           show_fig=False,
                           compile_mode="default",
                           forward_keyboard_interrupt=True); # should be True in a loop

# and show figure
ber_plot_ldpc(ylim=(1e-5, 1))
Running: 5G LDPC BP-20 (n=128)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.7177e-01 | 8.5500e-01 |       10993 |       64000 |          855 |        1000 |        15.3 |reached target block errors
     0.25 | 1.5150e-01 | 7.9900e-01 |        9696 |       64000 |          799 |        1000 |         3.1 |reached target block errors
      0.5 | 1.2881e-01 | 7.2200e-01 |        8244 |       64000 |          722 |        1000 |         0.0 |reached target block errors
     0.75 | 1.0684e-01 | 6.1800e-01 |        6838 |       64000 |          618 |        1000 |         0.0 |reached target block errors
      1.0 | 8.6836e-02 | 4.9600e-01 |       11115 |      128000 |          992 |        2000 |         0.0 |reached target block errors
     1.25 | 6.8875e-02 | 4.0800e-01 |        8816 |      128000 |          816 |        2000 |         0.0 |reached target block errors
      1.5 | 5.2125e-02 | 3.0300e-01 |        6672 |      128000 |          606 |        2000 |         0.0 |reached target block errors
     1.75 | 3.6302e-02 | 2.1667e-01 |        6970 |      192000 |          650 |        3000 |         0.0 |reached target block errors
      2.0 | 2.6293e-02 | 1.6150e-01 |        6731 |      256000 |          646 |        4000 |         0.0 |reached target block errors
     2.25 | 1.7006e-02 | 1.0500e-01 |        5442 |      320000 |          525 |        5000 |         0.0 |reached target block errors
      2.5 | 1.0713e-02 | 6.8375e-02 |        5485 |      512000 |          547 |        8000 |         0.0 |reached target block errors
     2.75 | 6.8607e-03 | 4.1833e-02 |        5269 |      768000 |          502 |       12000 |         0.0 |reached target block errors
      3.0 | 3.4967e-03 | 2.1458e-02 |        5371 |     1536000 |          515 |       24000 |         0.0 |reached target block errors
     3.25 | 1.9879e-03 | 1.2600e-02 |        5089 |     2560000 |          504 |       40000 |         0.1 |reached target block errors
      3.5 | 9.7801e-04 | 6.1852e-03 |        5070 |     5184000 |          501 |       81000 |         0.2 |reached target block errors
     3.75 | 4.0266e-04 | 2.7400e-03 |        2577 |     6400000 |          274 |      100000 |         0.2 |reached max iterations
      4.0 | 2.1687e-04 | 1.4200e-03 |        1388 |     6400000 |          142 |      100000 |         0.2 |reached max iterations
     4.25 | 8.0156e-05 | 4.8000e-04 |         513 |     6400000 |           48 |      100000 |         0.2 |reached max iterations
      4.5 | 4.6563e-05 | 2.7000e-04 |         298 |     6400000 |           27 |      100000 |         0.2 |reached max iterations
     4.75 | 2.1406e-05 | 1.2000e-04 |         137 |     6400000 |           12 |      100000 |         0.2 |reached max iterations
Running: 5G LDPC BP-20 (n=256)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6577e-01 | 9.2400e-01 |       21219 |      128000 |          924 |        1000 |        19.5 |reached target block errors
     0.25 | 1.4409e-01 | 8.6400e-01 |       18443 |      128000 |          864 |        1000 |        17.1 |reached target block errors
      0.5 | 1.2023e-01 | 7.7300e-01 |       15389 |      128000 |          773 |        1000 |         0.0 |reached target block errors
     0.75 | 9.9305e-02 | 6.4700e-01 |       12711 |      128000 |          647 |        1000 |         0.0 |reached target block errors
      1.0 | 6.9344e-02 | 4.9800e-01 |       17752 |      256000 |          996 |        2000 |         0.0 |reached target block errors
     1.25 | 4.6266e-02 | 3.4500e-01 |       11844 |      256000 |          690 |        2000 |         0.0 |reached target block errors
      1.5 | 3.3934e-02 | 2.5200e-01 |        8687 |      256000 |          504 |        2000 |         0.0 |reached target block errors
     1.75 | 2.0166e-02 | 1.5600e-01 |       10325 |      512000 |          624 |        4000 |         0.0 |reached target block errors
      2.0 | 9.0435e-03 | 7.8571e-02 |        8103 |      896000 |          550 |        7000 |         0.0 |reached target block errors
     2.25 | 4.6803e-03 | 4.0538e-02 |        7788 |     1664000 |          527 |       13000 |         0.0 |reached target block errors
      2.5 | 2.0257e-03 | 1.8536e-02 |        7260 |     3584000 |          519 |       28000 |         0.1 |reached target block errors
     2.75 | 8.3484e-04 | 7.8594e-03 |        6839 |     8192000 |          503 |       64000 |         0.2 |reached target block errors
      3.0 | 3.0852e-04 | 3.0100e-03 |        3949 |    12800000 |          301 |      100000 |         0.3 |reached max iterations
     3.25 | 1.2063e-04 | 1.1800e-03 |        1544 |    12800000 |          118 |      100000 |         0.3 |reached max iterations
      3.5 | 3.8281e-05 | 3.7000e-04 |         490 |    12800000 |           37 |      100000 |         0.3 |reached max iterations
     3.75 | 7.0312e-06 | 1.3000e-04 |          90 |    12800000 |           13 |      100000 |         0.3 |reached max iterations
      4.0 | 4.1406e-06 | 2.0000e-05 |          53 |    12800000 |            2 |      100000 |         0.3 |reached max iterations
     4.25 | 1.2500e-06 | 1.0000e-05 |          16 |    12800000 |            1 |      100000 |         0.3 |reached max iterations
      4.5 | 0.0000e+00 | 0.0000e+00 |           0 |    12800000 |            0 |      100000 |         0.3 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 4.5 dB.

Running: 5G LDPC BP-20 (n=512)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6061e-01 | 9.7800e-01 |       41117 |      256000 |          978 |        1000 |        18.5 |reached target block errors
     0.25 | 1.3880e-01 | 9.3000e-01 |       35532 |      256000 |          930 |        1000 |        17.1 |reached target block errors
      0.5 | 1.1349e-01 | 8.3200e-01 |       29054 |      256000 |          832 |        1000 |         0.0 |reached target block errors
     0.75 | 8.1687e-02 | 6.6100e-01 |       20912 |      256000 |          661 |        1000 |         0.0 |reached target block errors
      1.0 | 4.9289e-02 | 4.5950e-01 |       25236 |      512000 |          919 |        2000 |         0.0 |reached target block errors
     1.25 | 2.5955e-02 | 2.6950e-01 |       13289 |      512000 |          539 |        2000 |         0.0 |reached target block errors
      1.5 | 1.4405e-02 | 1.5100e-01 |       14751 |     1024000 |          604 |        4000 |         0.0 |reached target block errors
     1.75 | 5.3232e-03 | 6.2500e-02 |       10902 |     2048000 |          500 |        8000 |         0.0 |reached target block errors
      2.0 | 2.0412e-03 | 2.3455e-02 |       11496 |     5632000 |          516 |       22000 |         0.1 |reached target block errors
     2.25 | 5.6345e-04 | 7.2143e-03 |       10097 |    17920000 |          505 |       70000 |         0.3 |reached target block errors
      2.5 | 1.5402e-04 | 2.0300e-03 |        3943 |    25600000 |          203 |      100000 |         0.5 |reached max iterations
     2.75 | 2.3477e-05 | 3.4000e-04 |         601 |    25600000 |           34 |      100000 |         0.5 |reached max iterations
      3.0 | 8.5547e-06 | 1.6000e-04 |         219 |    25600000 |           16 |      100000 |         0.5 |reached max iterations
     3.25 | 9.7656e-07 | 3.0000e-05 |          25 |    25600000 |            3 |      100000 |         0.5 |reached max iterations
      3.5 | 5.4687e-07 | 2.0000e-05 |          14 |    25600000 |            2 |      100000 |         0.5 |reached max iterations
     3.75 | 7.8125e-08 | 1.0000e-05 |           2 |    25600000 |            1 |      100000 |         0.5 |reached max iterations
      4.0 | 0.0000e+00 | 0.0000e+00 |           0 |    25600000 |            0 |      100000 |         0.5 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 4.0 dB.

Running: 5G LDPC BP-20 (n=1000)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6321e-01 | 9.9800e-01 |       81604 |      500000 |          998 |        1000 |        18.7 |reached target block errors
     0.25 | 1.4217e-01 | 9.8400e-01 |       71087 |      500000 |          984 |        1000 |        19.1 |reached target block errors
      0.5 | 1.0647e-01 | 9.1500e-01 |       53237 |      500000 |          915 |        1000 |         0.0 |reached target block errors
     0.75 | 6.9028e-02 | 7.4700e-01 |       34514 |      500000 |          747 |        1000 |         0.0 |reached target block errors
      1.0 | 3.4180e-02 | 4.5900e-01 |       34180 |     1000000 |          918 |        2000 |         0.0 |reached target block errors
     1.25 | 1.3535e-02 | 2.2267e-01 |       20303 |     1500000 |          668 |        3000 |         0.0 |reached target block errors
      1.5 | 3.4831e-03 | 7.2286e-02 |       12191 |     3500000 |          506 |        7000 |         0.1 |reached target block errors
     1.75 | 7.1331e-04 | 1.8034e-02 |       10343 |    14500000 |          523 |       29000 |         0.3 |reached target block errors
      2.0 | 1.2210e-04 | 3.5300e-03 |        6105 |    50000000 |          353 |      100000 |         1.0 |reached max iterations
     2.25 | 1.0320e-05 | 3.9000e-04 |         516 |    50000000 |           39 |      100000 |         1.0 |reached max iterations
      2.5 | 1.5800e-06 | 7.0000e-05 |          79 |    50000000 |            7 |      100000 |         1.0 |reached max iterations
     2.75 | 2.6000e-07 | 3.0000e-05 |          13 |    50000000 |            3 |      100000 |         1.0 |reached max iterations
      3.0 | 0.0000e+00 | 0.0000e+00 |           0 |    50000000 |            0 |      100000 |         1.0 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 3.0 dB.

Running: 5G LDPC BP-20 (n=2000)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6015e-01 | 1.0000e+00 |      160149 |     1000000 |         1000 |        1000 |         0.2 |reached target block errors
     0.25 | 1.3415e-01 | 9.9900e-01 |      134154 |     1000000 |          999 |        1000 |         0.1 |reached target block errors
      0.5 | 9.7863e-02 | 9.7600e-01 |       97863 |     1000000 |          976 |        1000 |         0.1 |reached target block errors
     0.75 | 5.4384e-02 | 8.0300e-01 |       54384 |     1000000 |          803 |        1000 |         0.1 |reached target block errors
      1.0 | 2.0400e-02 | 4.7100e-01 |       40801 |     2000000 |          942 |        2000 |         0.2 |reached target block errors
     1.25 | 4.5122e-03 | 1.5675e-01 |       18049 |     4000000 |          627 |        4000 |         0.5 |reached target block errors
      1.5 | 5.0690e-04 | 2.5700e-02 |       10138 |    20000000 |          514 |       20000 |         2.4 |reached target block errors
     1.75 | 2.5660e-05 | 2.0500e-03 |        2566 |   100000000 |          205 |      100000 |        13.1 |reached max iterations
      2.0 | 3.1000e-07 | 1.0000e-04 |          31 |   100000000 |           10 |      100000 |        17.0 |reached max iterations
     2.25 | 0.0000e+00 | 0.0000e+00 |           0 |   100000000 |            0 |      100000 |        15.8 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 2.2 dB.

Running: 5G LDPC BP-20 (n=4000)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.6143e-01 | 1.0000e+00 |      322856 |     2000000 |         1000 |        1000 |         0.3 |reached target block errors
     0.25 | 1.3848e-01 | 1.0000e+00 |      276958 |     2000000 |         1000 |        1000 |         0.3 |reached target block errors
      0.5 | 9.9780e-02 | 9.9800e-01 |      199561 |     2000000 |          998 |        1000 |         0.3 |reached target block errors
     0.75 | 5.1782e-02 | 9.2900e-01 |      103564 |     2000000 |          929 |        1000 |         0.3 |reached target block errors
      1.0 | 1.2115e-02 | 5.1600e-01 |       24231 |     2000000 |          516 |        1000 |         0.3 |reached target block errors
     1.25 | 1.0578e-03 | 1.0880e-01 |       10578 |    10000000 |          544 |        5000 |         1.6 |reached target block errors
      1.5 | 1.9505e-05 | 5.0808e-03 |        3862 |   198000000 |          503 |       99000 |        29.2 |reached target block errors
     1.75 | 2.4000e-07 | 1.5000e-04 |          48 |   200000000 |           15 |      100000 |        28.8 |reached max iterations
      2.0 | 0.0000e+00 | 0.0000e+00 |           0 |   200000000 |            0 |      100000 |        26.7 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 2.0 dB.

Running: 5G LDPC BP-20 (n=8000)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.3737e-01 | 1.0000e+00 |      549480 |     4000000 |         1000 |        1000 |         0.9 |reached target block errors
     0.25 | 1.1062e-01 | 1.0000e+00 |      442473 |     4000000 |         1000 |        1000 |         0.9 |reached target block errors
      0.5 | 7.0917e-02 | 1.0000e+00 |      283667 |     4000000 |         1000 |        1000 |         0.9 |reached target block errors
     0.75 | 2.7464e-02 | 9.6000e-01 |      109857 |     4000000 |          960 |        1000 |         0.9 |reached target block errors
      1.0 | 2.9976e-03 | 4.1050e-01 |       23981 |     8000000 |          821 |        2000 |         1.8 |reached target block errors
     1.25 | 4.0212e-05 | 1.9269e-02 |        4182 |   104000000 |          501 |       26000 |        23.8 |reached target block errors
      1.5 | 4.5000e-08 | 7.0000e-05 |          18 |   400000000 |            7 |      100000 |        88.3 |reached max iterations
     1.75 | 0.0000e+00 | 0.0000e+00 |           0 |   400000000 |            0 |      100000 |        86.4 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 1.8 dB.

Running: 5G LDPC BP-20 (n=16000)
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
      0.0 | 1.3725e-01 | 1.0000e+00 |     1098024 |     8000000 |         1000 |        1000 |         1.8 |reached target block errors
     0.25 | 1.1025e-01 | 1.0000e+00 |      881980 |     8000000 |         1000 |        1000 |         1.8 |reached target block errors
      0.5 | 7.2389e-02 | 1.0000e+00 |      579115 |     8000000 |         1000 |        1000 |         1.8 |reached target block errors
     0.75 | 2.4931e-02 | 9.9500e-01 |      199444 |     8000000 |          995 |        1000 |         1.8 |reached target block errors
      1.0 | 1.1863e-03 | 4.5400e-01 |       18980 |    16000000 |          908 |        2000 |         3.5 |reached target block errors
     1.25 | 1.8587e-06 | 3.9500e-03 |        1487 |   800000000 |          395 |      100000 |       175.7 |reached max iterations
      1.5 | 1.2500e-09 | 1.0000e-05 |           1 |   800000000 |            1 |      100000 |       175.6 |reached max iterations
     1.75 | 0.0000e+00 | 0.0000e+00 |           0 |   800000000 |            0 |      100000 |       174.6 |reached max iterations

Simulation stopped as no error occurred @ EbNo = 1.8 dB.

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_24_1.png

HARQ with 5G LDPC Codes#

Hybrid Automatic Repeat reQuest (HARQ) is a key mechanism in 5G NR that combines forward error correction with retransmissions. When a first transmission fails, the receiver requests a retransmission. Each transmission uses a redundancy version (RV) that selects a window of coded bits from the LDPC circular buffer (TS 38.212, Sec. 5.4.2.1). The standard defines four RVs (0-3), each starting at a different position in the buffer.

There are two main HARQ combining strategies:

  1. Chase combining - every (re)transmission sends the same RV (typically RV 0). The receiver accumulates LLRs from repeated observations of identical coded bits. This is equivalent to a repetition code and provides an SNR gain but no additional coding gain.

  2. Incremental Redundancy (IR) - each retransmission uses a different RV, so the receiver sees new parity bits from a different region of the mother codeword. This effectively lowers the code rate and provides coding gains.

[13]:
# --- Shared HARQ setup ---
k_harq = 500
n_harq = 1000
num_bits_per_symbol = 2  # QPSK
target_bler = 1e-4

enc_harq = LDPC5GEncoder(k_harq, n_harq)
dec_harq = LDPC5GDecoder(enc_harq, num_iter=20, harq_mode=True)

source = BinarySource()
constellation = Constellation("qam", num_bits_per_symbol=num_bits_per_symbol)
mapper = Mapper(constellation=constellation)
demapper = Demapper("app", constellation=constellation)
channel = AWGN()

def run_harq_sim(rv_list, coderate):
    """Create a simulation callable for PlotBER.simulate."""
    @torch.no_grad()
    def sim(batch_size, ebno_db):
        no = ebnodb2no(ebno_db,
                       num_bits_per_symbol=num_bits_per_symbol,
                       coderate=coderate)
        u = source([batch_size, k_harq])
        c = enc_harq(u, rv=rv_list)
        x = mapper(c)
        y = channel(x, no)
        llr = demapper(y, no)
        if len(rv_list) == 1:
            llr = llr.squeeze(-2)
        u_hat = dec_harq(llr, rv=rv_list)
        return u, u_hat
    return sim

# --- Chase combining (repeat rv=0) over Eb/N0 ---
chase_configs = [
    ("Baseline",    [0]),
    ("Chase: [rv0, rv0]",    [0, 0]),
    ("Chase: [rv0, rv0, rv0]",    [0, 0, 0]),
    ("Chase: [rv0, rv0, rv0, rv0]",    [0, 0, 0, 0]),
]

ebno_db = np.arange(-2, 5, 0.25)
ber_harq = PlotBER(f"LDPC HARQ (k={k_harq}, n={n_harq}, QPSK)")

for label, rv_list in chase_configs:
    eff_rate = k_harq / (n_harq * len(rv_list))
    ber_harq.simulate(
        run_harq_sim(rv_list, coderate=eff_rate),
        ebno_dbs=ebno_db,
        batch_size=1000,
        num_target_block_errors=200,
        max_mc_iter=100,
        add_bler=True,
        target_bler=target_bler,
        show_fig=False,
        legend=f"{label}, r={eff_rate:.3f}",
    )

ber_harq(ylim=(target_bler, 1), show_bler=True, show_ber=False);
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.5747e-01 | 1.0000e+00 |      128735 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.4966e-01 | 1.0000e+00 |      124829 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.4213e-01 | 1.0000e+00 |      121065 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.3269e-01 | 1.0000e+00 |      116347 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 2.2283e-01 | 1.0000e+00 |      111414 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 2.1172e-01 | 1.0000e+00 |      105858 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.9812e-01 | 1.0000e+00 |       99058 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.25 | 1.8228e-01 | 1.0000e+00 |       91142 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
      0.0 | 1.6431e-01 | 9.9900e-01 |       82154 |      500000 |          999 |        1000 |         0.3 |reached target block errors
     0.25 | 1.3870e-01 | 9.8700e-01 |       69350 |      500000 |          987 |        1000 |         0.3 |reached target block errors
      0.5 | 1.0748e-01 | 9.1700e-01 |       53741 |      500000 |          917 |        1000 |         0.3 |reached target block errors
     0.75 | 6.9560e-02 | 7.3100e-01 |       34780 |      500000 |          731 |        1000 |         0.3 |reached target block errors
      1.0 | 3.2890e-02 | 4.5700e-01 |       16445 |      500000 |          457 |        1000 |         0.3 |reached target block errors
     1.25 | 1.2476e-02 | 2.1000e-01 |        6238 |      500000 |          210 |        1000 |         0.3 |reached target block errors
      1.5 | 3.5900e-03 | 7.9000e-02 |        5385 |     1500000 |          237 |        3000 |         0.8 |reached target block errors
     1.75 | 8.0709e-04 | 1.8455e-02 |        4439 |     5500000 |          203 |       11000 |         3.1 |reached target block errors
      2.0 | 9.9176e-05 | 2.9559e-03 |        3372 |    34000000 |          201 |       68000 |        20.4 |reached target block errors
     2.25 | 1.8780e-05 | 5.6000e-04 |         939 |    50000000 |           56 |      100000 |        31.4 |reached max iterations
      2.5 | 1.3600e-06 | 8.0000e-05 |          68 |    50000000 |            8 |      100000 |        29.6 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 2.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.5828e-01 | 1.0000e+00 |      129138 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.5063e-01 | 1.0000e+00 |      125314 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.4004e-01 | 1.0000e+00 |      120019 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.3136e-01 | 1.0000e+00 |      115682 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 2.2236e-01 | 1.0000e+00 |      111180 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 2.1077e-01 | 1.0000e+00 |      105387 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.9761e-01 | 1.0000e+00 |       98805 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.25 | 1.8335e-01 | 1.0000e+00 |       91676 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
      0.0 | 1.6356e-01 | 9.9900e-01 |       81780 |      500000 |          999 |        1000 |         0.3 |reached target block errors
     0.25 | 1.4238e-01 | 9.8800e-01 |       71191 |      500000 |          988 |        1000 |         0.3 |reached target block errors
      0.5 | 1.0605e-01 | 9.0600e-01 |       53026 |      500000 |          906 |        1000 |         0.3 |reached target block errors
     0.75 | 6.9656e-02 | 7.1100e-01 |       34828 |      500000 |          711 |        1000 |         0.3 |reached target block errors
      1.0 | 3.4546e-02 | 4.5100e-01 |       17273 |      500000 |          451 |        1000 |         0.3 |reached target block errors
     1.25 | 1.0896e-02 | 2.0600e-01 |        5448 |      500000 |          206 |        1000 |         0.3 |reached target block errors
      1.5 | 3.4993e-03 | 7.3000e-02 |        5249 |     1500000 |          219 |        3000 |         0.9 |reached target block errors
     1.75 | 9.0600e-04 | 1.9273e-02 |        4983 |     5500000 |          212 |       11000 |         3.3 |reached target block errors
      2.0 | 9.2094e-05 | 3.1563e-03 |        2947 |    32000000 |          202 |       64000 |        19.1 |reached target block errors
     2.25 | 1.3860e-05 | 5.3000e-04 |         693 |    50000000 |           53 |      100000 |        29.7 |reached max iterations
      2.5 | 3.1600e-06 | 8.0000e-05 |         158 |    50000000 |            8 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 2.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.5859e-01 | 1.0000e+00 |      129297 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.4942e-01 | 1.0000e+00 |      124711 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.4162e-01 | 1.0000e+00 |      120809 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.3017e-01 | 1.0000e+00 |      115087 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 2.2211e-01 | 1.0000e+00 |      111056 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 2.1055e-01 | 1.0000e+00 |      105274 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.9775e-01 | 1.0000e+00 |       98876 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.25 | 1.8469e-01 | 1.0000e+00 |       92347 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
      0.0 | 1.6528e-01 | 9.9700e-01 |       82638 |      500000 |          997 |        1000 |         0.3 |reached target block errors
     0.25 | 1.3759e-01 | 9.8500e-01 |       68796 |      500000 |          985 |        1000 |         0.3 |reached target block errors
      0.5 | 1.0543e-01 | 9.0000e-01 |       52716 |      500000 |          900 |        1000 |         0.3 |reached target block errors
     0.75 | 6.7514e-02 | 7.2300e-01 |       33757 |      500000 |          723 |        1000 |         0.3 |reached target block errors
      1.0 | 3.4286e-02 | 4.6800e-01 |       17143 |      500000 |          468 |        1000 |         0.3 |reached target block errors
     1.25 | 1.3640e-02 | 2.2700e-01 |        6820 |      500000 |          227 |        1000 |         0.3 |reached target block errors
      1.5 | 3.3407e-03 | 6.8000e-02 |        5011 |     1500000 |          204 |        3000 |         0.9 |reached target block errors
     1.75 | 7.2636e-04 | 1.8909e-02 |        3995 |     5500000 |          208 |       11000 |         3.2 |reached target block errors
      2.0 | 1.3159e-04 | 3.4828e-03 |        3816 |    29000000 |          202 |       58000 |        17.1 |reached target block errors
     2.25 | 1.4720e-05 | 5.5000e-04 |         736 |    50000000 |           55 |      100000 |        29.5 |reached max iterations
      2.5 | 2.0200e-06 | 8.0000e-05 |         101 |    50000000 |            8 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 2.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.5851e-01 | 1.0000e+00 |      129255 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.4904e-01 | 1.0000e+00 |      124522 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.3962e-01 | 1.0000e+00 |      119812 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.3145e-01 | 1.0000e+00 |      115726 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 2.2245e-01 | 1.0000e+00 |      111224 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 2.1044e-01 | 1.0000e+00 |      105221 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.9940e-01 | 1.0000e+00 |       99701 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.25 | 1.8461e-01 | 1.0000e+00 |       92307 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
      0.0 | 1.6258e-01 | 9.9500e-01 |       81290 |      500000 |          995 |        1000 |         0.3 |reached target block errors
     0.25 | 1.3540e-01 | 9.7400e-01 |       67702 |      500000 |          974 |        1000 |         0.3 |reached target block errors
      0.5 | 1.0696e-01 | 9.1300e-01 |       53481 |      500000 |          913 |        1000 |         0.3 |reached target block errors
     0.75 | 6.8198e-02 | 7.3900e-01 |       34099 |      500000 |          739 |        1000 |         0.3 |reached target block errors
      1.0 | 3.2280e-02 | 4.3500e-01 |       16140 |      500000 |          435 |        1000 |         0.3 |reached target block errors
     1.25 | 1.2002e-02 | 2.2300e-01 |        6001 |      500000 |          223 |        1000 |         0.3 |reached target block errors
      1.5 | 3.5440e-03 | 7.3000e-02 |        5316 |     1500000 |          219 |        3000 |         0.9 |reached target block errors
     1.75 | 7.9491e-04 | 1.9909e-02 |        4372 |     5500000 |          219 |       11000 |         3.3 |reached target block errors
      2.0 | 1.0160e-04 | 3.1077e-03 |        3302 |    32500000 |          202 |       65000 |        19.2 |reached target block errors
     2.25 | 1.0160e-05 | 4.0000e-04 |         508 |    50000000 |           40 |      100000 |        29.6 |reached max iterations
      2.5 | 1.5400e-06 | 9.0000e-05 |          77 |    50000000 |            9 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 2.5 dB.

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_26_1.png

As the plot shows, all Chase combining curves overlap when accounting for the effective rates. This is because every retransmission repeats the exact same coded bits, acting as a repetition code on top of the LDPC code. The SNR gain from soft-combining two independent observations is ~3 dB, but there is no additional coding gain.

Chase combining has practical advantages: the receiver only needs a single LLR buffer of length n, and no circular-buffer re-indexing is required. This makes it attractive for low-complexity devices or latency-constrained scenarios.

Now let us compare this with incremental redundancy, where each retransmission provides new parity information.

[14]:
# --- Incremental Redundancy (different RVs) over Eb/N0 ---
ir_configs = [
    ("IR-HARQ: [rv0,rv2]",       [0, 2]),
    ("IR-HARQ: [rv0,rv2,rv3]",   [0, 2, 3]),
    ("IR-HARQ: [rv0,rv2,rv3,rv1]", [0, 2, 3, 1]),
]

for label, rv_list in ir_configs:
    eff_rate = k_harq / (n_harq * len(rv_list))
    ber_harq.simulate(
        run_harq_sim(rv_list, coderate=eff_rate),
        ebno_dbs=ebno_db,
        batch_size=1000,
        num_target_block_errors=200,
        max_mc_iter=100,
        add_bler=True,
        target_bler=target_bler,
        show_fig=False,
        legend=f"{label}, r={eff_rate:.3f}",
    )

ber_harq(ylim=(target_bler, 1), show_bler=True, show_ber=False);
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.5500e-01 | 1.0000e+00 |      127499 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.4342e-01 | 1.0000e+00 |      121711 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.2972e-01 | 1.0000e+00 |      114859 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.1430e-01 | 1.0000e+00 |      107151 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 1.9450e-01 | 1.0000e+00 |       97251 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 1.6764e-01 | 9.9600e-01 |       83821 |      500000 |          996 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.3729e-01 | 9.6900e-01 |       68646 |      500000 |          969 |        1000 |         0.3 |reached target block errors
    -0.25 | 8.9770e-02 | 8.5000e-01 |       44885 |      500000 |          850 |        1000 |         0.3 |reached target block errors
      0.0 | 5.1766e-02 | 6.2000e-01 |       25883 |      500000 |          620 |        1000 |         0.3 |reached target block errors
     0.25 | 2.3014e-02 | 3.9700e-01 |       11507 |      500000 |          397 |        1000 |         0.3 |reached target block errors
      0.5 | 7.9520e-03 | 1.5200e-01 |        7952 |     1000000 |          304 |        2000 |         0.6 |reached target block errors
     0.75 | 1.2423e-03 | 3.1571e-02 |        4348 |     3500000 |          221 |        7000 |         2.0 |reached target block errors
      1.0 | 1.8393e-04 | 6.6667e-03 |        2759 |    15000000 |          200 |       30000 |         8.7 |reached target block errors
     1.25 | 2.3300e-05 | 8.9000e-04 |        1165 |    50000000 |           89 |      100000 |        28.9 |reached max iterations
      1.5 | 2.1000e-06 | 1.4000e-04 |         105 |    50000000 |           14 |      100000 |        29.1 |reached max iterations
     1.75 | 4.4000e-07 | 2.0000e-05 |          22 |    50000000 |            2 |      100000 |        29.3 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 1.8 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.3275e-01 | 1.0000e+00 |      116374 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.2068e-01 | 1.0000e+00 |      110342 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.0506e-01 | 1.0000e+00 |      102532 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 1.8648e-01 | 1.0000e+00 |       93238 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.0 | 1.6395e-01 | 1.0000e+00 |       81976 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -0.75 | 1.3037e-01 | 9.8300e-01 |       65183 |      500000 |          983 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.0038e-01 | 9.4400e-01 |       50190 |      500000 |          944 |        1000 |         0.3 |reached target block errors
    -0.25 | 6.1244e-02 | 7.6600e-01 |       30622 |      500000 |          766 |        1000 |         0.3 |reached target block errors
      0.0 | 3.0320e-02 | 5.2100e-01 |       15160 |      500000 |          521 |        1000 |         0.3 |reached target block errors
     0.25 | 1.1984e-02 | 2.8200e-01 |        5992 |      500000 |          282 |        1000 |         0.3 |reached target block errors
      0.5 | 2.9493e-03 | 8.9000e-02 |        4424 |     1500000 |          267 |        3000 |         0.9 |reached target block errors
     0.75 | 6.0600e-04 | 2.2778e-02 |        2727 |     4500000 |          205 |        9000 |         2.7 |reached target block errors
      1.0 | 7.3878e-05 | 4.0816e-03 |        1810 |    24500000 |          200 |       49000 |        14.4 |reached target block errors
     1.25 | 1.2080e-05 | 8.3000e-04 |         604 |    50000000 |           83 |      100000 |        29.5 |reached max iterations
      1.5 | 2.0800e-06 | 1.7000e-04 |         104 |    50000000 |           17 |      100000 |        29.5 |reached max iterations
     1.75 | 8.0000e-08 | 1.0000e-05 |           4 |    50000000 |            1 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 1.8 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
     -2.0 | 2.7190e-01 | 1.0000e+00 |      135948 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.75 | 2.5818e-01 | 1.0000e+00 |      129090 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -1.5 | 2.4489e-01 | 1.0000e+00 |      122447 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
    -1.25 | 2.1934e-01 | 9.9900e-01 |      109668 |      500000 |          999 |        1000 |         0.3 |reached target block errors
     -1.0 | 1.9574e-01 | 9.9800e-01 |       97869 |      500000 |          998 |        1000 |         0.3 |reached target block errors
    -0.75 | 1.5529e-01 | 9.7900e-01 |       77647 |      500000 |          979 |        1000 |         0.3 |reached target block errors
     -0.5 | 1.1182e-01 | 8.8900e-01 |       55910 |      500000 |          889 |        1000 |         0.3 |reached target block errors
    -0.25 | 6.1474e-02 | 6.8600e-01 |       30737 |      500000 |          686 |        1000 |         0.3 |reached target block errors
      0.0 | 2.8706e-02 | 3.9500e-01 |       14353 |      500000 |          395 |        1000 |         0.3 |reached target block errors
     0.25 | 7.0200e-03 | 1.4450e-01 |        7020 |     1000000 |          289 |        2000 |         0.6 |reached target block errors
      0.5 | 1.6376e-03 | 4.3200e-02 |        4094 |     2500000 |          216 |        5000 |         1.5 |reached target block errors
     0.75 | 2.1014e-04 | 7.1429e-03 |        2942 |    14000000 |          200 |       28000 |         8.3 |reached target block errors
      1.0 | 2.1780e-05 | 1.0900e-03 |        1089 |    50000000 |          109 |      100000 |        29.6 |reached max iterations
     1.25 | 4.6000e-06 | 1.7000e-04 |         230 |    50000000 |           17 |      100000 |        29.6 |reached max iterations
      1.5 | 4.0000e-08 | 2.0000e-05 |           2 |    50000000 |            2 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = 1.5 dB.

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_28_1.png

Each new RV delivers coded bits from a different region of the LDPC mother codeword, effectively lowering the code rate from r=0.5 (1 RV) down to r=0.125 (4 RVs). The decoder sees more parity information which yields a true coding gain.

Finally, let us overlay both strategies in a non-rate adjusted SNR \(E_s/N_0\). This view shows the raw per-transmission power efficiency - how much each additional transmission helps at a fixed transmit power.

[15]:
# --- Figure 3: Chase vs IR over Es/N0 ---
esno_db = np.arange(-10, 6, 0.5)

ber_esno = PlotBER(f"Chase vs IR-HARQ over Es/N0 (k={k_harq}, n={n_harq}, QPSK)")

# Chase curves (dashed style via legend naming)
for label, rv_list in chase_configs:
    ber_esno.simulate(
        run_harq_sim(rv_list, coderate=1),  # rate=1 -> Es/N0
        ebno_dbs=esno_db,
        batch_size=1000,
        num_target_block_errors=200,
        max_mc_iter=100,
        add_bler=True,
        target_bler=target_bler,
        show_fig=False,
        legend=f"{label}",
    )

# IR curves
for label, rv_list in ir_configs:
    ber_esno.simulate(
        run_harq_sim(rv_list, coderate=1),  # rate=1 -> Es/N0
        ebno_dbs=esno_db,
        batch_size=1000,
        num_target_block_errors=200,
        max_mc_iter=100,
        add_bler=True,
        target_bler=target_bler,
        show_fig=False,
        legend=f"{label}",
    )

ber_esno(ylim=(target_bler, 1), show_bler=True, show_ber=False);
plt.xlabel("Es/N0 (dB)");
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 3.6821e-01 | 1.0000e+00 |      184107 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 3.5893e-01 | 1.0000e+00 |      179465 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 3.5086e-01 | 1.0000e+00 |      175431 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.5 | 3.4186e-01 | 1.0000e+00 |      170931 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.0 | 3.3140e-01 | 1.0000e+00 |      165702 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.5 | 3.2221e-01 | 1.0000e+00 |      161104 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.0 | 3.1107e-01 | 1.0000e+00 |      155535 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -6.5 | 2.9880e-01 | 1.0000e+00 |      149398 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -6.0 | 2.8664e-01 | 1.0000e+00 |      143322 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -5.5 | 2.7338e-01 | 1.0000e+00 |      136688 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -5.0 | 2.5746e-01 | 1.0000e+00 |      128728 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -4.5 | 2.4153e-01 | 1.0000e+00 |      120763 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -4.0 | 2.2114e-01 | 1.0000e+00 |      110571 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -3.5 | 1.9759e-01 | 1.0000e+00 |       98795 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -3.0 | 1.6322e-01 | 9.9800e-01 |       81608 |      500000 |          998 |        1000 |         0.3 |reached target block errors
     -2.5 | 1.0451e-01 | 9.1200e-01 |       52255 |      500000 |          912 |        1000 |         0.3 |reached target block errors
     -2.0 | 3.1306e-02 | 4.6000e-01 |       15653 |      500000 |          460 |        1000 |         0.3 |reached target block errors
     -1.5 | 3.0460e-03 | 6.6000e-02 |        6092 |     2000000 |          264 |        4000 |         1.1 |reached target block errors
     -1.0 | 1.1519e-04 | 2.9851e-03 |        3859 |    33500000 |          200 |       67000 |        20.6 |reached target block errors
     -0.5 | 1.5800e-06 | 9.0000e-05 |          79 |    50000000 |            9 |      100000 |        31.4 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -0.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 3.1147e-01 | 1.0000e+00 |      155735 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 3.0021e-01 | 1.0000e+00 |      150103 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 2.8642e-01 | 1.0000e+00 |      143209 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.5 | 2.7191e-01 | 1.0000e+00 |      135956 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.0 | 2.5913e-01 | 1.0000e+00 |      129564 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.5 | 2.4148e-01 | 1.0000e+00 |      120740 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.0 | 2.2107e-01 | 1.0000e+00 |      110535 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -6.5 | 1.9674e-01 | 1.0000e+00 |       98368 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -6.0 | 1.6380e-01 | 9.9900e-01 |       81899 |      500000 |          999 |        1000 |         0.3 |reached target block errors
     -5.5 | 1.0262e-01 | 8.9200e-01 |       51309 |      500000 |          892 |        1000 |         0.3 |reached target block errors
     -5.0 | 3.4060e-02 | 4.6400e-01 |       17030 |      500000 |          464 |        1000 |         0.3 |reached target block errors
     -4.5 | 3.6727e-03 | 7.9333e-02 |        5509 |     1500000 |          238 |        3000 |         0.9 |reached target block errors
     -4.0 | 1.0756e-04 | 2.6623e-03 |        4141 |    38500000 |          205 |       77000 |        23.4 |reached target block errors
     -3.5 | 2.8800e-06 | 8.0000e-05 |         144 |    50000000 |            8 |      100000 |        29.6 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -3.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 2.6460e-01 | 1.0000e+00 |      132298 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 2.4967e-01 | 1.0000e+00 |      124837 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 2.3026e-01 | 1.0000e+00 |      115130 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.5 | 2.1096e-01 | 1.0000e+00 |      105478 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.0 | 1.7971e-01 | 1.0000e+00 |       89856 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.5 | 1.3412e-01 | 9.8200e-01 |       67059 |      500000 |          982 |        1000 |         0.3 |reached target block errors
     -7.0 | 6.2712e-02 | 7.1600e-01 |       31356 |      500000 |          716 |        1000 |         0.3 |reached target block errors
     -6.5 | 1.2540e-02 | 2.0700e-01 |        6270 |      500000 |          207 |        1000 |         0.3 |reached target block errors
     -6.0 | 5.3120e-04 | 1.4600e-02 |        3984 |     7500000 |          219 |       15000 |         4.5 |reached target block errors
     -5.5 | 9.7400e-06 | 3.5000e-04 |         487 |    50000000 |           35 |      100000 |        29.7 |reached max iterations
     -5.0 | 1.2000e-07 | 1.0000e-05 |           6 |    50000000 |            1 |      100000 |        29.6 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -5.0 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 2.2186e-01 | 1.0000e+00 |      110931 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 1.9586e-01 | 1.0000e+00 |       97929 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 1.6191e-01 | 9.9700e-01 |       80953 |      500000 |          997 |        1000 |         0.3 |reached target block errors
     -8.5 | 1.0198e-01 | 9.0400e-01 |       50989 |      500000 |          904 |        1000 |         0.3 |reached target block errors
     -8.0 | 3.1172e-02 | 4.2900e-01 |       15586 |      500000 |          429 |        1000 |         0.3 |reached target block errors
     -7.5 | 2.9595e-03 | 6.1500e-02 |        5919 |     2000000 |          246 |        4000 |         1.2 |reached target block errors
     -7.0 | 7.3103e-05 | 2.3103e-03 |        3180 |    43500000 |          201 |       87000 |        25.9 |reached target block errors
     -6.5 | 2.0800e-06 | 4.0000e-05 |         104 |    50000000 |            4 |      100000 |        29.6 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -6.5 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 3.2718e-01 | 1.0000e+00 |      163591 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 3.1175e-01 | 1.0000e+00 |      155874 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 2.9630e-01 | 1.0000e+00 |      148148 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.5 | 2.7734e-01 | 1.0000e+00 |      138670 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.0 | 2.5709e-01 | 1.0000e+00 |      128545 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.5 | 2.2914e-01 | 1.0000e+00 |      114571 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -7.0 | 1.9017e-01 | 9.9900e-01 |       95087 |      500000 |          999 |        1000 |         0.3 |reached target block errors
     -6.5 | 1.3029e-01 | 9.6500e-01 |       65143 |      500000 |          965 |        1000 |         0.3 |reached target block errors
     -6.0 | 4.6050e-02 | 5.8800e-01 |       23025 |      500000 |          588 |        1000 |         0.3 |reached target block errors
     -5.5 | 6.0390e-03 | 1.2600e-01 |        6039 |     1000000 |          252 |        2000 |         0.6 |reached target block errors
     -5.0 | 1.9376e-04 | 6.0909e-03 |        3197 |    16500000 |          201 |       33000 |         9.7 |reached target block errors
     -4.5 | 2.2600e-06 | 1.5000e-04 |         113 |    50000000 |           15 |      100000 |        29.4 |reached max iterations
     -4.0 | 0.0000e+00 | 0.0000e+00 |           0 |    50000000 |            0 |      100000 |        29.4 |reached max iterations

Simulation stopped as no error occurred @ EbNo = -4.0 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 2.4748e-01 | 1.0000e+00 |      123738 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.5 | 2.1827e-01 | 1.0000e+00 |      109137 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -9.0 | 1.8311e-01 | 1.0000e+00 |       91554 |      500000 |         1000 |        1000 |         0.3 |reached target block errors
     -8.5 | 1.3005e-01 | 9.8100e-01 |       65025 |      500000 |          981 |        1000 |         0.3 |reached target block errors
     -8.0 | 5.5546e-02 | 7.4500e-01 |       27773 |      500000 |          745 |        1000 |         0.3 |reached target block errors
     -7.5 | 7.6720e-03 | 2.1800e-01 |        3836 |      500000 |          218 |        1000 |         0.3 |reached target block errors
     -7.0 | 4.1517e-04 | 1.8167e-02 |        2491 |     6000000 |          218 |       12000 |         3.5 |reached target block errors
     -6.5 | 1.2420e-05 | 6.9000e-04 |         621 |    50000000 |           69 |      100000 |        29.5 |reached max iterations
     -6.0 | 2.0000e-08 | 1.0000e-05 |           1 |    50000000 |            1 |      100000 |        29.5 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -6.0 dB.

EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status
---------------------------------------------------------------------------------------------------------------------------------------
    -10.0 | 1.8909e-01 | 9.9700e-01 |       94544 |      500000 |          997 |        1000 |         0.3 |reached target block errors
     -9.5 | 1.0520e-01 | 8.6500e-01 |       52600 |      500000 |          865 |        1000 |         0.3 |reached target block errors
     -9.0 | 2.4080e-02 | 3.5100e-01 |       12040 |      500000 |          351 |        1000 |         0.3 |reached target block errors
     -8.5 | 1.1487e-03 | 3.3833e-02 |        3446 |     3000000 |          203 |        6000 |         1.8 |reached target block errors
     -8.0 | 2.1300e-05 | 7.5000e-04 |        1065 |    50000000 |           75 |      100000 |        29.6 |reached max iterations
     -7.5 | 5.4000e-07 | 4.0000e-05 |          27 |    50000000 |            4 |      100000 |        29.6 |reached max iterations

Simulation stopped as target BLER is reached @ EbNo = -7.5 dB.

../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_30_1.png

As we have seen seen, Chase combining provides an SNR gain through soft-combining of repeated observations but no additional coding gain. It is simple to implement since the receiver only needs a single buffer of length n. Incremental redundancy, on the other hand, delivers additional coding gains by providing new parity bits with each retransmission, at the cost of a larger circular-buffer accumulator and more complex rate de-matching. In 5G NR, IR-HARQ with the RV cycling order [0, 2, 3, 1] is used by default.

A Deeper Look into the Polar Code Module#

A Polar code can be defined by a set of frozen bit and information bit positions [1]. The package sionna.phy.fec.polar.utils supports 5G-compliant Polar code design, but also Reed-Muller (RM) codes are available and can be used within the same encoder/decoder layer. If required, rate-matching and CRC concatenation are handled by the class sionna.phy.fec.polar.Polar5GEncoder and sionna.phy.fec.polar.Polar5GDecoder, respectively.

Further, the following decoders are available:

  • Successive cancellation (SC) decoding [1]

    • Fast and low-complexity

    • Sub-optimal error-rate performance

  • Successive cancellation list (SCL) decoding [2]

    • Excellent error-rate performance

    • High-complexity

    • CRC-aided decoding possible

  • Iterative belief propagation (BP) decoding [6]

    • Produces soft-output estimates

    • Sub-optimal error-rate performance

Let us now generate a new Polar code.

[16]:
code_type = "5G" # try also "RM"

# Load the 5G compliant polar code
if code_type=="5G":
    k = 32
    n = 64
    # load 5G compliant channel ranking [3]
    frozen_pos, info_pos = generate_5g_ranking(k,n)
    print("Generated Polar code of length n = {} and k = {}".format(n, k))
    print("Frozen codeword positions: ", frozen_pos)

# Alternatively Reed-Muller code design is also available
elif code_type=="RM":
    r = 3
    m = 7
    frozen_pos, info_pos, n, k, d_min = generate_rm_code(r, m)
    print("Generated ({},{}) Reed-Muller code of length n = {} and k = {} with minimum distance d_min = {}".format(r, m, n, k, d_min))
    print("Frozen codeword positions: ", frozen_pos)

else:
    print("Code not found")
Generated Polar code of length n = 64 and k = 32
Frozen codeword positions:  [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 16 17 18 19 20 21 24 25 26
 32 33 34 35 36 37 40 48]

Now, we can initialize the encoder and a BinarySource to generate random Polar codewords.

[17]:
# init polar encoder
encoder_polar = PolarEncoder(frozen_pos, n)

# init binary source to generate information bits
source = BinarySource()
# define a batch_size
batch_size = 1

# generate random info bits
u = source([batch_size, k])
# and encode
c = encoder_polar(u)

print("Information bits: ", u.cpu().numpy())
print("Polar encoded bits: ", c.cpu().numpy())
Information bits:  [[0. 1. 0. 0. 1. 1. 1. 0. 0. 0. 1. 1. 0. 0. 1. 0. 1. 0. 1. 0. 1. 1. 0. 1.
  1. 0. 1. 1. 1. 0. 1. 1.]]
Polar encoded bits:  [[0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 1. 0.
  1. 1. 1. 0. 0. 0. 1. 1. 0. 1. 0. 0. 0. 1. 0. 1. 0. 1. 0. 1. 1. 1. 1. 0.
  0. 0. 0. 1. 0. 1. 1. 0. 0. 0. 0. 0. 1. 1. 0. 1.]]

As can be seen, the length of the resulting code must be a power of 2. This brings us to the problem of rate-matching and we will now have a closer look how we can adapt the length of the code.

Rate-Matching and Rate-Recovery#

The general task of rate-matching is to enable flexibility of the code w.r.t. the codeword length \(n\) and information bit input size \(k\) and, thereby, the rate \(r = \frac{k}{n}\). In modern communication standards such as 5G NR, these parameters can be adjusted on a bit-level granularity without - in a wider sense - redefining the (mother) code itself. This is enabled by a powerful rate-matching and the corresponding rate-recovery block which will be explained in the following.

The principle idea is to select a mother code as close as possible to the desired properties from a set of possible mother codes. For example for Polar codes, the codeword length must be a power of 2, i.e., \(n = 32, 64, ..., 512, 1024\). For LDPC codes the codeword length is more flexible (due to the different lifting factors), however, does not allow bit-wise granularity neither. Afterwards, the bit-level granularity is provided by shortening, puncturing and repetitions.

To summarize, the rate-matching procedure consists of:

  1. ) 5G NR defines multiple mother codes with similar properties (e.g., via base-graph lifting of LDPC code or sub-codes for Polar codes)

  2. ) Puncturing, shortening and repetitions of bits to allow bit-level rate adjustments

The following figure summarizes the principle for the 5G NR Polar code uplink control channel (UCI). The Fig. is inspired by Fig. 6 in [9].

Rate Match

For bit-wise length adjustments, the following techniques are commonly used:

  1. ) Puncturing: A (\(k,n\)) mother code is punctured by not transmitting \(p\) punctured codeword bits. Thus, the rate increases to \(r_{\text{pun}} = \frac{k}{n-p} > \frac{k}{n} \quad \forall p > 0\). At the decoder these codeword bits are treated as erasure (\(\ell_{\text{ch}} = 0\)).

  2. ) Shortening: A (\(k,n\)) mother code is shortened by setting \(s\) information bits to a fixed (=known) value. Assuming systematic encoding, these \(s\) positions are not transmitted leading to a new code of rate \(r_{\text{short}} = \frac{k-s}{n-s}<\frac{k}{n}\). At the decoder these codeword bits are treated as known values (\(\ell_{\text{ch}} = \infty\)).

  3. ) Repetitions can be used to lower the effective rate. For details we refer the interested reader to [11].

We will now simulate the performance of rate-matched 5G Polar codes for different lengths and rates. For this, we are interested in the required SNR to achieve a target BLER at \(10^{-3}\). Please note that this is a reproduction of the results from [Fig.13a, 4].

Note: This needs a bisection search as we usually simulate the BLER at fixed SNR and, thus, this is simulation takes some time. Please only execute the cell below if you have enough simulation capabilities.

[18]:
# find the EsNo in dB to achieve target_bler
def find_threshold(model, # model to be tested
                   batch_size=1000,
                   max_batch_iter=10, # simulate cws up to batch_size * max_batch_iter
                   max_block_errors=100,  # number of errors before stop
                   target_bler=1e-3): # target error rate to simulate (same as in[4])
        """Bisection search to find required SNR to reach target SNR."""

        # bisection parameters
        esno_db_min = -15 # smallest possible search SNR
        esno_db_max = 15 # largest possible search SNR
        esno_interval = (esno_db_max-esno_db_min)/4 # initial search interval size
        esno_db = 2*esno_interval + esno_db_min # current test SNR
        max_iters = 12 # number of iterations for bisection search

        # run bisection
        for i in range(max_iters):
            num_block_error = 0
            num_cws = 0
            for j in range(max_batch_iter):
                # run model and evaluate BLER
                with torch.no_grad():
                    # Pass esno_db as tensor to avoid recompilation for each value
                    u, u_hat = model(batch_size, torch.tensor(esno_db, dtype=torch.float32))
                num_block_error += count_block_errors(u, u_hat)
                num_cws += batch_size
                # early stop if target number of block errors is reached
                if num_block_error>max_block_errors:
                    break
            bler = num_block_error/num_cws
            # increase SNR if BLER was great than target
            # (larger SNR leads to decreases BLER)
            if bler>target_bler:
                esno_db += esno_interval
            else: # and decrease SNR otherwise
                esno_db -= esno_interval
            esno_interval = esno_interval/2

        # return final SNR after max_iters
        return esno_db

[19]:
# run simulations for multiple code parameters
num_bits_per_symbol = 2 # QPSK
# we sweep over multiple values for k and n
ks = np.array([12, 16, 32, 64, 128, 140, 210, 220, 256, 300, 400, 450, 460, 512, 800, 880, 940])
ns = np.array([160, 240, 480, 960])

# we use EsNo instead of EbNo to have the same results as in [4]
esno = np.zeros([len(ns), len(ks)])

for j,n in enumerate(ns):
    for i,k in enumerate(ks):
        if k<n: # only simulate if code parameters are feasible (i.e., r < 1)
            print(f"Finding threshold of k = {k}, n = {n}")

            # initialize new encoder / decoder pair
            enc = Polar5GEncoder(k=k, n=n)
            dec = Polar5GDecoder(enc, dec_type="SCL", list_size=8)
            #build model
            model = System_Model(k=k,
                                 n=n,
                                 num_bits_per_symbol=num_bits_per_symbol,
                                 encoder=enc,
                                 decoder=dec,
                                 sim_esno=True) # no rate adjustment
            model = torch.compile(model)
            # and find threshold via bisection search
            esno[j, i] = find_threshold(model)
            print("Found threshold at: ", esno[j, i])
Finding threshold of k = 12, n = 160
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -4.259033203125
Finding threshold of k = 16, n = 160
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -3.197021484375
Finding threshold of k = 32, n = 160
Found threshold at:  -0.487060546875
Finding threshold of k = 64, n = 160
Found threshold at:  2.479248046875
Finding threshold of k = 128, n = 160
Found threshold at:  6.793212890625
Finding threshold of k = 140, n = 160
Found threshold at:  8.331298828125
Finding threshold of k = 12, n = 240
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -6.126708984375
Finding threshold of k = 16, n = 240
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -5.050048828125
Finding threshold of k = 32, n = 240
Found threshold at:  -2.698974609375
Finding threshold of k = 64, n = 240
Found threshold at:  0.040283203125
Finding threshold of k = 128, n = 240
Found threshold at:  3.402099609375
Finding threshold of k = 140, n = 240
Found threshold at:  3.848876953125
Finding threshold of k = 210, n = 240
Found threshold at:  7.796630859375
Finding threshold of k = 220, n = 240
Found threshold at:  8.756103515625
Finding threshold of k = 12, n = 480
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -9.180908203125
Finding threshold of k = 16, n = 480
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -8.338623046875
Finding threshold of k = 32, n = 480
Found threshold at:  -5.650634765625
Finding threshold of k = 64, n = 480
Found threshold at:  -3.446044921875
Finding threshold of k = 128, n = 480
Found threshold at:  -0.611572265625
Finding threshold of k = 140, n = 480
Found threshold at:  -0.113525390625
Finding threshold of k = 210, n = 480
Found threshold at:  1.805419921875
Finding threshold of k = 220, n = 480
Found threshold at:  2.017822265625
Finding threshold of k = 256, n = 480
Found threshold at:  2.962646484375
Finding threshold of k = 300, n = 480
Found threshold at:  3.988037109375
Finding threshold of k = 400, n = 480
Found threshold at:  6.492919921875
Finding threshold of k = 450, n = 480
Found threshold at:  8.778076171875
Finding threshold of k = 460, n = 480
Found threshold at:  9.488525390625
Finding threshold of k = 12, n = 960
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -9.429931640625
Finding threshold of k = 16, n = 960
Warning: For 12<=k<=19 additional 3 parity-check bits are defined in 38.212. They are currently not implemented by this encoder and, thus, ignored.
Found threshold at:  -8.404541015625
Finding threshold of k = 32, n = 960
Found threshold at:  -8.602294921875
Finding threshold of k = 64, n = 960
Found threshold at:  -6.624755859375
Finding threshold of k = 128, n = 960
Found threshold at:  -4.017333984375
Finding threshold of k = 140, n = 960
Found threshold at:  -3.709716796875
Finding threshold of k = 210, n = 960
Found threshold at:  -1.973876953125
Finding threshold of k = 220, n = 960
Found threshold at:  -1.695556640625
Finding threshold of k = 256, n = 960
Found threshold at:  -1.072998046875
Finding threshold of k = 300, n = 960
Found threshold at:  -0.318603515625
Finding threshold of k = 400, n = 960
Found threshold at:  1.292724609375
Finding threshold of k = 450, n = 960
Found threshold at:  1.834716796875
Finding threshold of k = 460, n = 960
Found threshold at:  1.885986328125
Finding threshold of k = 512, n = 960
Found threshold at:  2.611083984375
Finding threshold of k = 800, n = 960
Found threshold at:  6.148681640625
Finding threshold of k = 880, n = 960
Found threshold at:  7.738037109375
Finding threshold of k = 940, n = 960
Found threshold at:  10.089111328125
[20]:
# plot the results
leg_str = []
for j,n in enumerate(ns):
    plt.plot(np.log2(ks[ks<n]), esno[j, ks<n])
    leg_str.append("n = {}".format(n))


# define labels manually
x_tick_labels = np.power(2, np.arange(3,11))
plt.xticks(ticks=np.arange(3,11),labels=x_tick_labels, fontsize=18)

# adjusted layout of figure
plt.grid("both")
plt.ylim([-10, 15])
plt.xlabel("Number of information bits $k$", fontsize=20)
plt.yticks(fontsize=18)
plt.ylabel("$E_s/N_0^*$ (dB)", fontsize=20)
plt.legend(leg_str, fontsize=18);
fig = plt.gcf() # get handle to current figure
fig.set_size_inches(15,10)
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_44_0.png

This figure equals [Fig. 13a, 4] with a few small exception for extreme low-rate codes. This can be explained by the fact that the 3 explicit parity-bits bits are not implemented, however, these bits are only relevant for for \(12\leq k \leq20\). It also explains the degraded performance of the n=960, k=16 code.

Throughput and Decoding Complexity#

In the last part of this notebook, you will compare the different computational complexity of the different codes and decoders. In theory the complexity is given as:

  • Successive cancellation list (SCL) decoding of Polar codes scales with \(\mathcal{O}(L \cdot n \cdot \operatorname{log} n)\) and \(\mathcal{O}(n \cdot \operatorname{log} n)\) for SC decoding, respectively.

  • Iterative belief propagation (BP) decoding of LDPC codes scales with \(\mathcal{O}(n)\). However, in particular for short codes a complexity comparison should be supported by empirical results.

We want to emphasize that the results strongly depend on the exact implementation and may differ for different implementations/optimizations. Implementing the SCL decoder in PyTorch involves several design trade-offs to enable a graph-friendly implementation, which can lead to degraded throughput mainly caused by the missing lazy copy-mechanism.

[21]:
def get_throughput(batch_size, ebno_dbs, model, repetitions=1, warmup_runs=10):
    """Simulate throughput in bit/s per ebno_dbs point.

    Uses CUDA events for accurate GPU timing when available,
    otherwise falls back to CPU timing.

    Parameters
    ----------
    batch_size : int
        Batch-size for evaluation.

    ebno_dbs : array-like
        SNR points to be evaluated.

    model : callable
        Function or model that yields the transmitted bits `u` and the
        receiver's estimate `u_hat` for a given ``batch_size`` and
        ``ebno_db``.

    repetitions : int
        Number of trials to average over for throughput measurement.

    warmup_runs : int
        Number of warmup runs before timing. Should be >= 10 for compiled
        models to ensure compilation and autotuning are complete.

    Returns
    -------
    throughput : np.ndarray
        Throughput in bit/s for each SNR point.
    """
    ebno_dbs = np.atleast_1d(ebno_dbs)
    throughput = np.zeros(len(ebno_dbs), dtype=np.float64)
    use_cuda = torch.cuda.is_available() and sionna.phy.config.device.startswith('cuda')

    # Create tensor for warmup SNR - using tensor avoids recompilation guards
    warmup_snr = torch.tensor(float(ebno_dbs[0]), dtype=sionna.phy.config.dtype,
                              device=sionna.phy.config.device)

    # Warmup runs to ensure model is compiled and GPU kernels are cached
    # For max-autotune, this needs many iterations for autotuning to complete
    with torch.no_grad():
        for _ in range(warmup_runs):
            u, u_hat = model(batch_size, warmup_snr)

        # Sync before starting measurements
        if use_cuda:
            torch.cuda.synchronize()

    # Create a tensor for ebno_db to avoid recompilation when value changes
    # torch.compile guards on Python scalars, but not on tensor values
    ebno_db_tensor = torch.tensor(0.0, dtype=sionna.phy.config.dtype,
                                  device=sionna.phy.config.device)

    for idx, ebno_db in enumerate(ebno_dbs):
        # Update tensor value in-place (avoids creating new tensor)
        ebno_db_tensor.fill_(float(ebno_db))

        # Per-SNR warmup: run a few iterations to ensure any lazy operations
        # or caching specific to this SNR value are complete
        with torch.no_grad():
            for _ in range(3):
                u, u_hat = model(batch_size, ebno_db_tensor)
        if use_cuda:
            torch.cuda.synchronize()

        if use_cuda:
            # Use CUDA events for precise GPU timing
            start_event = torch.cuda.Event(enable_timing=True)
            end_event = torch.cuda.Event(enable_timing=True)

            torch.cuda.synchronize()
            start_event.record()

            with torch.no_grad():
                for _ in range(repetitions):
                    u, u_hat = model(batch_size, ebno_db_tensor)

            end_event.record()
            torch.cuda.synchronize()

            # elapsed_time returns milliseconds
            elapsed_ms = start_event.elapsed_time(end_event)
            elapsed_s = elapsed_ms / 1000.0
        else:
            # CPU timing fallback
            with torch.no_grad():
                # Ensure any pending operations complete
                t_start = time.perf_counter()
                for _ in range(repetitions):
                    u, u_hat = model(batch_size, ebno_db_tensor)
                t_stop = time.perf_counter()
            elapsed_s = t_stop - t_start

        # Calculate throughput: total bits processed / elapsed time
        num_bits = u.numel() * repetitions
        throughput[idx] = num_bits / elapsed_s

    return throughput

[22]:
# plot throughput and ber together for ldpc codes
# and simulate the results
num_bits_per_symbol = 2 # QPSK

ebno_db = [5] # SNR to simulate
num_bits_per_batch = 1e7 # must be reduced in case of out-of-memory errors
num_repetitions = 20 # average throughput over multiple runs

# run throughput simulations for each code
throughput = np.zeros(len(codes_under_test))
code_length = np.zeros(len(codes_under_test))
for idx, code in enumerate(codes_under_test):
    print("Running: " + code[2])

    # save codeword length for plotting
    code_length[idx] = code[4]

    # init new model for given encoder/decoder
    torch.cuda.empty_cache()
    torch._dynamo.reset()
    model = System_Model(k=code[3],
                         n=code[4],
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=code[0],
                         decoder=code[1])
    model = torch.compile(model) # You can test different compilation modes

    # scale batch_size such that same number of bits is simulated for all codes
    batch_size = int(num_bits_per_batch / code[4])
    # and measure throughput of the model (extract single value since ebno_db has one element)
    throughput[idx] = get_throughput(batch_size,
                                     ebno_db,
                                     model,
                                     repetitions=num_repetitions)[0]
Running: 5G LDPC BP-20 (n=128)
Running: 5G LDPC BP-20 (n=256)
Running: 5G LDPC BP-20 (n=512)
Running: 5G LDPC BP-20 (n=1000)
Running: 5G LDPC BP-20 (n=2000)
Running: 5G LDPC BP-20 (n=4000)
Running: 5G LDPC BP-20 (n=8000)
Running: 5G LDPC BP-20 (n=16000)
[23]:
# plot results
plt.figure(figsize=(16,10))

plt.xticks(fontsize=18)
plt.yticks(fontsize=18)

plt.title("Throughput LDPC BP Decoding @ rate=0.5", fontsize=25)
plt.xlabel("Codeword length", fontsize=25)
plt.ylabel("Throughput (Mbit/s)", fontsize=25)
plt.grid(which="both")

# and plot results (logarithmic scale in x-dim)
x_tick_labels = code_length.astype(int)
plt.xticks(ticks=np.log2(code_length),labels=x_tick_labels, fontsize=18)
plt.plot(np.log2(code_length), throughput/1e6)

[23]:
[<matplotlib.lines.Line2D at 0x74a73f05d9a0>]
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_49_1.png

As expected, the throughput of BP decoding is (relatively) constant: the complexity scales linearly as \(\mathcal{O}(n)\), so the work per decoded bit stays the same. Note that the plot above is in the log domain.

The throughput drop towards \(n = 8000\) is largely explained by the basegraph switch: for these long codes the encoder selects BG1, while the shorter codes still fall in the BG2 regime, so the decoder runs on a denser, structurally different decoding graph.

Let us have a look at what happens for different SNR values.

[24]:
# --- LDPC ---
n = 1000
k = 500
encoder = LDPC5GEncoder(k, n)
decoder = LDPC5GDecoder(encoder)

# init a new model
torch.cuda.empty_cache()
torch._dynamo.reset()
model = System_Model(k=k,
                     n=n,
                     num_bits_per_symbol=num_bits_per_symbol,
                     encoder=encoder,
                     decoder=decoder)
model = torch.compile(model)

# run throughput tests at 2 dB and 5 dB
ebno_db = [2, 5]
batch_size = 10000
throughput = get_throughput(batch_size,
                            ebno_db, # snr point
                            model,
                            repetitions=num_repetitions)

# and print the results
for idx, snr_db in enumerate(ebno_db):
    print(f"Throughput @ {snr_db:.1f} dB: {throughput[idx]/1e6:.2f} Mbit/s")
Throughput @ 2.0 dB: 61.44 Mbit/s
Throughput @ 5.0 dB: 61.88 Mbit/s

For most Sionna decoders the throughput is not SNR dependent as early stopping of individual samples within a batch is difficult to realize.

We now benchmark the SCL decoder across the operating SNR range.

[25]:
# --- Polar SCL decoding ---
n = 256
k = 128
encoder = Polar5GEncoder(k, n)
decoder = Polar5GDecoder(encoder, "SCL", list_size=8)

# init a new model
torch.cuda.empty_cache()
torch._dynamo.reset()
model = System_Model(k=k,
                     n=n,
                     num_bits_per_symbol=num_bits_per_symbol,
                     encoder=encoder,
                     decoder=decoder)
# compile for throughput
model = torch.compile(model)

ebno_db = np.arange(0, 5, 0.5) # EbNo to evaluate
batch_size = 1000
throughput = get_throughput(batch_size,
                            ebno_db, # snr point
                            model,
                            repetitions=num_repetitions)

# and print the results
for idx, snr_db in enumerate(ebno_db):
    print(f"Throughput @ {snr_db:.1f} dB: {throughput[idx]/1e6:.3f} Mbit/s")
Throughput @ 0.0 dB: 23.852 Mbit/s
Throughput @ 0.5 dB: 23.829 Mbit/s
Throughput @ 1.0 dB: 23.806 Mbit/s
Throughput @ 1.5 dB: 23.810 Mbit/s
Throughput @ 2.0 dB: 23.817 Mbit/s
Throughput @ 2.5 dB: 23.830 Mbit/s
Throughput @ 3.0 dB: 23.753 Mbit/s
Throughput @ 3.5 dB: 23.660 Mbit/s
Throughput @ 4.0 dB: 23.749 Mbit/s
Throughput @ 4.5 dB: 23.737 Mbit/s

As can be seen above, the SCL decoder also has a constant throughput for different SNR values.

The SCL decoder’s complexity grows with the list size \(L\). Doubling \(L\) roughly doubles the number of path-metric updates per decoding step, so we expect the throughput to scale approximately as \(\mathcal{O}(1/L)\).

Let us measure this directly by sweeping \(L \in \{1, 2, 4, 8, 16, 32\}\) for the same code as above.

[26]:
# --- Polar SCL throughput vs. list size ---
n = 256
k = 128
ebno_db = [3.0] # single SNR is enough; SCL throughput is essentially SNR-independent
batch_size = 10000

list_sizes = [1, 2, 4, 8, 16, 32]
throughput_scl = np.zeros(len(list_sizes))

for idx, L in enumerate(list_sizes):
    print(f"Running: SCL list_size={L}")

    encoder = Polar5GEncoder(k, n)
    decoder = Polar5GDecoder(encoder, "SCL", list_size=L)

    torch.cuda.empty_cache()
    torch._dynamo.reset()
    model = System_Model(k=k,
                         n=n,
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=encoder,
                         decoder=decoder)
    model = torch.compile(model)

    throughput_scl[idx] = get_throughput(batch_size,
                                         ebno_db,
                                         model,
                                         repetitions=num_repetitions)[0]

# print results and the relative slowdown w.r.t. list_size=1
print()
for L, tput in zip(list_sizes, throughput_scl):
    print(f"L={L:>2}: {tput/1e6:6.2f} Mbit/s "
          f"(relative to L=1: {throughput_scl[0]/tput:.2f}x slower)")

# plot Mbit/s vs. list_size (log-x)
plt.figure(figsize=(10, 6))
plt.semilogx(list_sizes, throughput_scl/1e6, marker="o", base=2)
plt.xticks(list_sizes, labels=[str(L) for L in list_sizes], fontsize=14)
plt.yticks(fontsize=14)
plt.title(f"Polar SCL Throughput vs. List Size (k={k}, n={n})", fontsize=18)
plt.xlabel("List size $L$", fontsize=16)
plt.ylabel("Throughput (Mbit/s)", fontsize=16)
plt.grid(which="both")
Running: SCL list_size=1
Running: SCL list_size=2
Running: SCL list_size=4
Running: SCL list_size=8
Running: SCL list_size=16
Running: SCL list_size=32

L= 1: 142.35 Mbit/s (relative to L=1: 1.00x slower)
L= 2:  91.56 Mbit/s (relative to L=1: 1.55x slower)
L= 4:  51.85 Mbit/s (relative to L=1: 2.75x slower)
L= 8:  27.81 Mbit/s (relative to L=1: 5.12x slower)
L=16:  14.75 Mbit/s (relative to L=1: 9.65x slower)
L=32:   7.47 Mbit/s (relative to L=1: 19.05x slower)
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_55_1.png

Finally, let us look at how the SCL throughput scales with the codeword length \(n\) for a fixed list size. The textbook complexity of SCL decoding is \(\mathcal{O}(L \cdot n \log n)\) when path forking uses Tal & Vardy’s lazy-copy trick.

The measured curve will not necessarily follow this idealized scaling. Graph-friendly PyTorch primitives impose constraints — for instance, path pruning here is done with torch.topk + torch.gather over the whole packed workspace rather than with lazy-copied per-stage LLRs — and runtime effects such as kernel-launch overhead, batch-size scaling and memory bandwidth typically dominate at these problem sizes. The dashed line below shows the lazy-copy ideal as an orientation, not as a tight predictor.

We sweep \(n \in \{128, 256, 512, 1024\}\) at a fixed rate of \(1/2\) and list size \(L=8\).

[27]:
# --- Polar SCL throughput vs. codeword length n ---
rate = 0.5
list_size = 8
ebno_db = [3.0]   # SCL throughput is essentially SNR-independent
num_bits_per_batch = 1e7  # keep total bits per call constant

ns_scl = [128, 256, 512, 1024]
throughput_scl_n = np.zeros(len(ns_scl))

for idx, n in enumerate(ns_scl):
    k = int(rate * n)
    print(f"Running: SCL n={n}, k={k}")

    encoder = Polar5GEncoder(k, n)
    decoder = Polar5GDecoder(encoder, "SCL", list_size=list_size)

    torch.cuda.empty_cache()
    torch._dynamo.reset()
    model = System_Model(k=k,
                         n=n,
                         num_bits_per_symbol=num_bits_per_symbol,
                         encoder=encoder,
                         decoder=decoder)
    model = torch.compile(model)

    batch_size = int(num_bits_per_batch / n)
    throughput_scl_n[idx] = get_throughput(batch_size,
                                           ebno_db,
                                           model,
                                           repetitions=num_repetitions)[0]

# print results
print()
for n, tput in zip(ns_scl, throughput_scl_n):
    print(f"n={n:>5}: {tput/1e6:6.2f} Mbit/s")

# plot throughput vs n with an O(1/log n) reference
plt.figure(figsize=(10, 6))
plt.semilogx(ns_scl, throughput_scl_n/1e6, marker="o", base=2,
             label="measured")
# Lazy-copy reference (textbook per-bit work ~ log n).
# Real throughput depends on the implementation and runtime effects.
ref = throughput_scl_n[0] * np.log2(ns_scl[0]) / np.log2(ns_scl)
plt.semilogx(ns_scl, ref/1e6, linestyle="--", color="gray",
             label=r"$\mathcal{O}(1/\log_2 n)$ (lazy-copy ideal)")
plt.xticks(ns_scl, labels=[str(n) for n in ns_scl], fontsize=14)
plt.gca().xaxis.set_minor_formatter(plt.NullFormatter())
plt.yticks(fontsize=14)
plt.title(f"Polar SCL Throughput vs. Codeword Length (rate={rate}, L={list_size})",
          fontsize=18)
plt.xlabel("Codeword length $n$", fontsize=16)
plt.ylabel("Throughput (Mbit/s)", fontsize=16)
plt.grid(which="both")
plt.legend(fontsize=14)
Running: SCL n=128, k=64
Running: SCL n=256, k=128
Running: SCL n=512, k=256
Running: SCL n=1024, k=512

n=  128:  46.37 Mbit/s
n=  256:  28.26 Mbit/s
n=  512:  17.19 Mbit/s
n= 1024:  10.72 Mbit/s
[27]:
<matplotlib.legend.Legend at 0x74a67dcf8bf0>
../../../../build/doctrees/nbsphinx/phy_tutorials_notebooks_5G_Channel_Coding_Polar_vs_LDPC_Codes_57_2.png

Note that the \(\mathcal{O}(L \cdot n \log n)\) complexity from Tal & Vardy only holds under the lazy-copy assumption. Lazy copy is not implemented in the Sionna SCL decoder because it does not map cleanly onto graph-friendly PyTorch primitives, so the empirical curve will not necessarily track the dashed reference.

You can also try:

  • Analyze different rates

  • What happens for different batch-sizes? Can you explain what happens?

  • Analyze the impact of code block segmentation on the throughput and BLER performance

References#

[1] E. Arikan, “Channel polarization: A method for constructing capacity-achieving codes for symmetric binary-input memoryless channels,” IEEE Transactions on Information Theory, 2009.

[2] Ido Tal and Alexander Vardy, “List Decoding of Polar Codes.” IEEE Transactions on Information Theory, 2015.

[3] ETSI 3GPP TS 38.212 “5G NR Multiplexing and channel coding”, v.16.5.0, 2021-03.

[4] V. Bioglio, C. Condo, I. Land, “Design of Polar Codes in 5G New Radio.” IEEE Communications Surveys & Tutorials, 2020.

[5] D. Hui, S. Sandberg, Y. Blankenship, M. Andersson, L. Grosjean “Channel coding in 5G new radio: A Tutorial Overview and Performance Comparison with 4G LTE.” IEEE Vehicular Technology Magazine, 2018.

[6] E. Arikan, “A Performance Comparison of Polar Codes and Reed-Muller Codes,” IEEE Commun. Lett., vol. 12, no. 6, pp. 447–449, Jun. 2008.

[7] R. G. Gallager, Low-Density Parity-Check Codes, M.I.T. Press Classic Series, Cambridge MA, 1963.

[8] T. Richardson and S. Kudekar. “Design of low-density parity check codes for 5G new radio,” IEEE Communications Magazine 56.3, 2018.

[9] G. Liva, L. Gaudio, T. Ninacs, T. Jerkovits, “Code design for short blocks: A survey,” arXiv preprint arXiv:1610.00873, 2016.

[10] S. Cammerer, B. Leible, M. Stahl, J. Hoydis, and S ten Brink, “Combining Belief Propagation and Successive Cancellation List Decoding of Polar Codes on a GPU Platform,” IEEE ICASSP, 2017.

[11] V. Bioglio, F. Gabry, I. Land, “Low-complexity puncturing and shortening of polar codes,” IEEE Wireless Communications and Networking Conference Workshops (WCNCW), 2017.

[12] M. Fossorier, S. Lin, “Soft-Decision Decoding of Linear Block Codes Based on Ordered Statistics”, IEEE Transactions on Information Theory, vol. 41, no. 5, 1995.