Basic Binary Operations for FEC

This example demonstrates the fundamental binary operations used in forward error correction (FEC) coding using Kaira’s utility functions. We’ll explore Hamming distances, Hamming weights, and binary-integer conversions.

import matplotlib.pyplot as plt
import torch

from kaira.models.fec.utils import (
    from_binary_tensor,
    hamming_distance,
    hamming_weight,
    to_binary_tensor,
)

Setting up

<torch._C.Generator object at 0x7f50199b2410>

Hamming Distance

The Hamming distance counts the number of differing positions between two vectors.

# Create binary vectors
x = torch.tensor([1, 0, 1, 0, 1, 0, 1])
y = torch.tensor([1, 1, 1, 0, 0, 0, 1])

# Calculate Hamming distance using Kaira's utility
dist = hamming_distance(x, y)

print(f"Vector x: {x.tolist()}")
print(f"Vector y: {y.tolist()}")
print(f"Hamming distance: {dist}")
Vector x: [1, 0, 1, 0, 1, 0, 1]
Vector y: [1, 1, 1, 0, 0, 0, 1]
Hamming distance: 2

Visualizing Hamming Distance

fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 8))

# Plot first vector
ax1.bar(range(len(x)), x.numpy(), color=["red" if bit == 1 else "blue" for bit in x])
ax1.set_title("Vector x", fontweight="bold")
ax1.set_ylabel("Bit value")
ax1.set_ylim(0, 1.2)

# Plot second vector
ax2.bar(range(len(y)), y.numpy(), color=["red" if bit == 1 else "blue" for bit in y])
ax2.set_title("Vector y", fontweight="bold")
ax2.set_ylabel("Bit value")
ax2.set_ylim(0, 1.2)

# Plot differences
diff = (x != y).int().numpy()
ax3.bar(range(len(diff)), diff, color=["purple" if d == 1 else "gray" for d in diff])
ax3.set_title(f"Differences (Hamming distance = {dist})", fontweight="bold")
ax3.set_xlabel("Bit position")
ax3.set_ylabel("Different")
ax3.set_ylim(0, 1.2)

plt.tight_layout()
plt.show()
Vector x, Vector y, Differences (Hamming distance = 2)

Hamming Weight

The Hamming weight counts the number of 1s in a binary vector.

# Create test vectors
vectors = [
    torch.tensor([1, 0, 1, 0, 1]),  # weight = 3
    torch.tensor([1, 1, 1, 0, 0]),  # weight = 3
    torch.tensor([0, 0, 0, 0, 1]),  # weight = 1
    torch.tensor([0, 0, 0, 0, 0]),  # weight = 0
]

print("\nHamming weights:")
for i, vec in enumerate(vectors):
    weight = hamming_weight(vec)
    print(f"Vector {i + 1}: {vec.tolist()} -> weight = {weight}")
Hamming weights:
Vector 1: [1, 0, 1, 0, 1] -> weight = 3
Vector 2: [1, 1, 1, 0, 0] -> weight = 3
Vector 3: [0, 0, 0, 0, 1] -> weight = 1
Vector 4: [0, 0, 0, 0, 0] -> weight = 0

Visualizing Hamming Weights

fig, axes = plt.subplots(2, 2, figsize=(10, 6))
axes = axes.flatten()

for i, (vec, ax) in enumerate(zip(vectors, axes)):
    weight = hamming_weight(vec)

    # Plot vector
    bars = ax.bar(range(len(vec)), vec.numpy(), color=["red" if bit == 1 else "blue" for bit in vec])
    ax.set_title(f"Vector {i + 1} (weight = {weight})", fontweight="bold")
    ax.set_ylim(0, 1.2)

    # Add weight indicator
    ax.text(len(vec) / 2, 1.1, f"Weight: {weight}/{len(vec)}", ha="center", fontweight="bold")

plt.tight_layout()
plt.show()
Vector 1 (weight = 3), Vector 2 (weight = 3), Vector 3 (weight = 1), Vector 4 (weight = 0)

Binary-Integer Conversions

Convert between binary tensors and integer representations.

# Convert integers to binary tensors
integers = [5, 10, 15, 7]
binary_length = 4

print("\nBinary-Integer conversions:")
for num in integers:
    # Convert to binary tensor
    binary = to_binary_tensor(num, binary_length)

    # Convert back to integer
    recovered = from_binary_tensor(binary)

    print(f"Integer: {num} -> Binary: {binary.tolist()} -> Recovered: {recovered}")
Binary-Integer conversions:
Integer: 5 -> Binary: [0, 1, 0, 1] -> Recovered: 5
Integer: 10 -> Binary: [1, 0, 1, 0] -> Recovered: 10
Integer: 15 -> Binary: [1, 1, 1, 1] -> Recovered: 15
Integer: 7 -> Binary: [0, 1, 1, 1] -> Recovered: 7

Visualizing Binary Representations

fig, axes = plt.subplots(2, 2, figsize=(10, 6))
axes = axes.flatten()

for i, (num, ax) in enumerate(zip(integers, axes)):
    binary = to_binary_tensor(num, binary_length)

    # Plot binary representation
    ax.bar(range(binary_length), binary.numpy(), color=["red" if bit == 1 else "blue" for bit in binary])
    ax.set_title("Integer {} = Binary {}".format(num, "".join(map(str, binary.tolist()))), fontweight="bold")
    ax.set_xlabel("Bit position (MSB to LSB)")
    ax.set_ylabel("Bit value")
    ax.set_ylim(0, 1.2)

    # Add positional values
    for j, bit in enumerate(binary):
        if bit == 1:
            power = binary_length - j - 1
            ax.text(j, bit + 0.1, f"2^{power}", ha="center", fontsize=8)

plt.tight_layout()
plt.show()
Integer 5 = Binary 0101, Integer 10 = Binary 1010, Integer 15 = Binary 1111, Integer 7 = Binary 0111

Practical Example: Error Detection

Demonstrate how these operations are used in error correction.

# Simulate a codeword and received word with errors
codeword = torch.tensor([1, 0, 1, 1, 0, 1, 0])
received = torch.tensor([1, 1, 1, 1, 0, 0, 0])  # 2 errors

# Calculate error metrics
num_errors = hamming_distance(codeword, received)
codeword_weight = hamming_weight(codeword)
error_weight = hamming_weight(received ^ codeword)

print("\nError detection example:")
print(f"Codeword:  {codeword.tolist()}")
print(f"Received:  {received.tolist()}")
print(f"Errors detected: {num_errors}")
print(f"Codeword weight: {codeword_weight}")
print(f"Error pattern weight: {error_weight}")
Error detection example:
Codeword:  [1, 0, 1, 1, 0, 1, 0]
Received:  [1, 1, 1, 1, 0, 0, 0]
Errors detected: 2
Codeword weight: 4
Error pattern weight: 2

Summary

This example demonstrated essential Kaira FEC utility functions: - hamming_distance(): Count bit differences between vectors - hamming_weight(): Count number of 1s in a vector - to_binary_tensor(): Convert integers to binary representation - from_binary_tensor(): Convert binary tensors back to integers

These utilities are fundamental building blocks for implementing error correction algorithms in communication systems.

Gallery generated by Sphinx-Gallery