├── tests ├── __init__.py ├── test_base │ ├── __init__.py │ └── test_codec.py ├── test_sc │ ├── __init__.py │ ├── test_codec.py │ └── test_decoder.py ├── test_fast_scan │ ├── __init__.py │ └── test_codec.py ├── test_fast_ssc │ ├── __init__.py │ ├── test_codec.py │ ├── test_node.py │ └── test_decoder.py ├── test_g_fast_ssc │ ├── __init__.py │ ├── test_codec.py │ └── test_node.py ├── test_rc_scan │ ├── __init__.py │ ├── test_node.py │ ├── test_codec.py │ └── test_decoder.py ├── test_sc_list │ ├── __init__.py │ └── test_codec.py ├── test_g_fast_scan │ ├── __init__.py │ └── test_codec.py ├── mixins.py └── base.py ├── examples ├── __init__.py ├── modelling │ ├── __init__.py │ ├── rc_scan │ │ ├── __init__.py │ │ ├── base.py │ │ └── 8192_4096.py │ ├── fast_ssc │ │ ├── __init__.py │ │ ├── base.py │ │ ├── 2048.py │ │ ├── 4096.py │ │ └── 8192_4096.py │ ├── mongo.py │ ├── runner.py │ └── functions.py └── simple_simulation.py ├── python_polar_coding ├── __init__.py ├── polar_codes │ ├── base │ │ ├── parallel │ │ │ ├── node.py │ │ │ ├── __init__.py │ │ │ ├── codec.py │ │ │ └── decoder.py │ │ ├── constants.py │ │ ├── __init__.py │ │ ├── functions │ │ │ ├── __init__.py │ │ │ ├── encoding.py │ │ │ ├── alpha.py │ │ │ ├── beta_soft.py │ │ │ ├── beta_hard.py │ │ │ └── node_types.py │ │ ├── encoder.py │ │ ├── decoding_path.py │ │ ├── node.py │ │ ├── decoder.py │ │ └── codec.py │ ├── parallel_rc_scan │ │ └── __init__.py │ ├── sc │ │ ├── __init__.py │ │ ├── codec.py │ │ └── decoder.py │ ├── g_fast_scan │ │ ├── __init__.py │ │ ├── decoder.py │ │ ├── node.py │ │ └── codec.py │ ├── sc_list │ │ ├── __init__.py │ │ ├── decoding_path.py │ │ ├── codec.py │ │ └── decoder.py │ ├── fast_scan │ │ ├── __init__.py │ │ ├── decoder.py │ │ ├── codec.py │ │ ├── node.py │ │ └── functions.py │ ├── fast_ssc │ │ ├── __init__.py │ │ ├── codec.py │ │ ├── node.py │ │ └── decoder.py │ ├── g_fast_ssc │ │ ├── __init__.py │ │ ├── decoder.py │ │ ├── node.py │ │ └── codec.py │ ├── rc_scan │ │ ├── __init__.py │ │ ├── codec.py │ │ ├── node.py │ │ ├── functions.py │ │ └── decoder.py │ ├── __init__.py │ ├── pcc.py │ ├── crc.py │ └── utils.py ├── modems │ ├── __init__.py │ └── simple.py ├── channels │ ├── __init__.py │ └── simple.py └── simulation │ ├── __init__.py │ ├── http.py │ ├── functions.py │ └── simulation.py ├── Changelog.md ├── .isort.cfg ├── MANIFEST.in ├── .travis.yml ├── tox.ini ├── requirements.in ├── .gitignore ├── Makefile ├── pyproject.toml ├── docker-compose.yml ├── requirements.txt ├── python-publish.yml ├── .github └── workflows │ └── python-publish.yml ├── LICENSE.txt ├── setup.py ├── results ├── 2048_512_I_2.json ├── 2048_1024_I_2.json ├── 2048_512_I_1.json ├── 2048_512_I_4.json ├── 2048_1024_I_1.json ├── 2048_1024_I_4.json ├── 2048_1536_I_1.json ├── 2048_1536_I_4.json ├── 2048_1536_I_2.json ├── sc_list.json ├── 8192_4096.json └── 8192_4096_I_1.json └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_sc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/modelling/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_fast_scan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_fast_ssc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_g_fast_ssc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_rc_scan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_sc_list/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/modelling/rc_scan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_g_fast_scan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/modelling/fast_ssc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/parallel/node.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/parallel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/parallel/codec.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/parallel/decoder.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/parallel_rc_scan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.0 4 | 5 | Initial version 6 | -------------------------------------------------------------------------------- /python_polar_coding/modems/__init__.py: -------------------------------------------------------------------------------- 1 | from .simple import SimpleBPSKModem 2 | -------------------------------------------------------------------------------- /python_polar_coding/channels/__init__.py: -------------------------------------------------------------------------------- 1 | from .simple import SimpleAWGNChannel, SimpleBPSKModulationAWGN 2 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import SCPolarCodec 2 | from .decoder import SCDecoder 3 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_scan/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import * 2 | from .decoder import * 3 | from .node import * 4 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc_list/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import SCListPolarCodec 2 | from .decoder import SCListDecoder 3 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/constants.py: -------------------------------------------------------------------------------- 1 | # LLR = 1000 is high enough to be considered as +∞ for SCAN decoding 2 | INFINITY = 1000 3 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | line_length=79 3 | length_sort_stdlib=1 4 | multi_line_output=3 5 | include_trailing_comma=True 6 | skip=apak,venv 7 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pyproject.toml 2 | 3 | # Include the README 4 | include README.md 5 | 6 | # Include the license file 7 | include LICENSE.txt 8 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_scan/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import * 2 | from .decoder import * 3 | from .functions import * 4 | from .node import * 5 | -------------------------------------------------------------------------------- /python_polar_coding/simulation/__init__.py: -------------------------------------------------------------------------------- 1 | from .functions import compute_fails, simulation_task, transmission 2 | from .simulation import simulate_multi_core 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | - "3.7" 5 | - "3.8" 6 | install: 7 | - pip install -r requirements.txt 8 | script: 9 | - make test -------------------------------------------------------------------------------- /examples/modelling/mongo.py: -------------------------------------------------------------------------------- 1 | username = 'root' 2 | password = 'example' 3 | URI = f'mongodb://{username}:{password}@localhost' 4 | DB_NAME = 'polar_codes_modelling' 5 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_ssc/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import FastSSCPolarCodec 2 | from .decoder import FastSSCDecoder 3 | from .node import FastSSCNode 4 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_ssc/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import GFastSSCPolarCodec 2 | from .decoder import GFastSSCDecoder 3 | from .node import GFastSSCNode 4 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{36,37,38} 3 | 4 | isolated_build = true 5 | 6 | [testenv] 7 | commands = 8 | python setup.py check -m -s 9 | make test 10 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | # Math lib 2 | numpy 3 | 4 | # JIT compilation for numpy code 5 | numba 6 | 7 | # Tree implementation 8 | anytree 9 | 10 | # HTTP requests 11 | requests 12 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/rc_scan/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import RCSCANPolarCodec 2 | from .decoder import RCSCANDecoder 3 | from .functions import * 4 | from .node import RCSCANNode 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | *~ 4 | .xlsx 5 | .idea 6 | .python-version 7 | .pytest_cache 8 | apak/ 9 | src/ 10 | tests/_trial_temp* 11 | venv/ 12 | *.egg-info/ 13 | build/ 14 | dist/ 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Run tests 3 | # ----------------------------------------------------------------------------- 4 | 5 | test: 6 | python -m unittest discover 7 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_scan/decoder.py: -------------------------------------------------------------------------------- 1 | from python_polar_coding.polar_codes.rc_scan import RCSCANDecoder 2 | 3 | from .node import FastSCANNode 4 | 5 | 6 | class FastSCANDecoder(RCSCANDecoder): 7 | node_class = FastSCANNode 8 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/__init__.py: -------------------------------------------------------------------------------- 1 | from .fast_ssc import FastSSCPolarCodec 2 | from .g_fast_ssc import GFastSSCPolarCodec 3 | from .rc_scan import RCSCANPolarCodec 4 | from .sc import SCPolarCodec 5 | from .sc_list import SCListPolarCodec 6 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_scan/codec.py: -------------------------------------------------------------------------------- 1 | from python_polar_coding.polar_codes.rc_scan import RCSCANPolarCodec 2 | 3 | from .decoder import FastSCANDecoder 4 | 5 | 6 | class FastSCANCodec(RCSCANPolarCodec): 7 | decoder_class = FastSCANDecoder 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | # These are the assumed default build requirements from pip: 3 | # https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support 4 | requires = ["setuptools>=40.8.0", "wheel"] 5 | build-backend = "setuptools.build_meta" 6 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .codec import BasePolarCodec 2 | from .constants import * 3 | from .decoder import BaseDecoder, BaseTreeDecoder 4 | from .decoding_path import DecodingPathMixin 5 | from .encoder import * 6 | from .functions import * 7 | from .node import * 8 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc_list/decoding_path.py: -------------------------------------------------------------------------------- 1 | from python_polar_coding.polar_codes.base import DecodingPathMixin 2 | from python_polar_coding.polar_codes.sc import SCDecoder 3 | 4 | 5 | class SCPath(DecodingPathMixin, SCDecoder): 6 | """Decoding path of SC List decoder.""" 7 | -------------------------------------------------------------------------------- /examples/modelling/rc_scan/base.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from examples.modelling.runner import executor 4 | from python_polar_coding.polar_codes import RCSCANPolarCode 5 | 6 | COLLECTION = 'rc_scan' 7 | 8 | 9 | rc_scan_executor = partial( 10 | executor, 11 | code_class=RCSCANPolarCode, 12 | collection_name=COLLECTION, 13 | ) 14 | -------------------------------------------------------------------------------- /examples/modelling/fast_ssc/base.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | from examples.modelling.runner import executor 4 | from python_polar_coding.polar_codes import FastSSCPolarCode 5 | 6 | COLLECTION = 'fast_ssc' 7 | 8 | 9 | fast_ssc_executor = partial( 10 | executor, 11 | code_class=FastSSCPolarCode, 12 | collection_name=COLLECTION, 13 | ) 14 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_scan/node.py: -------------------------------------------------------------------------------- 1 | from python_polar_coding.polar_codes.rc_scan import RCSCANNode 2 | 3 | from ..base import NodeTypes 4 | 5 | 6 | class FastSCANNode(RCSCANNode): 7 | supported_nodes = ( 8 | NodeTypes.ZERO, 9 | NodeTypes.ONE, 10 | NodeTypes.REPETITION, 11 | NodeTypes.SINGLE_PARITY_CHECK, 12 | ) 13 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc/codec.py: -------------------------------------------------------------------------------- 1 | from ..base import BasePolarCodec 2 | from .decoder import SCDecoder 3 | 4 | 5 | class SCPolarCodec(BasePolarCodec): 6 | """Polar code with SC decoding algorithm.""" 7 | decoder_class = SCDecoder 8 | 9 | def init_decoder(self): 10 | return self.decoder_class( 11 | n=self.n, mask=self.mask, is_systematic=self.is_systematic 12 | ) 13 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | 5 | mongo: 6 | image: mongo:3.6.14-xenial 7 | environment: 8 | MONGO_INITDB_ROOT_USERNAME: root 9 | MONGO_INITDB_ROOT_PASSWORD: example 10 | ports: 11 | - "27017:27017" 12 | 13 | mongo-express: 14 | image: mongo-express 15 | ports: 16 | - 8081:8081 17 | environment: 18 | ME_CONFIG_MONGODB_ADMINUSERNAME: root 19 | ME_CONFIG_MONGODB_ADMINPASSWORD: example 20 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/__init__.py: -------------------------------------------------------------------------------- 1 | from .alpha import ( 2 | compute_alpha, 3 | compute_left_alpha, 4 | compute_right_alpha, 5 | function_1, 6 | function_2, 7 | ) 8 | from .beta_hard import ( 9 | compute_beta_hard, 10 | compute_parent_beta_hard, 11 | make_hard_decision, 12 | ) 13 | from .beta_soft import compute_beta_soft 14 | from .encoding import compute_encoding_step 15 | from .node_types import NodeTypes, get_node_type 16 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_ssc/decoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from python_polar_coding.polar_codes.fast_ssc import FastSSCDecoder 4 | 5 | from .node import GFastSSCNode 6 | 7 | 8 | class GFastSSCDecoder(FastSSCDecoder): 9 | node_class = GFastSSCNode 10 | 11 | def __init__(self, n: int, mask: np.array, AF: int = 0): 12 | self.AF = AF 13 | super().__init__(n=n, mask=mask) 14 | 15 | def _setup_decoding_tree(self): 16 | """Setup decoding tree.""" 17 | return self.node_class(mask=self.mask, AF=self.AF) 18 | -------------------------------------------------------------------------------- /examples/modelling/fast_ssc/2048.py: -------------------------------------------------------------------------------- 1 | from examples.modelling.fast_ssc.base import fast_ssc_executor 2 | 3 | CODE_RATES = [0.25, 0.33, 0.5, 0.66, 0.75, ] 4 | SNR_RANGE = [i/2 for i in range(2, 9)] 5 | MESSAGES_PER_EXPERIMENT = 1000 6 | REPETITIONS = 50 7 | CODE_LENGTH = 2048 8 | MAX_WORKERS = 7 9 | 10 | 11 | if __name__ == '__main__': 12 | fast_ssc_executor( 13 | codeword_length=CODE_LENGTH, 14 | code_rates=CODE_RATES, 15 | snr_range=SNR_RANGE, 16 | task_repetitions=REPETITIONS, 17 | messages_per_task=MESSAGES_PER_EXPERIMENT, 18 | ) 19 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_scan/decoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from ..rc_scan import RCSCANDecoder 4 | from .node import GFastSCANNode 5 | 6 | 7 | class GFastSCANDecoder(RCSCANDecoder): 8 | 9 | node_class = GFastSCANNode 10 | 11 | def __init__( 12 | self, 13 | n: int, 14 | mask: np.array, 15 | AF: int = 1, 16 | I: int = 1, 17 | ): 18 | self.AF = AF 19 | super().__init__(n=n, mask=mask, I=I) 20 | 21 | def _setup_decoding_tree(self): 22 | """Setup decoding tree.""" 23 | return self.node_class(mask=self.mask, AF=self.AF) 24 | -------------------------------------------------------------------------------- /tests/test_sc_list/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.sc_list import SCListPolarCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestSCListPolarCode1024_512_4(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = SCListPolarCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 512, 12 | 'L': 4, 13 | } 14 | 15 | 16 | class TestSCListPolarCode1024_512_8(BasicVerifyPolarCode, TestCase): 17 | polar_code_class = SCListPolarCodec 18 | code_parameters = { 19 | 'N': 1024, 20 | 'K': 512, 21 | 'L': 8, 22 | } 23 | -------------------------------------------------------------------------------- /examples/modelling/fast_ssc/4096.py: -------------------------------------------------------------------------------- 1 | from examples.modelling.fast_ssc.base import fast_ssc_executor 2 | 3 | # (4096, 3072) code built with Bhattacharya parameters method 4 | CODE_RATES = [0.75, ] 5 | # From 2 to 5 with step 0.25 dB 6 | SNR_RANGE = [i/4 for i in range(8, 21)] 7 | MESSAGES_PER_EXPERIMENT = 100 8 | REPETITIONS = 1000 9 | CODE_LENGTH = 4096 10 | MAX_WORKERS = 4 11 | 12 | 13 | if __name__ == '__main__': 14 | fast_ssc_executor( 15 | codeword_length=CODE_LENGTH, 16 | code_rates=CODE_RATES, 17 | snr_range=SNR_RANGE, 18 | task_repetitions=REPETITIONS, 19 | messages_per_task=MESSAGES_PER_EXPERIMENT, 20 | ) 21 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_ssc/codec.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from python_polar_coding.polar_codes.base import BasePolarCodec 4 | 5 | from .decoder import FastSSCDecoder 6 | 7 | 8 | class FastSSCPolarCodec(BasePolarCodec): 9 | """Polar code with SC decoding algorithm. 10 | 11 | Based on: https://arxiv.org/pdf/1307.7154.pdf 12 | 13 | """ 14 | decoder_class = FastSSCDecoder 15 | 16 | def init_decoder(self): 17 | return self.decoder_class(n=self.n, mask=self.mask) 18 | 19 | def decode(self, received_message: np.array) -> np.array: 20 | """Decode received message presented as LLR values.""" 21 | return self.decoder(received_message) 22 | -------------------------------------------------------------------------------- /examples/modelling/fast_ssc/8192_4096.py: -------------------------------------------------------------------------------- 1 | from examples.modelling.fast_ssc.base import fast_ssc_executor 2 | 3 | CODE_RATES = [0.5, ] 4 | # From 1.5 to 3 with step 0.25 dB 5 | SNR_RANGE = [i/4 for i in range(6, 13)] 6 | MESSAGES_PER_EXPERIMENT = 100 7 | REPETITIONS = 1000 8 | CODE_LENGTH = 8192 9 | MAX_WORKERS = 7 10 | ITERATIONS = [ 11 | {'design_snr': 1.4, }, 12 | ] 13 | 14 | 15 | if __name__ == '__main__': 16 | fast_ssc_executor( 17 | codeword_length=CODE_LENGTH, 18 | code_rates=CODE_RATES, 19 | snr_range=SNR_RANGE, 20 | task_repetitions=REPETITIONS, 21 | messages_per_task=MESSAGES_PER_EXPERIMENT, 22 | additional_code_params=ITERATIONS, 23 | ) 24 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_ssc/node.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from ..base import HardNode, NodeTypes 4 | 5 | 6 | class FastSSCNode(HardNode): 7 | """Decoding node for Fast SSC algorithm.""" 8 | supported_nodes = ( 9 | NodeTypes.ZERO, 10 | NodeTypes.ONE, 11 | NodeTypes.SINGLE_PARITY_CHECK, 12 | NodeTypes.REPETITION, 13 | ) 14 | 15 | @property 16 | def is_zero(self) -> bool: 17 | """Check is the node is Zero node.""" 18 | return self.node_type == NodeTypes.ZERO 19 | 20 | def get_decoding_params(self) -> Dict: 21 | return dict( 22 | node_type=self.node_type, 23 | llr=self.alpha, 24 | ) 25 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/encoding.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | 5 | @numba.njit 6 | def compute_encoding_step( 7 | level: int, 8 | n: int, 9 | source: np.array, 10 | result: np.array, 11 | ) -> np.array: 12 | """Compute single step of polar encoding process.""" 13 | pairs_per_group = step = np.power(2, n - level - 1) 14 | groups = np.power(2, level) 15 | 16 | for g in range(groups): 17 | start = 2 * g * step 18 | 19 | for p in range(pairs_per_group): 20 | result[p + start] = source[p + start] ^ source[p + start + step] 21 | result[p + start + step] = source[p + start + step] 22 | 23 | return result 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile requirements.in 6 | # 7 | anytree==2.8.0 # via -r requirements.in 8 | certifi==2020.6.20 # via requests 9 | chardet==3.0.4 # via requests 10 | idna==2.10 # via requests 11 | llvmlite==0.33.0 # via numba 12 | numba==0.50.1 # via -r requirements.in 13 | numpy==1.19.1 # via -r requirements.in, numba 14 | requests==2.24.0 # via -r requirements.in 15 | six==1.15.0 # via anytree 16 | urllib3==1.25.10 # via requests 17 | 18 | # The following packages are considered to be unsafe in a requirements file: 19 | # setuptools 20 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_scan/node.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from python_polar_coding.polar_codes.fast_scan import FastSCANNode 4 | 5 | from ..base import NodeTypes 6 | 7 | 8 | class GFastSCANNode(FastSCANNode): 9 | supported_nodes = ( 10 | NodeTypes.ZERO, 11 | NodeTypes.ONE, 12 | NodeTypes.REPETITION, 13 | NodeTypes.SINGLE_PARITY_CHECK, 14 | NodeTypes.G_REPETITION, 15 | NodeTypes.RG_PARITY, 16 | ) 17 | 18 | def get_decoding_params(self) -> Dict: 19 | return dict( 20 | node_type=self.node_type, 21 | llr=self.alpha, 22 | mask_steps=self.mask_steps, 23 | last_chunk_type=self.last_chunk_type, 24 | ) 25 | -------------------------------------------------------------------------------- /tests/test_sc/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.sc import SCPolarCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestSCCode_1024_512(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = SCPolarCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 512, 12 | } 13 | 14 | 15 | class TestSCCode_1024_256(BasicVerifyPolarCode, TestCase): 16 | polar_code_class = SCPolarCodec 17 | code_parameters = { 18 | 'N': 1024, 19 | 'K': 256, 20 | } 21 | 22 | 23 | class TestSCCode_1024_768(BasicVerifyPolarCode, TestCase): 24 | polar_code_class = SCPolarCodec 25 | code_parameters = { 26 | 'N': 1024, 27 | 'K': 768, 28 | } 29 | -------------------------------------------------------------------------------- /examples/modelling/rc_scan/8192_4096.py: -------------------------------------------------------------------------------- 1 | from examples.modelling import rc_scan_executor 2 | 3 | CODE_RATES = [0.5, ] 4 | # From 1.5 to 3 with step 0.25 dB 5 | SNR_RANGE = [i/4 for i in range(6, 13)] 6 | MESSAGES_PER_EXPERIMENT = 100 7 | REPETITIONS = 100 8 | CODE_LENGTH = 8192 9 | MAX_WORKERS = 7 10 | ITERATIONS = [ 11 | {'design_snr': 1.4, 'iterations': 1}, 12 | {'design_snr': 1.4, 'iterations': 2}, 13 | {'design_snr': 1.4, 'iterations': 4}, 14 | ] 15 | 16 | 17 | if __name__ == '__main__': 18 | rc_scan_executor( 19 | codeword_length=CODE_LENGTH, 20 | code_rates=CODE_RATES, 21 | snr_range=SNR_RANGE, 22 | task_repetitions=REPETITIONS, 23 | messages_per_task=MESSAGES_PER_EXPERIMENT, 24 | additional_code_params=ITERATIONS, 25 | ) 26 | -------------------------------------------------------------------------------- /tests/test_fast_ssc/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.fast_ssc import FastSSCPolarCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestFastSSCCode_1024_512(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = FastSSCPolarCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 512, 12 | } 13 | 14 | 15 | class TestFastSSCCode_1024_256(BasicVerifyPolarCode, TestCase): 16 | polar_code_class = FastSSCPolarCodec 17 | code_parameters = { 18 | 'N': 1024, 19 | 'K': 256, 20 | } 21 | 22 | 23 | class TestFastSSCCode_1024_768(BasicVerifyPolarCode, TestCase): 24 | polar_code_class = FastSSCPolarCodec 25 | code_parameters = { 26 | 'N': 1024, 27 | 'K': 768, 28 | } 29 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/pcc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def bhattacharyya_bounds(codeword_length: int, design_snr: float): 5 | """Estimate Bhattacharyya bounds of bit channels of polar code.""" 6 | bhattacharya_bounds = np.zeros(codeword_length, dtype=np.double) 7 | snr = np.power(10, design_snr / 10) 8 | bhattacharya_bounds[0] = np.exp(-snr) 9 | 10 | for level in range(1, int(np.log2(codeword_length)) + 1): 11 | B = np.power(2, level) 12 | 13 | for j in range(int(B / 2)): 14 | val = bhattacharya_bounds[j] 15 | # TODO: refactor with `logdomain_diff` same with Matlab model 16 | bhattacharya_bounds[j] = 2 * val - np.power(val, 2) 17 | bhattacharya_bounds[int(B / 2 + j)] = np.power(val, 2) 18 | 19 | return bhattacharya_bounds 20 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_scan/functions.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | 5 | @numba.njit 6 | def compute_repetition_beta(alpha) -> np.array: 7 | """Compute beta value for Repetition node.""" 8 | alpha_sum = np.sum(alpha) 9 | return -1 * alpha + alpha_sum 10 | 11 | 12 | @numba.njit 13 | def compute_spc_beta(alpha) -> np.array: 14 | """Compute beta value for Single parity node.""" 15 | all_sign = np.sign(np.prod(alpha)) 16 | abs_alpha = np.fabs(alpha) 17 | first_min_idx, second_min_idx = np.argsort(abs_alpha)[:2] 18 | 19 | result = np.sign(alpha) * all_sign 20 | for i in range(result.size): 21 | if i == first_min_idx: 22 | result[i] *= abs_alpha[second_min_idx] 23 | else: 24 | result[i] *= abs_alpha[first_min_idx] 25 | 26 | return result 27 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_ssc/node.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from python_polar_coding.polar_codes.fast_ssc import FastSSCNode 4 | 5 | from ..base import NodeTypes 6 | 7 | 8 | class GFastSSCNode(FastSSCNode): 9 | """Decoder for Generalized Fast SSC code. 10 | 11 | Based on: https://arxiv.org/pdf/1804.09508.pdf 12 | 13 | """ 14 | supported_nodes = ( 15 | NodeTypes.ZERO, 16 | NodeTypes.ONE, 17 | NodeTypes.SINGLE_PARITY_CHECK, 18 | NodeTypes.REPETITION, 19 | NodeTypes.RG_PARITY, 20 | NodeTypes.G_REPETITION, 21 | ) 22 | 23 | def get_decoding_params(self) -> Dict: 24 | return dict( 25 | node_type=self.node_type, 26 | llr=self.alpha, 27 | mask_steps=self.mask_steps, 28 | last_chunk_type=self.last_chunk_type, 29 | ) 30 | -------------------------------------------------------------------------------- /python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /examples/simple_simulation.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from python_polar_coding.channels.simple import SimpleBPSKModulationAWGN 4 | from python_polar_coding.polar_codes import FastSSCPolarCodec 5 | from python_polar_coding.simulation import simulation_task 6 | 7 | 8 | def fast_ssc_simulation(): 9 | code = FastSSCPolarCodec(N=4096, K=1024, design_snr=2.0) 10 | channel = SimpleBPSKModulationAWGN(fec_rate=code.K/code.N) 11 | snr_range = [1.0, 1.5, 2.0, 2.5, 3.0] 12 | 13 | results = list() 14 | 15 | for snr in snr_range: 16 | start = datetime.now() 17 | result = simulation_task(code=code, 18 | channel=channel, 19 | snr_db=snr, 20 | messages=1000) 21 | end = datetime.now() 22 | print(f'Experiment took {(end-start).seconds} seconds') 23 | results.append(result) 24 | 25 | for r in results: 26 | print(r) 27 | 28 | 29 | if __name__ == '__main__': 30 | fast_ssc_simulation() 31 | -------------------------------------------------------------------------------- /python_polar_coding/simulation/http.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | def prepare( 5 | url, 6 | snr_range, 7 | required_messages, 8 | codes, 9 | per_experiment=1000, 10 | ): 11 | params = { 12 | 'codes': codes, 13 | 'snr_range': snr_range, 14 | 'required_messages': required_messages, 15 | 'per_experiment': per_experiment, 16 | } 17 | try: 18 | return requests.post(f'{url}/prepare', json=params) 19 | except Exception as e: 20 | print(e) 21 | 22 | 23 | def get_params(url): 24 | try: 25 | resp = requests.put(f'{url}/get-params', json={}) 26 | return resp.status_code, resp.json() 27 | except Exception as e: 28 | print(e) 29 | 30 | 31 | def save_result(url, result, code_id, code_type, cls, channel_type): 32 | result.update({'route_params': { 33 | 'code_id': code_id, 34 | 'code_type': code_type, 35 | 'channel_type': channel_type, 36 | 'type': cls, 37 | }}) 38 | try: 39 | return requests.post(f'{url}/save-result', json=result) 40 | except Exception as e: 41 | print(e) 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Grigory Timofeev & contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_ssc/codec.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from python_polar_coding.polar_codes.fast_ssc import FastSSCPolarCodec 4 | 5 | from .decoder import GFastSSCDecoder 6 | 7 | 8 | class GFastSSCPolarCodec(FastSSCPolarCodec): 9 | """Generalized Fast SSC code. 10 | 11 | Based on: https://arxiv.org/pdf/1804.09508.pdf 12 | 13 | """ 14 | decoder_class = GFastSSCDecoder 15 | 16 | def __init__( 17 | self, 18 | N: int, 19 | K: int, 20 | design_snr: float = 0.0, 21 | mask: Union[str, None] = None, 22 | pcc_method: str = FastSSCPolarCodec.BHATTACHARYYA, 23 | AF: int = 0, 24 | ): 25 | 26 | self.AF = AF 27 | super().__init__( 28 | N=N, 29 | K=K, 30 | design_snr=design_snr, 31 | mask=mask, 32 | pcc_method=pcc_method, 33 | ) 34 | 35 | def init_decoder(self): 36 | return self.decoder_class(n=self.n, mask=self.mask, AF=self.AF) 37 | 38 | def to_dict(self): 39 | d = super().to_dict() 40 | d.update({'AF': self.AF}) 41 | return d 42 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc_list/codec.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from ..base import BasePolarCodec 4 | from .decoder import SCListDecoder 5 | 6 | 7 | class SCListPolarCodec(BasePolarCodec): 8 | """Polar code with SC List decoding algorithm.""" 9 | decoder_class = SCListDecoder 10 | 11 | def __init__(self, N: int, K: int, 12 | design_snr: float = 0.0, 13 | is_systematic: bool = True, 14 | mask: Union[str, None] = None, 15 | pcc_method: str = BasePolarCodec.BHATTACHARYYA, 16 | L: int = 1): 17 | 18 | self.L = L 19 | super().__init__(N=N, K=K, 20 | is_systematic=is_systematic, 21 | design_snr=design_snr, 22 | mask=mask, 23 | pcc_method=pcc_method) 24 | 25 | def init_decoder(self): 26 | return self.decoder_class(n=self.n, mask=self.mask, 27 | is_systematic=self.is_systematic, L=self.L) 28 | 29 | def to_dict(self): 30 | d = super().to_dict() 31 | d.update({'L': self.L}) 32 | return d 33 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/g_fast_scan/codec.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from python_polar_coding.polar_codes.rc_scan import RCSCANPolarCodec 4 | 5 | from .decoder import GFastSCANDecoder 6 | 7 | 8 | class GFastSCANCodec(RCSCANPolarCodec): 9 | decoder_class = GFastSCANDecoder 10 | 11 | def __init__( 12 | self, 13 | N: int, 14 | K: int, 15 | design_snr: float = 0.0, 16 | mask: Union[str, None] = None, 17 | pcc_method: str = RCSCANPolarCodec.BHATTACHARYYA, 18 | AF: int = 0, 19 | I: int = 1, 20 | * args, **kwargs, 21 | ): 22 | 23 | self.AF = AF 24 | super().__init__( 25 | N=N, 26 | K=K, 27 | design_snr=design_snr, 28 | mask=mask, 29 | pcc_method=pcc_method, 30 | I=I, 31 | ) 32 | 33 | def init_decoder(self): 34 | return self.decoder_class( 35 | n=self.n, 36 | mask=self.mask, 37 | AF=self.AF, 38 | I=self.I, 39 | ) 40 | 41 | def to_dict(self): 42 | d = super().to_dict() 43 | d.update({'AF': self.AF}) 44 | return d 45 | -------------------------------------------------------------------------------- /python_polar_coding/simulation/functions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numba import njit 3 | 4 | 5 | @njit 6 | def generate_binary_message(size): 7 | """Generate binary message.""" 8 | return np.random.randint(0, 2, size) 9 | 10 | 11 | @njit 12 | def compute_fails(expected, decoded): 13 | """Compute number of fails in decoded message comparing with expected.""" 14 | bit_errors = np.sum(expected != decoded) 15 | frame_error = int(bit_errors > 0) 16 | return bit_errors, frame_error 17 | 18 | 19 | def transmission(code, channel, snr_db): 20 | message = np.random.randint(0, 2, code.K) 21 | encoded = code.encode(message) 22 | llr = channel.transmit(message=encoded, snr_db=snr_db) 23 | decoded = code.decode(llr) 24 | 25 | fails = compute_fails(expected=message, decoded=decoded) 26 | bit_errors = fails 27 | frame_errors = int(fails > 0) 28 | 29 | return bit_errors, frame_errors 30 | 31 | 32 | def simulation_task(code, channel, snr_db, messages): 33 | bit_errors, frame_errors = 0, 0 34 | 35 | for m in range(messages): 36 | be, fe = transmission(code, channel, snr_db) 37 | bit_errors += be 38 | frame_errors += fe 39 | 40 | return { 41 | 'snr_db': snr_db, 42 | 'bits': messages * code.K, 43 | 'bit_errors': bit_errors, 44 | 'frames': messages, 45 | 'frame_errors': frame_errors, 46 | } 47 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/crc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PyCRC.CRC32 import CRC32 3 | from PyCRC.CRCCCITT import CRCCCITT 4 | 5 | from python_polar_coding.polar_codes import utils 6 | 7 | 8 | class CRC: 9 | """CRC encoder. 10 | 11 | Supports CRC 32 and CRC 16 CCITT. 12 | 13 | """ 14 | crc_classes = { 15 | 16: CRCCCITT, 16 | 32: CRC32, 17 | } 18 | 19 | def __init__(self, crc_size): 20 | self.crc_size = crc_size 21 | self.crc_coder = self.crc_classes[crc_size]() 22 | 23 | def compute_crc(self, message: np.array) -> np.array: 24 | """Compute CRC value.""" 25 | return utils.int_to_bin_array( 26 | value=self._compute_crc(message), 27 | size=self.crc_size, 28 | ) 29 | 30 | def _compute_crc(self, message: np.array) -> int: 31 | """Compute CRC bytes value.""" 32 | bit_string = ''.join(str(m) for m in message) 33 | byte_string = utils.bitstring_to_bytes(bit_string) 34 | return self.crc_coder.calculate(byte_string) 35 | 36 | def check_crc(self, message: np.array) -> bool: 37 | """Check if message has errors or not using CRC.""" 38 | received_crc = int( 39 | ''.join([str(m) for m in message[-self.crc_size::]]), 40 | 2 41 | ) 42 | check_crc = self._compute_crc(message[:-self.crc_size]) 43 | return received_crc == check_crc 44 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/rc_scan/codec.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.base import BasePolarCodec 6 | 7 | from .decoder import RCSCANDecoder 8 | 9 | 10 | class RCSCANPolarCodec(BasePolarCodec): 11 | """Polar code with RC-SCAN decoding algorithm. 12 | 13 | Based on: https://arxiv.org/pdf/1510.06495.pdf, 14 | DOI: 10.1109/DASIP.2015.7367252 15 | 16 | """ 17 | decoder_class = RCSCANDecoder 18 | 19 | def __init__( 20 | self, 21 | N: int, 22 | K: int, 23 | design_snr: float = 0.0, 24 | mask: Union[str, None] = None, 25 | pcc_method: str = BasePolarCodec.BHATTACHARYYA, 26 | I: int = 1, 27 | * args, **kwargs, 28 | ): 29 | self.I = I 30 | super().__init__(N=N, K=K, 31 | is_systematic=True, 32 | design_snr=design_snr, 33 | mask=mask, 34 | pcc_method=pcc_method) 35 | 36 | def init_decoder(self): 37 | return self.decoder_class(n=self.n, mask=self.mask, I=self.I) 38 | 39 | def to_dict(self): 40 | d = super().to_dict() 41 | d.update({'I': self.I}) 42 | return d 43 | 44 | def decode(self, received_message: np.array) -> np.array: 45 | """Decode received message presented as LLR values.""" 46 | return self.decoder(received_message) 47 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/alpha.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | 5 | @numba.njit 6 | def compute_alpha(a: np.array, b: np.array) -> np.array: 7 | """Basic function to compute intermediate LLR values.""" 8 | c = np.zeros(a.shape[0]) 9 | for i in range(c.shape[0]): 10 | c[i] = ( 11 | np.sign(a[i]) * 12 | np.sign(b[i]) * 13 | np.fabs(np.array([a[i], b[i]])).min() 14 | ) 15 | return c 16 | 17 | 18 | @numba.njit 19 | def compute_left_alpha(llr: np.array) -> np.array: 20 | """Compute Alpha for left node during SC-based decoding.""" 21 | N = llr.size // 2 22 | left = llr[:N] 23 | right = llr[N:] 24 | return compute_alpha(left, right) 25 | 26 | 27 | @numba.njit 28 | def compute_right_alpha(llr: np.array, left_beta: np.array) -> np.array: 29 | """Compute Alpha for right node during SC-based decoding.""" 30 | N = llr.size // 2 31 | left = llr[:N] 32 | right = llr[N:] 33 | return right - (2 * left_beta - 1) * left 34 | 35 | 36 | @numba.njit 37 | def function_1(a: np.array, b: np.array, c: np.array) -> np.array: 38 | """Function 1. 39 | 40 | Source: doi:10.1007/s12243-018-0634-7, formula 1. 41 | 42 | """ 43 | return compute_alpha(a, b + c) 44 | 45 | 46 | @numba.njit 47 | def function_2(a: np.array, b: np.array, c: np.array) -> np.array: 48 | """Function 2. 49 | 50 | Source: doi:10.1007/s12243-018-0634-7, formula 2. 51 | 52 | """ 53 | return compute_alpha(a, b) + c 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | from pip._internal.network.session import PipSession 3 | from pip._internal.req import parse_requirements 4 | 5 | with open('README.md', 'r') as fh: 6 | long_description = fh.read() 7 | 8 | requirements = parse_requirements( 9 | filename='requirements.txt', 10 | session=PipSession() 11 | ) 12 | 13 | setuptools.setup( 14 | name='python-polar-coding', 15 | 16 | version='0.0.1', 17 | 18 | description='Polar coding implementation in Python', 19 | 20 | long_description=long_description, 21 | 22 | long_description_content_type='text/markdown', 23 | 24 | url='https://github.com/fr0mhell/python-polar-coding', 25 | 26 | author='Grigory Timofeev', 27 | 28 | author_email='t1m0feev.grigorij@gmail.com', 29 | 30 | classifiers=[ 31 | 'Development Status :: 3 - Alpha', 32 | 'License :: OSI Approved :: MIT License', 33 | 'Programming Language :: Python :: 3', 34 | 'Programming Language :: Python :: 3.6', 35 | 'Programming Language :: Python :: 3.7', 36 | 'Programming Language :: Python :: 3.8', 37 | 'Programming Language :: Python :: 3 :: Only', 38 | ], 39 | 40 | keywords='polar codes, fec, simulation', 41 | 42 | packages=setuptools.find_packages(), 43 | 44 | python_requires='>=3.6, <4', 45 | 46 | install_requires=[req.requirement for req in requirements], 47 | 48 | project_urls={ # Optional 49 | 'Bug Report': 'https://github.com/fr0mhell/python-polar-coding/issues', 50 | 'Source': 'https://github.com/fr0mhell/python-polar-coding', 51 | }, 52 | ) 53 | -------------------------------------------------------------------------------- /python_polar_coding/modems/simple.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | 5 | class SimpleBPSKModem: 6 | """Simple model of BPSK-modem. 7 | 8 | Implemented for the comparison with the SC decoder proposed by H. Vangala, 9 | E. Viterbo, and Yi Hong (See `PlotPC and PlotPCSystematic`): 10 | https://ecse.monash.edu/staff/eviterbo/polarcodes.html. 11 | 12 | """ 13 | noise_power = 2.0 14 | 15 | def __init__(self, fec_rate: float, snr_db: float): 16 | self.fec_rate = fec_rate 17 | self.symbol_energy = self._compute_symbol_energy(snr_db, self.fec_rate) 18 | 19 | def modulate(self, message: np.array) -> np.array: 20 | """BPSK modulation.""" 21 | return self._modulate(message, self.symbol_energy) 22 | 23 | def demodulate(self, transmitted: np.array) -> np.array: 24 | """BPSK demodulation.""" 25 | return self._llr_detect(transmitted, self.symbol_energy, self.noise_power) # noqa 26 | 27 | @staticmethod 28 | @numba.njit 29 | def _compute_symbol_energy(snr_db, fec_rate): 30 | snr = np.power(10, snr_db / 10) 31 | return snr * 2 * fec_rate 32 | 33 | @staticmethod 34 | @numba.njit 35 | def _modulate(message: np.array, symbol_energy: float) -> np.array: 36 | """BPSK modulation.""" 37 | return (2 * message - 1) * np.sqrt(symbol_energy) 38 | 39 | @staticmethod 40 | @numba.njit 41 | def _llr_detect(signal: np.array, symbol_energy: float, noise_power: float) -> np.array: 42 | """LLR detection of BPSK signal with AWGN.""" 43 | return -(4 * np.sqrt(symbol_energy) / noise_power) * signal 44 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/fast_ssc/decoder.py: -------------------------------------------------------------------------------- 1 | from python_polar_coding.polar_codes.base import BaseTreeDecoder 2 | from python_polar_coding.polar_codes.base.functions import ( 3 | compute_left_alpha, 4 | compute_parent_beta_hard, 5 | compute_right_alpha, 6 | ) 7 | 8 | from .node import FastSSCNode 9 | 10 | 11 | class FastSSCDecoder(BaseTreeDecoder): 12 | """Implements Fast SSC decoding algorithm.""" 13 | node_class = FastSSCNode 14 | 15 | def _compute_intermediate_alpha(self, leaf): 16 | """Compute intermediate Alpha values (LLR).""" 17 | for node in leaf.path[1:]: 18 | if node.is_computed: 19 | continue 20 | 21 | # No need to compute zero node because output is vector of zeros 22 | if node.is_zero: 23 | continue 24 | 25 | parent_alpha = node.parent.alpha 26 | 27 | if node.is_left: 28 | node.alpha = compute_left_alpha(parent_alpha) 29 | continue 30 | 31 | left_node = node.siblings[0] 32 | left_beta = left_node.beta 33 | node.alpha = compute_right_alpha(parent_alpha, left_beta) 34 | node.is_computed = True 35 | 36 | def _compute_intermediate_beta(self, node): 37 | """Compute intermediate Beta values (BIT).""" 38 | if node.is_left: 39 | return 40 | 41 | if node.is_root: 42 | return 43 | 44 | parent = node.parent 45 | left = node.siblings[0] 46 | parent.beta = compute_parent_beta_hard(left.beta, node.beta) 47 | return self._compute_intermediate_beta(parent) 48 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/rc_scan/node.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from python_polar_coding.polar_codes.base.functions.beta_soft import ( 4 | compute_beta_soft, 5 | one, 6 | zero, 7 | ) 8 | 9 | from ..base import NodeTypes, SoftNode 10 | 11 | 12 | class RCSCANNode(SoftNode): 13 | supported_nodes = ( 14 | NodeTypes.ZERO, 15 | NodeTypes.ONE, 16 | ) 17 | 18 | @property 19 | def is_zero(self) -> bool: 20 | """Check is the node is Zero node.""" 21 | return self.node_type == NodeTypes.ZERO 22 | 23 | @property 24 | def is_one(self) -> bool: 25 | """Check is the node is One node.""" 26 | return self.node_type == NodeTypes.ONE 27 | 28 | def get_decoding_params(self) -> Dict: 29 | return dict( 30 | node_type=self.node_type, 31 | llr=self.alpha, 32 | ) 33 | 34 | def compute_leaf_beta(self): 35 | """Do nothing for ZERO and ONE nodes. 36 | 37 | Unlike SC-based decoders SCAN decoders does not make decisions 38 | in leaves. 39 | 40 | """ 41 | if self.is_one or self.is_zero: 42 | return 43 | self.beta = compute_beta_soft(self.node_type, self.alpha) 44 | 45 | def initialize_leaf_beta(self): 46 | """Initialize BETA values on tree building. 47 | 48 | Initialize ZERO and ONE nodes following to Section III 49 | doi:10.1109/jsac.2014.140515 50 | 51 | """ 52 | if not self.is_leaf: 53 | return 54 | 55 | if self.is_zero: 56 | self._beta = zero(self.alpha) 57 | if self.is_one: 58 | self._beta = one(self.alpha) 59 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numba import njit 3 | 4 | 5 | def bitstring_to_bytes(s): 6 | """Converts bit string into bytes.""" 7 | return int(s, 2).to_bytes(len(s) // 8, byteorder='big') 8 | 9 | 10 | def int_to_bin_array(value: int, size: int) -> np.array: 11 | """Get binary representation in a list form of given value. 12 | 13 | Args: 14 | value (int): value for binary representation. 15 | size (int): size of binary representation. 16 | 17 | Returns: 18 | (list): binary representation of given value and size. 19 | 20 | """ 21 | return np.array([int(bit) for bit in np.binary_repr(value, width=size)]) 22 | 23 | 24 | def reverse_bits(value: int, size: int) -> int: 25 | """Reverse bits of n-bit integer value.""" 26 | return int(''.join(reversed(np.binary_repr(value, width=size))), 2) 27 | 28 | 29 | @njit 30 | def lowerconv(upperdecision: int, upperllr: float, lowerllr: float) -> float: 31 | """PERFORMS IN LOG DOMAIN 32 | llr = lowerllr * upperllr, if uppperdecision == 0 33 | llr = lowerllr / upperllr, if uppperdecision == 1 34 | 35 | """ 36 | if upperdecision == 0: 37 | return lowerllr + upperllr 38 | else: 39 | return lowerllr - upperllr 40 | 41 | 42 | @njit 43 | def logdomain_sum(x: float, y: float) -> float: 44 | """""" 45 | if x < y: 46 | return y + np.log(1 + np.exp(x - y)) 47 | else: 48 | return x + np.log(1 + np.exp(y - x)) 49 | 50 | 51 | @njit 52 | def upperconv(llr1: float, llr2: float) -> float: 53 | """PERFORMS IN LOG DOMAIN 54 | llr = (llr1 * llr2 + 1) / (llr1 + llr2) 55 | 56 | """ 57 | return logdomain_sum(llr1 + llr2, 0) - logdomain_sum(llr1, llr2) 58 | 59 | 60 | def splits(start, end): 61 | while start <= end: 62 | yield start 63 | start *= 2 64 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/rc_scan/functions.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | from python_polar_coding.polar_codes.base.functions.alpha import ( 5 | function_1, 6 | function_2, 7 | ) 8 | 9 | from ..base import INFINITY 10 | 11 | 12 | @numba.njit 13 | def compute_beta_zero_node(alpha): 14 | """Compute beta values for ZERO node. 15 | 16 | https://arxiv.org/pdf/1510.06495.pdf Section III.C. 17 | 18 | """ 19 | return np.ones(alpha.size, dtype=np.double) * INFINITY 20 | 21 | 22 | @numba.njit 23 | def compute_beta_one_node(alpha): 24 | """Compute beta values for ONE node. 25 | 26 | https://arxiv.org/pdf/1510.06495.pdf Section III.C. 27 | 28 | """ 29 | return np.zeros(alpha.size, dtype=np.double) 30 | 31 | 32 | @numba.njit 33 | def compute_left_alpha(parent_alpha, beta): 34 | """Compute LLR for left node.""" 35 | N = parent_alpha.size // 2 36 | left_parent_alpha = parent_alpha[:N] 37 | right_parent_alpha = parent_alpha[N:] 38 | 39 | return function_1(left_parent_alpha, right_parent_alpha, beta) 40 | 41 | 42 | @numba.njit 43 | def compute_right_alpha(parent_alpha, beta): 44 | """Compute LLR for right node.""" 45 | N = parent_alpha.size // 2 46 | left_parent_alpha = parent_alpha[:N] 47 | right_parent_alpha = parent_alpha[N:] 48 | 49 | return function_2(left_parent_alpha, beta, right_parent_alpha) 50 | 51 | 52 | @numba.njit 53 | def compute_parent_beta(left_beta, right_beta, parent_alpha): 54 | """Compute bits of a parent Node.""" 55 | N = parent_alpha.size // 2 56 | left_parent_alpha = parent_alpha[:N] 57 | right_parent_alpha = parent_alpha[N:] 58 | 59 | result = np.zeros(parent_alpha.size, dtype=np.double) 60 | 61 | result[:N] = function_1(left_beta, right_beta, right_parent_alpha) 62 | result[N:] = function_2(left_beta, left_parent_alpha, right_beta) 63 | 64 | return result 65 | -------------------------------------------------------------------------------- /examples/modelling/runner.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | from datetime import datetime 3 | from functools import partial 4 | from concurrent import futures 5 | 6 | from examples.modelling.functions import ( 7 | generate_simulation_parameters, 8 | simulation_task, 9 | ) 10 | from examples.modelling.mongo import DB_NAME 11 | from python_polar_coding.channels.simple import SimpleBPSKModulationAWGN 12 | 13 | 14 | def run_model(workers, task, list_of_parameters): 15 | """""" 16 | print('Start execution at', datetime.now().strftime('%H:%M:%S %d-%m-%Y')) 17 | 18 | with futures.ProcessPoolExecutor(max_workers=workers) as ex: 19 | run_tasks = {ex.submit(task, *params): params for params in 20 | list_of_parameters} 21 | for future in futures.as_completed(run_tasks): 22 | try: 23 | future.result() 24 | except Exception as exc: 25 | print(exc) 26 | 27 | print('Finish execution at', datetime.now().strftime('%H:%M:%S %d-%m-%Y')) 28 | 29 | 30 | def executor(code_class, codeword_length, code_rates, snr_range, 31 | task_repetitions, messages_per_task, collection_name, 32 | additional_code_params=None, db_name=DB_NAME, 33 | channel_class=SimpleBPSKModulationAWGN, workers=None): 34 | """""" 35 | list_of_task_parameters = generate_simulation_parameters( 36 | code_cls=code_class, 37 | channel_cls=channel_class, 38 | N=codeword_length, 39 | code_rates=code_rates, 40 | snr_range=snr_range, 41 | repetitions=task_repetitions, 42 | additional_code_params=additional_code_params, 43 | ) 44 | task = partial( 45 | simulation_task, 46 | db_name=db_name, 47 | collection=collection_name, 48 | messages=messages_per_task 49 | ) 50 | workers = workers or multiprocessing.cpu_count() - 2 51 | 52 | run_model(workers, task, list_of_task_parameters) 53 | -------------------------------------------------------------------------------- /tests/mixins.py: -------------------------------------------------------------------------------- 1 | class CodeModellingMixin: 2 | messages = 10000 3 | 4 | def test_snr_1_0_db(self): 5 | snr_db = 1.0 6 | self._modelling_test(snr_db=snr_db) 7 | 8 | def test_snr_1_25_db(self): 9 | snr_db = 1.25 10 | self._modelling_test(snr_db=snr_db) 11 | 12 | def test_snr_1_5_db(self): 13 | snr_db = 1.5 14 | self._modelling_test(snr_db=snr_db) 15 | 16 | def test_snr_1_75_db(self): 17 | snr_db = 1.75 18 | self._modelling_test(snr_db=snr_db) 19 | 20 | def test_snr_2_0_db(self): 21 | snr_db = 2.0 22 | self._modelling_test(snr_db=snr_db) 23 | 24 | def test_snr_2_25_db(self): 25 | snr_db = 2.25 26 | self._modelling_test(snr_db=snr_db) 27 | 28 | def test_snr_2_5_db(self): 29 | snr_db = 2.5 30 | self._modelling_test(snr_db=snr_db) 31 | 32 | def test_snr_2_75_db(self): 33 | snr_db = 2.75 34 | self._modelling_test(snr_db=snr_db) 35 | 36 | def test_snr_3_0_db(self): 37 | snr_db = 3.0 38 | self._modelling_test(snr_db=snr_db) 39 | 40 | def test_snr_3_25_db(self): 41 | snr_db = 3.25 42 | self._modelling_test(snr_db=snr_db) 43 | 44 | def test_snr_3_5_db(self): 45 | snr_db = 3.5 46 | self._modelling_test(snr_db=snr_db) 47 | 48 | def test_snr_3_75_db(self): 49 | snr_db = 3.75 50 | self._modelling_test(snr_db=snr_db) 51 | 52 | def test_snr_4_0_db(self): 53 | snr_db = 4.0 54 | self._modelling_test(snr_db=snr_db) 55 | 56 | def test_snr_4_25_db(self): 57 | snr_db = 4.25 58 | self._modelling_test(snr_db=snr_db) 59 | 60 | def test_snr_4_5_db(self): 61 | snr_db = 4.5 62 | self._modelling_test(snr_db=snr_db) 63 | 64 | def test_snr_4_75_db(self): 65 | snr_db = 4.75 66 | self._modelling_test(snr_db=snr_db) 67 | 68 | def test_snr_5_0_db(self): 69 | snr_db = 5.0 70 | self._modelling_test(snr_db=snr_db) 71 | -------------------------------------------------------------------------------- /tests/test_rc_scan/test_node.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.base import INFINITY 6 | from python_polar_coding.polar_codes.base.functions import NodeTypes 7 | from python_polar_coding.polar_codes.rc_scan import RCSCANNode 8 | 9 | 10 | class TestRCSCANNode(TestCase): 11 | 12 | @classmethod 13 | def setUpClass(cls): 14 | cls.llr = np.array([-2.7273, 8.7327, -0.1087, 1.6463, ]) 15 | 16 | def test_zero_node(self): 17 | node = RCSCANNode(mask=np.zeros(4)) 18 | self.assertTrue(node.is_zero) 19 | 20 | node.llr = self.llr 21 | node.initialize_leaf_beta() 22 | np.testing.assert_equal( 23 | node.beta, 24 | np.ones(4) * INFINITY, 25 | ) 26 | 27 | def test_one_node(self): 28 | node = RCSCANNode(mask=np.ones(4)) 29 | self.assertTrue(node.is_one) 30 | 31 | node.llr = self.llr 32 | node.initialize_leaf_beta() 33 | np.testing.assert_equal( 34 | node.beta, 35 | np.zeros(4) 36 | ) 37 | 38 | def test_with_multiple_nodes(self): 39 | node = RCSCANNode(mask=np.array([ 40 | 0, 41 | 1, 42 | 0, 0, 43 | 0, 0, 44 | 1, 1, 45 | 0, 0, 0, 0, 46 | 1, 1, 1, 1, 47 | ])) 48 | 49 | leaf_path_lengths = [5, 5, 4, 4, 4, 3, 3] 50 | leaf_masks = [ 51 | np.array([0, ]), np.array([1, ]), np.array([0, 0, ]), 52 | np.array([0, 0, ]), np.array([1, 1, ]), 53 | np.array([0, 0, 0, 0, ]), np.array([1, 1, 1, 1, ]), 54 | ] 55 | leaf_types = [ 56 | NodeTypes.ZERO, NodeTypes.ONE, NodeTypes.ZERO, 57 | NodeTypes.ZERO, NodeTypes.ONE, 58 | NodeTypes.ZERO, NodeTypes.ONE, 59 | ] 60 | 61 | for i, leaf in enumerate(node.leaves): 62 | self.assertEqual(len(leaf.path), leaf_path_lengths[i]) 63 | np.testing.assert_equal(leaf.mask, leaf_masks[i]) 64 | self.assertTrue(leaf.node_type, leaf_types[i]) 65 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/encoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numba import njit 3 | 4 | 5 | class Encoder: 6 | """Polar Codes encoder.""" 7 | 8 | def __init__(self, 9 | mask: np.array, 10 | n: int, 11 | is_systematic: bool = True): 12 | 13 | self.n = n 14 | self.N = mask.shape[0] 15 | self.mask = mask 16 | self.is_systematic = is_systematic 17 | 18 | def encode(self, message: np.array) -> np.array: 19 | """Encode message with a polar code. 20 | 21 | Support both non-systematic and systematic encoding. 22 | 23 | """ 24 | precoded = self._precode(message) 25 | encoded = self._non_systematic_encode(precoded, self.n) 26 | 27 | if self.is_systematic: 28 | encoded *= self.mask 29 | encoded = self._non_systematic_encode(encoded, self.n) 30 | 31 | return encoded 32 | 33 | def _precode(self, message: np.array) -> np.array: 34 | """Apply polar code mask to information message. 35 | 36 | Replace 1's of polar code mask with bits of information message. 37 | 38 | """ 39 | precoded = np.zeros(self.N, dtype=int) 40 | precoded[self.mask == 1] = message 41 | return precoded 42 | 43 | @staticmethod 44 | @njit 45 | def _non_systematic_encode(message: np.array, n: int) -> np.array: 46 | """Non-systematic encoding. 47 | 48 | Args: 49 | message (numpy.array): precoded message to encode. 50 | 51 | Returns: 52 | message (numpy.array): non-systematically encoded message. 53 | 54 | """ 55 | for i in range(n - 1, -1, -1): 56 | pairs_per_group = step = np.power(2, n - i - 1) 57 | groups = np.power(2, i) 58 | 59 | for g in range(groups): 60 | start = 2 * g * step 61 | 62 | for p in range(pairs_per_group): 63 | message[p + start] = message[p + start] ^ message[p + start + step] 64 | message[p + start + step] = message[p + start + step] 65 | 66 | return message 67 | -------------------------------------------------------------------------------- /tests/test_fast_scan/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.fast_scan import FastSCANCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestFastSCANCodec_1024_512(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = FastSCANCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 512, 12 | 'I': 1, 13 | } 14 | 15 | 16 | class TestFastSCANCodec_1024_256(BasicVerifyPolarCode, TestCase): 17 | polar_code_class = FastSCANCodec 18 | code_parameters = { 19 | 'N': 1024, 20 | 'K': 256, 21 | 'I': 1, 22 | } 23 | 24 | 25 | class TestFastSCANCodec_1024_768(BasicVerifyPolarCode, TestCase): 26 | polar_code_class = FastSCANCodec 27 | code_parameters = { 28 | 'N': 1024, 29 | 'K': 768, 30 | 'I': 1, 31 | } 32 | 33 | 34 | # Iterations 2 35 | 36 | 37 | class TestFastSCANCodec_1024_512_iter_2(BasicVerifyPolarCode, TestCase): 38 | polar_code_class = FastSCANCodec 39 | code_parameters = { 40 | 'N': 1024, 41 | 'K': 512, 42 | 'I': 2, 43 | } 44 | 45 | 46 | class TestFastSCANCodec_1024_256_iter_2(BasicVerifyPolarCode, TestCase): 47 | polar_code_class = FastSCANCodec 48 | code_parameters = { 49 | 'N': 1024, 50 | 'K': 256, 51 | 'I': 2, 52 | } 53 | 54 | 55 | class TestFastSCANCodec_1024_768_iter_2(BasicVerifyPolarCode, TestCase): 56 | polar_code_class = FastSCANCodec 57 | code_parameters = { 58 | 'N': 1024, 59 | 'K': 768, 60 | 'I': 2, 61 | } 62 | 63 | 64 | # Iterations 4 65 | 66 | 67 | class TestFastSCANCodec_1024_512_iter_4(BasicVerifyPolarCode, TestCase): 68 | polar_code_class = FastSCANCodec 69 | code_parameters = { 70 | 'N': 1024, 71 | 'K': 512, 72 | 'I': 4, 73 | } 74 | 75 | 76 | class TestFastSCANCodec_1024_256_iter_4(BasicVerifyPolarCode, TestCase): 77 | polar_code_class = FastSCANCodec 78 | code_parameters = { 79 | 'N': 1024, 80 | 'K': 256, 81 | 'I': 4, 82 | } 83 | 84 | 85 | class TestFastSCANCodec_1024_768_iter_4(BasicVerifyPolarCode, TestCase): 86 | polar_code_class = FastSCANCodec 87 | code_parameters = { 88 | 'N': 1024, 89 | 'K': 768, 90 | 'I': 4, 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_rc_scan/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.rc_scan import RCSCANPolarCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestRCSCANCode_1024_512(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = RCSCANPolarCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 512, 12 | 'I': 1, 13 | } 14 | 15 | 16 | class TestRCSCANCode_1024_256(BasicVerifyPolarCode, TestCase): 17 | polar_code_class = RCSCANPolarCodec 18 | code_parameters = { 19 | 'N': 1024, 20 | 'K': 256, 21 | 'I': 1, 22 | } 23 | 24 | 25 | class TestRCSCANCode_1024_768(BasicVerifyPolarCode, TestCase): 26 | polar_code_class = RCSCANPolarCodec 27 | code_parameters = { 28 | 'N': 1024, 29 | 'K': 768, 30 | 'I': 1, 31 | } 32 | 33 | 34 | # Iterations 2 35 | 36 | 37 | class TestRCSCANCode_1024_512_iter_2(BasicVerifyPolarCode, TestCase): 38 | polar_code_class = RCSCANPolarCodec 39 | code_parameters = { 40 | 'N': 1024, 41 | 'K': 512, 42 | 'I': 2, 43 | } 44 | 45 | 46 | class TestRCSCANCode_1024_256_iter_2(BasicVerifyPolarCode, TestCase): 47 | polar_code_class = RCSCANPolarCodec 48 | code_parameters = { 49 | 'N': 1024, 50 | 'K': 256, 51 | 'I': 2, 52 | } 53 | 54 | 55 | class TestRCSCANCode_1024_768_iter_2(BasicVerifyPolarCode, TestCase): 56 | polar_code_class = RCSCANPolarCodec 57 | code_parameters = { 58 | 'N': 1024, 59 | 'K': 768, 60 | 'I': 2, 61 | } 62 | 63 | 64 | # Iterations 4 65 | 66 | 67 | class TestRCSCANCode_1024_512_iter_4(BasicVerifyPolarCode, TestCase): 68 | polar_code_class = RCSCANPolarCodec 69 | code_parameters = { 70 | 'N': 1024, 71 | 'K': 512, 72 | 'I': 4, 73 | } 74 | 75 | 76 | class TestRCSCANCode_1024_256_iter_4(BasicVerifyPolarCode, TestCase): 77 | polar_code_class = RCSCANPolarCodec 78 | code_parameters = { 79 | 'N': 1024, 80 | 'K': 256, 81 | 'I': 4, 82 | } 83 | 84 | 85 | class TestRCSCANCode_1024_768_iter_4(BasicVerifyPolarCode, TestCase): 86 | polar_code_class = RCSCANPolarCodec 87 | code_parameters = { 88 | 'N': 1024, 89 | 'K': 768, 90 | 'I': 4, 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_fast_ssc/test_node.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.base import NodeTypes 6 | from python_polar_coding.polar_codes.fast_ssc.decoder import FastSSCNode 7 | 8 | 9 | class FastSSCNodeTest(TestCase): 10 | 11 | @classmethod 12 | def setUpClass(cls): 13 | cls.llr = np.array([-2.7273, 8.7327, -0.1087, 1.6463, ]) 14 | 15 | def test_zero_node(self): 16 | node = FastSSCNode(np.zeros(4)) 17 | 18 | node.alpha = self.llr 19 | node() 20 | np.testing.assert_equal(node.beta, np.zeros(4)) 21 | 22 | def test_one_node(self): 23 | node = FastSSCNode(np.ones(4)) 24 | 25 | node.alpha = self.llr 26 | node() 27 | np.testing.assert_equal(node.beta, np.array([1, 0, 1, 0])) 28 | 29 | def test_spc_node(self): 30 | node = FastSSCNode(np.array([0, 1, 1, 1])) 31 | 32 | node.alpha = self.llr 33 | node() 34 | np.testing.assert_equal(node.beta, np.array([1, 0, 1, 0])) 35 | 36 | def test_repetition_node(self): 37 | node = FastSSCNode(np.array([0, 0, 0, 1])) 38 | 39 | node.alpha = self.llr 40 | node() 41 | np.testing.assert_equal(node.beta, np.array([0, 0, 0, 0])) 42 | 43 | def test_with_multiple_nodes(self): 44 | node = FastSSCNode(np.array([ 45 | 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 46 | ])) 47 | 48 | leaf_path_lengths = [4, 4, 3, 5, 5, 5, 5, 3] 49 | leaf_masks = [ 50 | np.array([1, 1]), 51 | np.array([0, 1]), 52 | np.array([0, 0, 0, 1]), 53 | np.array([1]), 54 | np.array([0]), 55 | np.array([1]), 56 | np.array([0]), 57 | np.array([0, 1, 1, 1]), 58 | ] 59 | leaf_types = [ 60 | NodeTypes.ONE, 61 | NodeTypes.REPETITION, 62 | NodeTypes.REPETITION, 63 | NodeTypes.ONE, 64 | NodeTypes.ZERO, 65 | NodeTypes.ONE, 66 | NodeTypes.ZERO, 67 | NodeTypes.SINGLE_PARITY_CHECK, 68 | ] 69 | 70 | for i, leaf in enumerate(node.leaves): 71 | self.assertEqual(len(leaf.path), leaf_path_lengths[i]) 72 | np.testing.assert_equal(leaf.mask, leaf_masks[i]) 73 | self.assertEqual(leaf.node_type, leaf_types[i]) 74 | -------------------------------------------------------------------------------- /python_polar_coding/channels/simple.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | 5 | class SimpleAWGNChannel: 6 | """Simple AWGN channel. 7 | 8 | Implemented for the comparison with the SC decoder proposed by H. Vangala, 9 | E. Viterbo, and Yi Hong (See `PlotPC and PlotPCSystematic`): 10 | https://ecse.monash.edu/staff/eviterbo/polarcodes.html. 11 | 12 | """ 13 | noise_power = 2.0 14 | 15 | def transmit(self, message: np.array) -> np.array: 16 | """Transmit BPSK-modulated message over AWGN channel.""" 17 | return self._add_noise(message, self.noise_power) 18 | 19 | @staticmethod 20 | @numba.njit 21 | def _add_noise(signal: np.array, noise_power: float) -> np.array: 22 | """Add AWGN noise to signal.""" 23 | noise = np.sqrt(noise_power / 2) * np.random.randn(signal.size) 24 | return signal + noise 25 | 26 | 27 | class SimpleBPSKModulationAWGN: 28 | """Simple model of BPSK-modulation + AWGN channel. 29 | 30 | Implemented for the comparison with the SC decoder proposed by H. Vangala, 31 | E. Viterbo, and Yi Hong (See `PlotPC and PlotPCSystematic`): 32 | https://ecse.monash.edu/staff/eviterbo/polarcodes.html. 33 | 34 | """ 35 | noise_power = 2.0 36 | 37 | def __init__(self, fec_rate: float): 38 | self.fec_rate = fec_rate 39 | 40 | def transmit(self, message: np.array, 41 | snr_db: float, 42 | with_noise: bool = True) -> np.array: 43 | """Transmit BPSK-modulated message over AWGN message.""" 44 | symbol_energy = self._compute_symbol_energy(snr_db, self.fec_rate) 45 | transmitted = self._modulate(message, symbol_energy) 46 | 47 | if with_noise: 48 | transmitted = self._add_noise(transmitted, self.noise_power) 49 | 50 | return self._llr_detection(transmitted, symbol_energy, self.noise_power) # noqa 51 | 52 | @staticmethod 53 | @numba.njit 54 | def _compute_symbol_energy(snr_db, fec_rate): 55 | snr = np.power(10, snr_db / 10) 56 | return snr * 2 * fec_rate 57 | 58 | @staticmethod 59 | @numba.njit 60 | def _modulate(message: np.array, symbol_energy: float) -> np.array: 61 | """BPSK modulation.""" 62 | return (2 * message - 1) * np.sqrt(symbol_energy) 63 | 64 | @staticmethod 65 | @numba.njit 66 | def _add_noise(signal: np.array, noise_power: float) -> np.array: 67 | """Add AWGN noise to signal.""" 68 | noise = np.sqrt(noise_power / 2) * np.random.randn(signal.size) 69 | return signal + noise 70 | 71 | @staticmethod 72 | @numba.njit 73 | def _llr_detection(signal: np.array, symbol_energy: float, noise_power: float) -> np.array: 74 | """LLR detection of BPSK signal with AWGN.""" 75 | return -(4 * np.sqrt(symbol_energy) / noise_power) * signal 76 | -------------------------------------------------------------------------------- /examples/modelling/functions.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from random import shuffle 3 | from datetime import datetime 4 | 5 | import numpy as np 6 | from pymongo import MongoClient 7 | 8 | from examples.modelling.mongo import URI 9 | 10 | 11 | def single_transmission(code, channel): 12 | """Transmit a message through a simple BPSK model. 13 | 14 | Args: 15 | code (PolarCode): Polar code tp simulate. 16 | channel (PSKModAWGNChannel): Transmission channels with configured SNR. 17 | 18 | Returns: 19 | bit_errors (int): Number of bit errors occurred while message 20 | transmission. 21 | word_errors (int): Number of messages transmitted with errors. 22 | 23 | """ 24 | message = np.random.randint(0, 2, code.K) 25 | encoded = code.encode(message) 26 | llr = channel.transmit(encoded) 27 | decoded = code.decode(llr) 28 | 29 | fails = np.sum(message != decoded) 30 | bit_errors = int(fails) 31 | word_errors = int(fails > 0) 32 | 33 | return bit_errors, word_errors 34 | 35 | 36 | def simulation_task(code, channel, db_name, collection, messages=1000): 37 | start = datetime.now() 38 | 39 | client = MongoClient(URI) 40 | bit_errors = word_errors = 0 41 | 42 | for m in range(messages): 43 | be, we = single_transmission(code, channel) 44 | bit_errors += be 45 | word_errors += we 46 | 47 | data = code.to_dict() 48 | 49 | end = datetime.now() 50 | data.update({ 51 | 'snr_db': channel.snr_db, 52 | 'bits': messages * code.K, 53 | 'bit_errors': bit_errors, 54 | 'word_errors': word_errors, 55 | 'words': messages, 56 | 'channels': str(channel), 57 | 'start': start, 58 | 'end': end 59 | }) 60 | 61 | client[db_name][collection].insert_one(data) 62 | 63 | iterations = getattr(code, '_iterations', -1) 64 | print(f'Execution took {end - start} ({iterations}).\n') 65 | 66 | 67 | def generate_simulation_parameters( 68 | code_cls, 69 | channel_cls, 70 | N, 71 | code_rates, 72 | snr_range, 73 | repetitions, 74 | additional_code_params=None 75 | ): 76 | """Get list of (PolarCode, Channel) pairs.""" 77 | additional_code_params = additional_code_params or [{}, ] 78 | 79 | combinations = [ 80 | ( 81 | code_cls( 82 | codeword_length=N, 83 | info_length=ceil(N * cr), 84 | is_systematic=True, 85 | **ap, 86 | ), 87 | channel_cls( 88 | snr_db=snr, 89 | N=N, 90 | K=ceil(N * cr) 91 | ) 92 | ) for cr in code_rates for snr in snr_range 93 | for ap in additional_code_params 94 | ] * repetitions 95 | 96 | shuffle(combinations) 97 | 98 | return combinations 99 | -------------------------------------------------------------------------------- /results/2048_512_I_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 512, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000000010001011100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000000010000000100111111000000000000000000000000000000010000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000000000000000000000000000000000000000001011100000000000000010000000100010111000000010001011100010111011111110000000000000001000000010001011100000001000101110001011101111111000000010001011100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000000100000001000101110000000000000001000000010001011100000001000101110001011101111111000000000000000100000001000101110000000100011111011111111111111100000111011111110111111111111111011111111111111111111111111111110000000000000001000001110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 2 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.3077919921875, 15 | "0.5": 0.259513671875, 16 | "1.0": 0.1440689453125, 17 | "1.5": 0.04157265625, 18 | "2.0": 0.0051146484375, 19 | "2.5": 0.0003595703125, 20 | "3.0": 0.0, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 0.9958, 27 | "0.5": 0.9323, 28 | "1.0": 0.6257, 29 | "1.5": 0.2231, 30 | "2.0": 0.0343, 31 | "2.5": 0.0027, 32 | "3.0": 0.0, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_1024_I_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1024, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000101110000000000000000000000010001011100000001001111110111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000101110000000000000000000000000000000000000000000000010000000100111111000000000000001100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000011100010111011111110000000100010111000101111111111100111111111111111111111111111111000000010001111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000010000000000000011000101110111111100000000000000000000000000000111000000010001011100010111011111110000000100010111001111111111111101111111111111111111111111111111000000000000000100000001000101110000000100111111011111111111111100000111011111110111111111111111011111111111111111111111111111110001011101111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000010000011101111111000101110111111101111111111111110001011101111111011111111111111111111111111111111111111111111111000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 2 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.20003251953125, 15 | "0.5": 0.16702841796875, 16 | "1.0": 0.08624609375, 17 | "1.5": 0.0164216796875, 18 | "2.0": 0.00114375, 19 | "2.5": 6.6015625e-05, 20 | "3.0": 1.7578125e-06, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 0.9962, 28 | "1.0": 0.8358, 29 | "1.5": 0.3111, 30 | "2.0": 0.0437, 31 | "2.5": 0.0049, 32 | "3.0": 0.0003, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_512_I_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 512, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000000010001011100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000000010000000100111111000000000000000000000000000000010000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000000000000000000000000000000000000000001011100000000000000010000000100010111000000010001011100010111011111110000000000000001000000010001011100000001000101110001011101111111000000010001011100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000000100000001000101110000000000000001000000010001011100000001000101110001011101111111000000000000000100000001000101110000000100011111011111111111111100000111011111110111111111111111011111111111111111111111111111110000000000000001000001110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 1 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.25869296875, 15 | "0.5": 0.2014337890625, 16 | "1.0": 0.108839453125, 17 | "1.5": 0.03341796875, 18 | "2.0": 0.005450390625, 19 | "2.5": 0.000408203125, 20 | "3.0": 1.640625e-05, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 0.9987, 27 | "0.5": 0.9552, 28 | "1.0": 0.6925, 29 | "1.5": 0.289, 30 | "2.0": 0.0564, 31 | "2.5": 0.0056, 32 | "3.0": 0.0004, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_512_I_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 512, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000000010001011100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000000010000000100111111000000000000000000000000000000010000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000000000000000000000000000000000000000001011100000000000000010000000100010111000000010001011100010111011111110000000000000001000000010001011100000001000101110001011101111111000000010001011100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000000100000001000101110000000000000001000000010001011100000001000101110001011101111111000000000000000100000001000101110000000100011111011111111111111100000111011111110111111111111111011111111111111111111111111111110000000000000001000001110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 4 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.31150234375, 15 | "0.5": 0.2704767578125, 16 | "1.0": 0.1564134765625, 17 | "1.5": 0.0457400390625, 18 | "2.0": 0.005834375, 19 | "2.5": 0.000428125, 20 | "3.0": 1.9140625e-05, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 0.9944, 27 | "0.5": 0.9183, 28 | "1.0": 0.5959, 29 | "1.5": 0.2043, 30 | "2.0": 0.0316, 31 | "2.5": 0.003, 32 | "3.0": 0.0001, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_1024_I_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1024, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000101110000000000000000000000010001011100000001001111110111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000101110000000000000000000000000000000000000000000000010000000100111111000000000000001100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000011100010111011111110000000100010111000101111111111100111111111111111111111111111111000000010001111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000010000000000000011000101110111111100000000000000000000000000000111000000010001011100010111011111110000000100010111001111111111111101111111111111111111111111111111000000000000000100000001000101110000000100111111011111111111111100000111011111110111111111111111011111111111111111111111111111110001011101111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000010000011101111111000101110111111101111111111111110001011101111111011111111111111111111111111111111111111111111111000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 1 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.17665771484375, 15 | "0.5": 0.132566796875, 16 | "1.0": 0.0656529296875, 17 | "1.5": 0.0132060546875, 18 | "2.0": 0.00121201171875, 19 | "2.5": 7.431640625e-05, 20 | "3.0": 4.58984375e-06, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 0.9982, 28 | "1.0": 0.8816, 29 | "1.5": 0.3885, 30 | "2.0": 0.0686, 31 | "2.5": 0.0059, 32 | "3.0": 0.0006, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_1024_I_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1024, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000101110000000000000000000000010001011100000001001111110111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000101110000000000000000000000000000000000000000000000010000000100111111000000000000001100010111011111110001011101111111011111111111111100000000000000000000000000000001000000000000011100010111011111110000000100010111000101111111111100111111111111111111111111111111000000010001111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000010000000000000011000101110111111100000000000000000000000000000111000000010001011100010111011111110000000100010111001111111111111101111111111111111111111111111111000000000000000100000001000101110000000100111111011111111111111100000111011111110111111111111111011111111111111111111111111111110001011101111111011111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000010000011101111111000101110111111101111111111111110001011101111111011111111111111111111111111111111111111111111111000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 4 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.2033162109375, 15 | "0.5": 0.17877802734375, 16 | "1.0": 0.10010556640625, 17 | "1.5": 0.01819541015625, 18 | "2.0": 0.0010361328125, 19 | "2.5": 6.07421875e-05, 20 | "3.0": 5.6640625e-06, 21 | "3.5": 1.46484375e-06, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 0.9952, 28 | "1.0": 0.8224, 29 | "1.5": 0.2749, 30 | "2.0": 0.0321, 31 | "2.5": 0.0035, 32 | "3.0": 0.0005, 33 | "3.5": 0.0001, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/decoding_path.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | import numpy as np 4 | 5 | 6 | class DecodingPathMixin: 7 | """Decoding Path for list decoding.""" 8 | 9 | def __init__(self, **kwargs): 10 | super().__init__(**kwargs) 11 | 12 | # Probability that current path contains correct decoding result 13 | self._path_metric = 0 14 | 15 | def __eq__(self, other): 16 | return self._path_metric == other._path_metric 17 | 18 | def __gt__(self, other): 19 | return self._path_metric > other._path_metric 20 | 21 | def __ge__(self, other): 22 | return self > other or self == other 23 | 24 | def __lt__(self, other): 25 | return not (self >= other) 26 | 27 | def __le__(self, other): 28 | return not (self > other) 29 | 30 | def __str__(self): 31 | return ( 32 | f'LLR: {self.current_llr}; ' 33 | f'Decision: {self._current_decision}; ' 34 | f'Path metric: {self._path_metric}' 35 | ) 36 | 37 | @property 38 | def current_llr(self): 39 | return self.intermediate_llr[-1][0] 40 | 41 | def __deepcopy__(self, memodict={}): 42 | new_path = self.__class__(n=self.n, mask=self.mask, 43 | is_systematic=self.is_systematic) 44 | 45 | # Copy intermediate LLR values 46 | new_path.intermediate_llr = [ 47 | np.array(llrs) for llrs in self.intermediate_llr 48 | ] 49 | # Copy intermediate bit values 50 | new_path.intermediate_bits = [ 51 | np.array(bits) for bits in self.intermediate_bits 52 | ] 53 | # Copy current state 54 | new_path.current_state = np.array(self.current_state) 55 | # Copy previous state 56 | new_path.previous_state = np.array(self.previous_state) 57 | 58 | # Copy path metric 59 | new_path._path_metric = self._path_metric 60 | 61 | # Make opposite decisions for each path 62 | self._current_decision = 0 63 | new_path._current_decision = 1 64 | 65 | return new_path 66 | 67 | def update_path_metric(self): 68 | """Update path metrics using LLR-based metric. 69 | 70 | Source: https://arxiv.org/abs/1411.7282 Section III-B 71 | 72 | """ 73 | if self.current_llr >= 0: 74 | self._path_metric -= (self.current_llr * self._current_decision) 75 | if self.current_llr < 0: 76 | self._path_metric += (self.current_llr * (1 - self._current_decision)) 77 | 78 | def split_path(self): 79 | """Make a copy of SC path with another decision. 80 | 81 | If LLR of the current position is out of bounds, there is no sense 82 | of splitting path because LLR >= 20 means 0 and LLR <= -20 means 1. 83 | 84 | """ 85 | new_path = deepcopy(self) 86 | return [self, new_path] 87 | -------------------------------------------------------------------------------- /results/2048_1536_I_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1536, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000011100000000000000000000000100010111000000010001011100011111111111110000000000000000000000000000000100000000000000010000000100010111000000000000000100000001000101110000000101111111011111111111111100000000000000010000011101111111000101110111111111111111111111110001011111111111111111111111111111111111111111111111111111111111000000000000000000000000000000010000000000000001000000110111111100000000000001110001011101111111000101111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100000011011111110111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000010001011100000001000101110001111111111111000000010011111101111111111111110111111111111111111111111111111100010111011111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 1 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.124334375, 15 | "0.5": 0.11240182291666667, 16 | "1.0": 0.10004811197916667, 17 | "1.5": 0.08361009114583333, 18 | "2.0": 0.05270794270833333, 19 | "2.5": 0.015048567708333334, 20 | "3.0": 0.0017393880208333332, 21 | "3.5": 0.0001673828125, 22 | "4.0": 2.2330729166666667e-05 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 1.0, 28 | "1.0": 1.0, 29 | "1.5": 0.9998, 30 | "2.0": 0.9804, 31 | "2.5": 0.6717, 32 | "3.0": 0.1955, 33 | "3.5": 0.0369, 34 | "4.0": 0.0061 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_1536_I_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1536, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000011100000000000000000000000100010111000000010001011100011111111111110000000000000000000000000000000100000000000000010000000100010111000000000000000100000001000101110000000101111111011111111111111100000000000000010000011101111111000101110111111111111111111111110001011111111111111111111111111111111111111111111111111111111111000000000000000000000000000000010000000000000001000000110111111100000000000001110001011101111111000101111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100000011011111110111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000010001011100000001000101110001111111111111000000010011111101111111111111110111111111111111111111111111111100010111011111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 4 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.12694420572916668, 15 | "0.5": 0.11548932291666666, 16 | "1.0": 0.10496920572916667, 17 | "1.5": 0.09319342447916666, 18 | "2.0": 0.066490234375, 19 | "2.5": 0.018289388020833332, 20 | "3.0": 0.0019571614583333333, 21 | "3.5": 0.00022096354166666667, 22 | "4.0": 3.4375e-05 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 1.0, 28 | "1.0": 1.0, 29 | "1.5": 0.9999, 30 | "2.0": 0.9728, 31 | "2.5": 0.5989, 32 | "3.0": 0.1705, 33 | "3.5": 0.0373, 34 | "4.0": 0.0075 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /results/2048_1536_I_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 2048, 4 | "info_length": 1536, 5 | "design_snr": 0.0, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000011100000000000000000000000100010111000000010001011100011111111111110000000000000000000000000000000100000000000000010000000100010111000000000000000100000001000101110000000101111111011111111111111100000000000000010000011101111111000101110111111111111111111111110001011111111111111111111111111111111111111111111111111111111111000000000000000000000000000000010000000000000001000000110111111100000000000001110001011101111111000101111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100000011011111110111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000010001011100000001000101110001111111111111000000010011111101111111111111110111111111111111111111111111111100010111011111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 2 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.12626888020833332, 15 | "0.5": 0.11494153645833333, 16 | "1.0": 0.10428138020833333, 17 | "1.5": 0.09197220052083334, 18 | "2.0": 0.06166041666666667, 19 | "2.5": 0.017657421875, 20 | "3.0": 0.0020194661458333334, 21 | "3.5": 0.00021555989583333334, 22 | "4.0": 3.190104166666666e-05 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 1.0, 28 | "1.0": 1.0, 29 | "1.5": 0.9999, 30 | "2.0": 0.9763, 31 | "2.5": 0.6376, 32 | "3.0": 0.1924, 33 | "3.5": 0.0411, 34 | "4.0": 0.0077 35 | }, 36 | "messages": 10000 37 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-polar-coding 2 | 3 | A package for Polar codes simulation. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | pip install python-polar-coding 9 | ``` 10 | 11 | ## Example 12 | 13 | Here is a simple example of simulation using `python_polar_coding`. 14 | 15 | Binary messages encoded with Polar code, modulated using BPSK, transmitted over 16 | channel with AWGN and decoded using [Fast SSC](https://arxiv.org/abs/1307.7154) algorithm. 17 | 18 | ```python 19 | from python_polar_coding.channels import SimpleBPSKModulationAWGN 20 | from python_polar_coding.polar_codes import FastSSCPolarCodec 21 | from python_polar_coding.simulation.functions import ( 22 | compute_fails, 23 | generate_binary_message, 24 | ) 25 | 26 | N = 128 27 | K = 64 28 | design_snr = 0.0 29 | messages = 1000 30 | # SNR in [.0, .5, ..., 4.5, 5] 31 | snr_range = [i / 2 for i in range(11)] 32 | 33 | codec = FastSSCPolarCodec(N=N, K=K, design_snr=design_snr) 34 | bpsk = SimpleBPSKModulationAWGN(fec_rate=K/N) 35 | 36 | result_ber = dict() 37 | result_fer = dict() 38 | 39 | print('Python polar coding simulation') 40 | print(f'Simulating ({codec.N}, {codec.K}) systematic polar code with Design SNR {codec.design_snr} dB') 41 | print() 42 | print('\tSNR (dB)|\tBER\t|\tFER') 43 | 44 | for snr in snr_range: 45 | ber = 0 46 | fer = 0 47 | 48 | for _ in range(messages): 49 | msg = generate_binary_message(size=K) 50 | encoded = codec.encode(msg) 51 | transmitted = bpsk.transmit(message=encoded, snr_db=snr) 52 | decoded = codec.decode(transmitted) 53 | 54 | bit_errors, frame_error = compute_fails(msg, decoded) 55 | ber += bit_errors 56 | fer += frame_error 57 | 58 | result_ber[snr] = ber / (messages * codec.K) 59 | result_fer[snr] = fer / messages 60 | 61 | print(f'\t{snr}\t|\t{result_ber[snr]:.4f}\t|\t{result_fer[snr]:.4f}') 62 | ``` 63 | 64 | ## Current progress 65 | 66 | ### Polar code construction 67 | 68 | - [x] Arikan's Bhattacharyya bounds [Section V.A](https://arxiv.org/pdf/1501.02473.pdf) 69 | 70 | ### Decoding 71 | - [x] SC Decoding 72 | - [x] [SC LIST Decoding](https://arxiv.org/abs/1206.0050) 73 | - [x] [Fast SSC Decoding](https://arxiv.org/abs/1307.7154) 74 | - [x] [RC SCAN Decoding]() 75 | - [x] [Generalized Fast SSC Decoding](https://arxiv.org/pdf/1804.09508.pdf) 76 | 77 | ### Modulation 78 | 79 | - [x] BPSK 80 | 81 | ## TODO 82 | 83 | ### Polar code construction 84 | 85 | - [ ] Arikan’s Monte-Carlo estimation [Section V.B](https://arxiv.org/pdf/1501.02473.pdf) 86 | - [ ] Trifonov’s Gaussian approximation [Section V.D](https://arxiv.org/pdf/1501.02473.pdf) 87 | 88 | ### Decoding 89 | - [ ] [SC STACK Decoding](https://ieeexplore.ieee.org/document/6215306) 90 | - [ ] [Fast SSC List Decoding](https://arxiv.org/pdf/1703.08208.pdf) 91 | - [ ] [Generalized Fast SSC LIST Decoding](https://arxiv.org/pdf/1804.09508.pdf) 92 | - [ ] CRC-aided decoders 93 | 94 | ### Modulation 95 | 96 | - [ ] Q-PSK 97 | - [ ] 4-QAM 98 | 99 | ## License 100 | 101 | [MIT License](LICENSE.txt) 102 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/rc_scan/decoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from anytree import PreOrderIter 3 | 4 | from python_polar_coding.polar_codes.base import BaseTreeDecoder 5 | from python_polar_coding.polar_codes.base.functions import make_hard_decision 6 | 7 | from .functions import ( 8 | compute_left_alpha, 9 | compute_parent_beta, 10 | compute_right_alpha, 11 | ) 12 | from .node import RCSCANNode 13 | 14 | 15 | class RCSCANDecoder(BaseTreeDecoder): 16 | """Implements Reduced-complexity SCAN decoding algorithm. 17 | 18 | Based on: 19 | * https://arxiv.org/pdf/1510.06495.pdf 20 | * doi:10.1007/s12243-018-0634-7 21 | 22 | """ 23 | node_class = RCSCANNode 24 | 25 | def __init__( 26 | self, 27 | n: int, 28 | mask: np.array, 29 | I: int = 1, 30 | ): 31 | super().__init__(n=n, mask=mask) 32 | self.I = I 33 | 34 | def decode(self, received_llr: np.array) -> np.array: 35 | """Implementation of SC decoding method.""" 36 | self._clean_before_decoding() 37 | 38 | for leaf in self.leaves: 39 | leaf.initialize_leaf_beta() 40 | 41 | for _ in range(self.I): 42 | super().decode(received_llr) 43 | 44 | return self.result 45 | 46 | def _clean_before_decoding(self): 47 | """Reset intermediate BETA values. 48 | 49 | Run this before calling `__call__` method. 50 | 51 | """ 52 | for node in PreOrderIter(self._decoding_tree): 53 | if not (node.is_zero or node.is_one): 54 | node.beta *= 0 55 | 56 | def _compute_intermediate_alpha(self, leaf): 57 | """Compute intermediate Alpha values (LLR).""" 58 | for node in leaf.path[1:]: 59 | if node.is_computed or node.is_zero or node.is_one: 60 | continue 61 | 62 | parent_alpha = node.parent.alpha 63 | 64 | if node.is_left: 65 | right_beta = node.siblings[0].beta 66 | node.alpha = compute_left_alpha(parent_alpha, right_beta) 67 | 68 | if node.is_right: 69 | left_beta = node.siblings[0].beta 70 | node.alpha = compute_right_alpha(parent_alpha, left_beta) 71 | 72 | node.is_computed = True 73 | 74 | def _compute_intermediate_beta(self, node): 75 | """Compute intermediate BETA values.""" 76 | parent = node.parent 77 | if node.is_left or node.is_root or parent.is_root: 78 | return 79 | 80 | left = node.siblings[0] 81 | parent.beta = compute_parent_beta(left.beta, node.beta, parent.alpha) 82 | return self._compute_intermediate_beta(parent) 83 | 84 | @property 85 | def result(self): 86 | return make_hard_decision(self.root.alpha + 87 | self._compute_result_beta()) 88 | 89 | def _compute_result_beta(self) -> np.array: 90 | """Compute result BETA values.""" 91 | alpha = self.root.alpha 92 | if not self.root.children: 93 | return self.root.beta 94 | 95 | left, right = self.root.children 96 | return compute_parent_beta(left.beta, right.beta, alpha) 97 | -------------------------------------------------------------------------------- /tests/test_g_fast_ssc/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.g_fast_ssc import GFastSSCPolarCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | 7 | class TestGeneralizedFastSSCCode_1024_256_AF_0(BasicVerifyPolarCode, TestCase): 8 | polar_code_class = GFastSSCPolarCodec 9 | code_parameters = { 10 | 'N': 1024, 11 | 'K': 256, 12 | 'AF': 0, 13 | } 14 | 15 | 16 | class TestGeneralizedFastSSCCode_1024_256_AF_1(BasicVerifyPolarCode, TestCase): 17 | polar_code_class = GFastSSCPolarCodec 18 | code_parameters = { 19 | 'N': 1024, 20 | 'K': 256, 21 | 'AF': 1, 22 | } 23 | 24 | 25 | class TestGeneralizedFastSSCCode_1024_256_AF_2(BasicVerifyPolarCode, TestCase): 26 | polar_code_class = GFastSSCPolarCodec 27 | code_parameters = { 28 | 'N': 1024, 29 | 'K': 256, 30 | 'AF': 2, 31 | } 32 | 33 | 34 | class TestGeneralizedFastSSCCode_1024_256_AF_3(BasicVerifyPolarCode, TestCase): 35 | polar_code_class = GFastSSCPolarCodec 36 | code_parameters = { 37 | 'N': 1024, 38 | 'K': 256, 39 | 'AF': 3, 40 | } 41 | 42 | 43 | class TestGeneralizedFastSSCCode_1024_512_AF_0(BasicVerifyPolarCode, TestCase): 44 | polar_code_class = GFastSSCPolarCodec 45 | code_parameters = { 46 | 'N': 1024, 47 | 'K': 512, 48 | 'AF': 0, 49 | } 50 | 51 | 52 | class TestGeneralizedFastSSCCode_1024_512_AF_1(BasicVerifyPolarCode, TestCase): 53 | polar_code_class = GFastSSCPolarCodec 54 | code_parameters = { 55 | 'N': 1024, 56 | 'K': 512, 57 | 'AF': 1, 58 | } 59 | 60 | 61 | class TestGeneralizedFastSSCCode_1024_512_AF_2(BasicVerifyPolarCode, TestCase): 62 | polar_code_class = GFastSSCPolarCodec 63 | code_parameters = { 64 | 'N': 1024, 65 | 'K': 512, 66 | 'AF': 2, 67 | } 68 | 69 | 70 | class TestGeneralizedFastSSCCode_1024_512_AF_3(BasicVerifyPolarCode, TestCase): 71 | polar_code_class = GFastSSCPolarCodec 72 | code_parameters = { 73 | 'N': 1024, 74 | 'K': 512, 75 | 'AF': 3, 76 | } 77 | 78 | 79 | class TestGeneralizedFastSSCCode_1024_768_AF_0(BasicVerifyPolarCode, TestCase): 80 | polar_code_class = GFastSSCPolarCodec 81 | code_parameters = { 82 | 'N': 1024, 83 | 'K': 768, 84 | 'AF': 0, 85 | } 86 | 87 | 88 | class TestGeneralizedFastSSCCode_1024_768_AF_1(BasicVerifyPolarCode, TestCase): 89 | polar_code_class = GFastSSCPolarCodec 90 | code_parameters = { 91 | 'N': 1024, 92 | 'K': 768, 93 | 'AF': 1, 94 | } 95 | 96 | 97 | class TestGeneralizedFastSSCCode_1024_768_AF_2(BasicVerifyPolarCode, TestCase): 98 | polar_code_class = GFastSSCPolarCodec 99 | code_parameters = { 100 | 'N': 1024, 101 | 'K': 768, 102 | 'AF': 2, 103 | } 104 | 105 | 106 | class TestGeneralizedFastSSCCode_1024_768_AF_3(BasicVerifyPolarCode, TestCase): 107 | polar_code_class = GFastSSCPolarCodec 108 | code_parameters = { 109 | 'N': 1024, 110 | 'K': 768, 111 | 'AF': 3, 112 | } 113 | -------------------------------------------------------------------------------- /tests/base.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from python_polar_coding.channels.simple import SimpleBPSKModulationAWGN 4 | 5 | 6 | class BasicVerifyPolarCode: 7 | """Provides simple BPSK modulator for polar codes testing.""" 8 | messages = 1000 9 | polar_code_class = None 10 | channel_class = SimpleBPSKModulationAWGN 11 | code_parameters = None 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | fec_rate = cls.code_parameters['K'] / cls.code_parameters['N'] 16 | cls.channel = cls.channel_class(fec_rate) 17 | cls.polar_code = cls.polar_code_class(**cls.code_parameters) 18 | cls.result = cls.polar_code.to_dict() 19 | 20 | @classmethod 21 | def tearDownClass(cls): 22 | print(cls.result) 23 | 24 | @property 25 | def N(self): 26 | return self.code_parameters['N'] 27 | 28 | @property 29 | def K(self): 30 | return self.code_parameters['K'] 31 | 32 | def test_sc_decoder_without_noise(self): 33 | """Test a Polar Code without any noise. 34 | 35 | For correctly implemented code the data is transmitted and decoded 36 | without errors. 37 | 38 | """ 39 | bit_errors, frame_errors = self._message_transmission( 40 | snr_db=10, 41 | with_noise=False, 42 | ) 43 | self.assertEqual(bit_errors, 0) 44 | self.assertEqual(frame_errors, 0) 45 | 46 | self.result.update({ 47 | 'no_noise': { 48 | 'bit_errors': bit_errors, 49 | 'frame_errors': frame_errors 50 | } 51 | }) 52 | 53 | def test_sc_decoder_10_db(self): 54 | """Test a Polar Code with low noise power. 55 | 56 | For correctly implemented code the data is transmitted and decoded 57 | without errors for SNR = 10 dB. 58 | 59 | Use the test as the example of modelling, but without assertions. 60 | 61 | """ 62 | bit_errors, frame_errors = self._modelling_test(snr_db=10.0) 63 | self.assertEqual(bit_errors, 0) 64 | self.assertEqual(frame_errors, 0) 65 | 66 | def _get_channel(self): 67 | fec_rate = self.K / self.N 68 | return self.channel_class(fec_rate) 69 | 70 | def _message_transmission(self, snr_db, with_noise=True): 71 | """Basic workflow to compute BER and FER on message transmission""" 72 | bit_errors = frame_errors = 0 73 | 74 | for m in range(self.messages): 75 | message = np.random.randint(0, 2, self.K) 76 | encoded = self.polar_code.encode(message) 77 | llr = self.channel.transmit( 78 | message=encoded, 79 | snr_db=snr_db, 80 | with_noise=with_noise, 81 | ) 82 | decoded = self.polar_code.decode(llr) 83 | 84 | fails = np.sum(message != decoded) 85 | bit_errors += fails 86 | frame_errors += fails > 0 87 | 88 | return bit_errors, frame_errors 89 | 90 | def _modelling_test(self, snr_db): 91 | bit_errors, frame_errors = self._message_transmission( 92 | snr_db=snr_db, 93 | with_noise=True, 94 | ) 95 | self.result.update({ 96 | snr_db: { 97 | 'bit_errors': bit_errors, 98 | 'frame_errors': frame_errors 99 | } 100 | }) 101 | return bit_errors, frame_errors 102 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc_list/decoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from python_polar_coding.polar_codes.base import BaseDecoder 4 | 5 | from .decoding_path import SCPath 6 | 7 | 8 | class SCListDecoder(BaseDecoder): 9 | """SC List decoding.""" 10 | path_class = SCPath 11 | 12 | def __init__(self, n: int, 13 | mask: np.array, 14 | is_systematic: bool = True, 15 | L: int = 1): 16 | super().__init__(n=n, mask=mask, is_systematic=is_systematic) 17 | self.L = L 18 | self.paths = [ 19 | self.path_class(n=n, mask=mask, is_systematic=is_systematic), 20 | ] 21 | 22 | @property 23 | def result(self): 24 | """Decoding result.""" 25 | return [path.result for path in self.paths] 26 | 27 | @property 28 | def best_result(self): 29 | """Result from the best path.""" 30 | return self.result[0] 31 | 32 | def decode_internal(self, received_llr: np.array) -> np.array: 33 | """Implementation of SC decoding method.""" 34 | self._set_initial_state(received_llr) 35 | 36 | for pos in range(self.N): 37 | self._decode_position(pos) 38 | 39 | return self.best_result 40 | 41 | def _set_initial_state(self, received_llr): 42 | """Initialize paths with received message.""" 43 | for path in self.paths: 44 | path._set_initial_state(received_llr) 45 | 46 | def _decode_position(self, position): 47 | """Single step of SC-decoding algorithm to decode one bit.""" 48 | self.set_decoder_state(position) 49 | self._compute_intermediate_alpha(position) 50 | 51 | if self.mask[position] == 1: 52 | self._populate_paths() 53 | if self.mask[position] == 0: 54 | self.set_frozen_value() 55 | 56 | self._update_paths_metrics() 57 | self._select_best_paths() 58 | self._compute_bits(position) 59 | 60 | def set_decoder_state(self, position): 61 | """Set current state of each path.""" 62 | for path in self.paths: 63 | path._set_decoder_state(position) 64 | 65 | def _compute_intermediate_alpha(self, position): 66 | """Compute intermediate LLR values of each path.""" 67 | for path in self.paths: 68 | path._compute_intermediate_alpha(position) 69 | 70 | def set_frozen_value(self): 71 | """Set current position to frozen values of each path.""" 72 | for path in self.paths: 73 | path._current_decision = 0 74 | 75 | def _populate_paths(self): 76 | """Populate SC paths with alternative decisions.""" 77 | new_paths = list() 78 | for path in self.paths: 79 | split_result = path.split_path() 80 | new_paths += split_result 81 | 82 | self.paths = new_paths 83 | 84 | def _update_paths_metrics(self): 85 | """Update path metric of each path.""" 86 | for path in self.paths: 87 | path.update_path_metric() 88 | 89 | def _select_best_paths(self): 90 | """Select best of populated paths. 91 | 92 | If the number of paths is less then L, all populated paths returned. 93 | 94 | """ 95 | if len(self.paths) <= self.L: 96 | self.paths = sorted(self.paths, reverse=True) 97 | else: 98 | self.paths = sorted(self.paths, reverse=True)[:self.L] 99 | 100 | def _compute_bits(self, position): 101 | """Compute bits of each path.""" 102 | for path in self.paths: 103 | path._compute_intermediate_beta(position) 104 | path._update_decoder_state() 105 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/beta_soft.py: -------------------------------------------------------------------------------- 1 | """Common functions for polar coding.""" 2 | import numba 3 | import numpy as np 4 | 5 | from ..constants import INFINITY 6 | from .node_types import NodeTypes 7 | 8 | 9 | @numba.njit 10 | def zero( 11 | llr: np.array, 12 | mask_steps: int = 0, 13 | last_chunk_type: int = 0, 14 | ) -> np.array: 15 | """Compute beta values for ZERO node. 16 | 17 | https://arxiv.org/pdf/1510.06495.pdf Section III.C. 18 | 19 | """ 20 | return np.ones(llr.size, dtype=np.double) * INFINITY 21 | 22 | 23 | @numba.njit 24 | def one( 25 | llr: np.array, 26 | mask_steps: int = 0, 27 | last_chunk_type: int = 0, 28 | ) -> np.array: 29 | """Compute beta values for ONE node. 30 | 31 | https://arxiv.org/pdf/1510.06495.pdf Section III.C. 32 | 33 | """ 34 | return np.zeros(llr.size, dtype=np.double) 35 | 36 | 37 | @numba.njit 38 | def repetition( 39 | llr: np.array, 40 | mask_steps: int = 0, 41 | last_chunk_type: int = 0, 42 | ) -> np.array: 43 | """Compute beta value for Repetition node.""" 44 | alpha_sum = np.sum(llr) 45 | return -1 * llr + alpha_sum 46 | 47 | 48 | @numba.njit 49 | def single_parity_check( 50 | llr: np.array, 51 | mask_steps: int = 0, 52 | last_chunk_type: int = 0, 53 | ) -> np.array: 54 | """Compute beta value for Single parity node.""" 55 | all_sign = np.sign(np.prod(llr)) 56 | abs_alpha = np.fabs(llr) 57 | first_min_idx, second_min_idx = np.argsort(abs_alpha)[:2] 58 | 59 | result = np.sign(llr) * all_sign 60 | for i in range(result.size): 61 | if i == first_min_idx: 62 | result[i] *= abs_alpha[second_min_idx] 63 | else: 64 | result[i] *= abs_alpha[first_min_idx] 65 | 66 | return result 67 | 68 | 69 | @numba.njit 70 | def g_repetition( 71 | llr: np.array, 72 | mask_steps: int, 73 | last_chunk_type: int, 74 | ) -> np.array: 75 | """Compute bits for Generalized Repetition node. 76 | 77 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, A. 78 | 79 | """ 80 | N = llr.size 81 | step = N // mask_steps # step is equal to a chunk size 82 | 83 | last_alpha = np.zeros(step) 84 | for i in range(step): 85 | last_alpha[i] = np.sum(np.array([ 86 | llr[i + j * step] for j in range(mask_steps) 87 | ])) 88 | 89 | last_beta = ( 90 | one(last_alpha) if last_chunk_type == 1 91 | else single_parity_check(last_alpha) 92 | ) 93 | 94 | result = np.zeros(N) 95 | for i in range(0, N, step): 96 | result[i: i + step] = last_beta 97 | 98 | return result 99 | 100 | 101 | @numba.njit 102 | def rg_parity( 103 | llr: np.array, 104 | mask_steps: int, 105 | last_chunk_type: int = 0, 106 | ) -> np.array: 107 | """Compute bits for Relaxed Generalized Parity Check node. 108 | 109 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, B. 110 | 111 | """ 112 | N = llr.size 113 | step = N // mask_steps # step is equal to a chunk size 114 | result = np.zeros(N) 115 | 116 | for i in range(step): 117 | alpha = np.zeros(mask_steps) 118 | for j in range(mask_steps): 119 | alpha[j] = llr[i + j * step] 120 | 121 | beta = single_parity_check(alpha) 122 | result[i:N:step] = beta 123 | 124 | return result 125 | 126 | 127 | # Mapping between decoding node types and corresponding decoding methods 128 | _methods_map = { 129 | NodeTypes.ZERO: zero, 130 | NodeTypes.ONE: one, 131 | NodeTypes.SINGLE_PARITY_CHECK: single_parity_check, 132 | NodeTypes.REPETITION: repetition, 133 | NodeTypes.RG_PARITY: rg_parity, 134 | NodeTypes.G_REPETITION: g_repetition, 135 | } 136 | 137 | 138 | def compute_beta_soft( 139 | node_type: str, 140 | llr: np.array, 141 | mask_steps: int = 0, 142 | last_chunk_type: int = 0, 143 | *args, **kwargs, 144 | ) -> np.array: 145 | """Unites functions for making soft decisions during decoding.""" 146 | method = _methods_map[node_type] 147 | return method(llr, mask_steps, last_chunk_type, *args, **kwargs) 148 | -------------------------------------------------------------------------------- /tests/test_rc_scan/test_decoder.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.rc_scan import INFINITY, RCSCANDecoder 6 | 7 | 8 | class TestRCSCANDecoder(TestCase): 9 | # @classmethod 10 | def setUp(cls): 11 | cls.received_llr = np.array([ 12 | -2.7273, -8.7327, 0.1087, 1.6463, 13 | 0.0506, -0.0552, -1.5304, -2.1233, 14 | ]) 15 | cls.n = 3 16 | cls.length = cls.received_llr.size 17 | 18 | def test_zero_node_decoder(self): 19 | mask = np.zeros(self.length, dtype=np.int8) 20 | decoder = RCSCANDecoder(mask=mask, n=self.n) 21 | decoder.decode(self.received_llr) 22 | 23 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 24 | np.testing.assert_equal( 25 | decoder.root.beta, 26 | np.ones(self.length, dtype=np.double) * INFINITY 27 | ) 28 | 29 | def test_one_node_decoder(self): 30 | mask = np.ones(self.length, dtype=np.int8) 31 | decoder = RCSCANDecoder(mask=mask, n=self.n) 32 | decoder.decode(self.received_llr) 33 | 34 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 35 | np.testing.assert_equal( 36 | decoder.root.beta, 37 | np.zeros(self.length) 38 | ) 39 | 40 | 41 | class TestRCSCANDecoderComplex(TestCase): 42 | @classmethod 43 | def setUpClass(cls): 44 | cls.mask = np.array( 45 | [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, ], 46 | dtype=np.int8 47 | ) 48 | cls.n = 4 49 | cls.sub_codes = [ 50 | np.array([0, 0, 0, 0, ], dtype=np.int8), 51 | np.array([0, ], dtype=np.int8), 52 | np.array([1, ], dtype=np.int8), 53 | np.array([1, 1, ], dtype=np.int8), 54 | np.array([0, ], dtype=np.int8), 55 | np.array([1, ], dtype=np.int8), 56 | np.array([1, 1, ], dtype=np.int8), 57 | np.array([1, 1, 1, 1, ], dtype=np.int8), 58 | ] 59 | 60 | def _get_decoder(self): 61 | return RCSCANDecoder(mask=self.mask, n=self.n) 62 | 63 | def test_sub_codes(self): 64 | """Check sub-codes built correctly.""" 65 | decoder = self._get_decoder() 66 | 67 | self.assertEqual( 68 | len(decoder._decoding_tree.leaves), 69 | len(self.sub_codes) 70 | ) 71 | 72 | for i, leaf in enumerate(decoder._decoding_tree.leaves): 73 | np.testing.assert_equal(leaf.mask, self.sub_codes[i]) 74 | 75 | def test_no_noise(self): 76 | decoder = self._get_decoder() 77 | llr = np.array([ 78 | -2, -2, 2, -2, -2, 2, -2, 2, 79 | 2, -2, 2, -2, 2, 2, -2, -2, 80 | ]) 81 | 82 | decoder.decode(llr) 83 | 84 | # Check result 85 | expected_result_beta = np.array( 86 | [-2, -2, 2, 6, -2, 2, -2, 2, 87 | 2, -2, 2, -2, 2, 2, -2, -2, ], 88 | dtype=np.float64 89 | ) 90 | np.testing.assert_equal( 91 | decoder._compute_result_beta(), 92 | expected_result_beta 93 | ) 94 | 95 | expected_result = np.array( 96 | [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, ], 97 | dtype=np.int8 98 | ) 99 | np.testing.assert_equal(decoder.result, expected_result) 100 | 101 | def test_with_some_noise(self): 102 | decoder = self._get_decoder() 103 | llr = np.array([ 104 | -1.9, -1.7, 2.6, -1.7, -1.1, 2.6, -1.3, 2.4, 105 | 2.2, -1.8, 2.1, -1.9, 2.3, 2.2, -1.5, -1.2, 106 | ]) 107 | 108 | decoder.decode(llr) 109 | 110 | # Check result 111 | # Iteration 1 112 | expected_result_beta = np.array( 113 | [-0.6, -1.7, 0.8, 4.2, -1.4, 1.2, -1.6, 1.3, 114 | 0.8, -1.9, 1, -0.8, 1.3, 1.4, -1.5, -1.5, ], 115 | dtype=np.float64 116 | ) 117 | np.testing.assert_almost_equal( 118 | decoder._compute_result_beta(), 119 | expected_result_beta 120 | ) 121 | 122 | expected_result = np.array( 123 | [1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, ], 124 | dtype=np.int8 125 | ) 126 | np.testing.assert_equal(decoder.result, expected_result) 127 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/node.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Tuple 2 | 3 | import numpy as np 4 | from anytree import Node 5 | 6 | from .functions import ( 7 | NodeTypes, 8 | compute_beta_hard, 9 | compute_beta_soft, 10 | get_node_type, 11 | ) 12 | 13 | 14 | class BaseDecodingNode(Node): 15 | """Base class for node of the decoding tree.""" 16 | 17 | LEFT = 'left_child' 18 | RIGHT = 'right_child' 19 | ROOT = 'root' 20 | NODE_NAMES: Tuple = (LEFT, RIGHT, ROOT) 21 | 22 | # Supported types of decoding nodes 23 | supported_nodes: Tuple = None 24 | 25 | def __init__(self, mask: np.array, name: str = ROOT, AF: int = 0, **kwargs): # noqa 26 | """A node of Fast SSC decoder.""" 27 | if name not in self.__class__.NODE_NAMES: 28 | raise ValueError('Wrong Fast SSC Node type') 29 | 30 | super().__init__(name, **kwargs) 31 | 32 | self.mask = mask 33 | self.AF = AF 34 | self.node_type = get_node_type( 35 | supported_nodes=self.supported_nodes, 36 | mask=self.mask, 37 | AF=self.AF, 38 | ) 39 | self.is_computed = False 40 | 41 | self._alpha = np.zeros(self.N, dtype=np.double) 42 | self._beta = np.zeros(self.N, dtype=np.int8) 43 | 44 | # For generalized decoders 45 | self.last_chunk_type = get_node_type.last_chunk_type 46 | self.mask_steps = get_node_type.mask_steps 47 | 48 | self.build_decoding_tree() 49 | 50 | def __str__(self): 51 | return ''.join([str(m) for m in self.mask]) 52 | 53 | def __len__(self): 54 | return self.mask.size 55 | 56 | @property 57 | def N(self) -> int: 58 | return self.mask.size 59 | 60 | @property 61 | def alpha(self) -> np.array: 62 | return self._alpha 63 | 64 | @alpha.setter 65 | def alpha(self, value: np.array): 66 | if self.mask.size != value.size: 67 | raise ValueError('Wrong size of LLR vector') 68 | self._alpha = np.array(value) 69 | 70 | @property 71 | def beta(self) -> np.array: 72 | return self._beta 73 | 74 | @beta.setter 75 | def beta(self, value: np.array): 76 | if self.mask.size != value.size: 77 | raise ValueError('Wrong size of Bits vector') 78 | self._beta = np.array(value) 79 | 80 | @property 81 | def is_left(self) -> bool: 82 | return self.name == self.__class__.LEFT 83 | 84 | @property 85 | def is_right(self) -> bool: 86 | return self.name == self.__class__.RIGHT 87 | 88 | @property 89 | def is_simplified_node(self) -> bool: 90 | return self.node_type != NodeTypes.OTHER 91 | 92 | def to_dict(self) -> Dict: 93 | return { 94 | 'type': self.node_type, 95 | 'mask': self.mask, 96 | } 97 | 98 | def build_decoding_tree(self): 99 | """Build decoding tree.""" 100 | if self.is_simplified_node: 101 | return 102 | 103 | left_mask, right_mask = np.split(self.mask, 2) 104 | cls = self.__class__ 105 | cls(mask=left_mask, name=self.LEFT, AF=self.AF, parent=self) 106 | cls(mask=right_mask, name=self.RIGHT, AF=self.AF, parent=self) 107 | 108 | def get_decoding_params(self) -> Dict: 109 | """Get decoding params to perform the decoding in a leaf node.""" 110 | raise NotImplementedError('Implement in a concrete class') 111 | 112 | def __call__(self, *args, **kwargs): 113 | """Compute beta value of the decoding node.""" 114 | raise NotImplementedError('Implement in a concrete class') 115 | 116 | 117 | class HardNode(BaseDecodingNode): 118 | """Decoding node for hard decoding methods.""" 119 | 120 | def __call__(self, *args, **kwargs): 121 | """Compute beta value of the decoding node.""" 122 | 123 | if not self.is_leaf: 124 | raise TypeError('Cannot make decision in not a leaf node.') 125 | 126 | params = self.get_decoding_params() 127 | self.beta = compute_beta_hard(**params) 128 | 129 | 130 | class SoftNode(BaseDecodingNode): 131 | """Decoding node for soft decoding methods.""" 132 | 133 | def __call__(self, *args, **kwargs): 134 | """Compute beta value of the decoding node.""" 135 | 136 | if not self.is_leaf: 137 | raise TypeError('Cannot make decision in not a leaf node.') 138 | 139 | params = self.get_decoding_params() 140 | self.beta = compute_beta_soft(**params) 141 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/decoder.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | import numpy as np 4 | from anytree import PreOrderIter 5 | 6 | 7 | class BaseDecoder(metaclass=abc.ABCMeta): 8 | """Basic class for polar decoder.""" 9 | 10 | def __init__(self, n, mask: np.array, is_systematic: bool = True): 11 | self.N = mask.shape[0] 12 | self.n = n 13 | self.is_systematic = is_systematic 14 | self.mask = mask 15 | 16 | def decode(self, received_llr: np.array) -> np.array: 17 | decoded = self.decode_internal(received_llr) 18 | return self.extract_result(decoded) 19 | 20 | @abc.abstractmethod 21 | def decode_internal(self, received_llr: np.array) -> np.array: 22 | """Implementation of particular decoding method.""" 23 | 24 | def extract_result(self, decoded: np.array) -> np.array: 25 | """Get decoding result. 26 | 27 | Extract info bits from decoded message due to polar code mask. 28 | 29 | """ 30 | decoded_info = list() 31 | 32 | for i in range(self.N): 33 | if self.mask[i] == 1: 34 | decoded_info = np.append(decoded_info, decoded[i]) 35 | return np.array(decoded_info, dtype=np.int) 36 | 37 | 38 | class BaseTreeDecoder(metaclass=abc.ABCMeta): 39 | """Basic class for polar decoder that use tree for decoding.""" 40 | 41 | node_class: 'BaseDecodingNode' 42 | 43 | def __init__(self, n, mask: np.array): 44 | self.N = mask.shape[0] 45 | self.n = n 46 | self.mask = mask 47 | 48 | self._decoding_tree = self._setup_decoding_tree() 49 | self._position = 0 50 | 51 | def __call__(self, received_llr: np.array) -> np.array: 52 | decoded = self.decode(received_llr) 53 | return self.extract_result(decoded) 54 | 55 | @property 56 | def leaves(self): 57 | return self._decoding_tree.leaves 58 | 59 | @property 60 | def root(self): 61 | """Returns root node of decoding tree.""" 62 | return self._decoding_tree.root 63 | 64 | @property 65 | def result(self): 66 | return self.root.beta 67 | 68 | def decode(self, received_llr: np.array) -> np.array: 69 | """Implementation of decoding using tree.""" 70 | self._set_initial_state(received_llr) 71 | self._reset_tree_computed_state() 72 | 73 | for leaf in self.leaves: 74 | self._set_decoder_state(self._position) 75 | self._compute_intermediate_alpha(leaf) 76 | leaf() 77 | self._compute_intermediate_beta(leaf) 78 | self._set_next_state(leaf.N) 79 | 80 | return self.result 81 | 82 | def extract_result(self, decoded: np.array) -> np.array: 83 | """Get decoding result. 84 | 85 | Extract info bits from decoded message due to polar code mask. 86 | 87 | """ 88 | decoded_info = list() 89 | 90 | for i in range(self.N): 91 | if self.mask[i] == 1: 92 | decoded_info = np.append(decoded_info, decoded[i]) 93 | return np.array(decoded_info, dtype=np.int) 94 | 95 | def _setup_decoding_tree(self, ): 96 | """Setup decoding tree.""" 97 | return self.node_class(mask=self.mask) 98 | 99 | def _set_initial_state(self, received_llr): 100 | """Initialize decoder with received message.""" 101 | self.current_state = np.zeros(self.n, dtype=np.int8) 102 | self.previous_state = np.ones(self.n, dtype=np.int8) 103 | 104 | # LLR values at intermediate steps 105 | self._position = 0 106 | self._decoding_tree.root.alpha = received_llr 107 | 108 | def _reset_tree_computed_state(self): 109 | """Reset the state of the tree before decoding""" 110 | for node in PreOrderIter(self._decoding_tree): 111 | node.is_computed = False 112 | 113 | def _set_decoder_state(self, position): 114 | """Set current state of the decoder.""" 115 | bits = np.unpackbits( 116 | np.array([position], dtype=np.uint32).byteswap().view(np.uint8) 117 | ) 118 | self.current_state = bits[-self.n:] 119 | 120 | @abc.abstractmethod 121 | def _compute_intermediate_alpha(self, leaf): 122 | """Compute intermediate Alpha values (LLR).""" 123 | 124 | @abc.abstractmethod 125 | def _compute_intermediate_beta(self, node): 126 | """Compute intermediate Beta values (Bits or LLR).""" 127 | 128 | def _set_next_state(self, leaf_size): 129 | self._position += leaf_size 130 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/beta_hard.py: -------------------------------------------------------------------------------- 1 | """Common functions for polar coding.""" 2 | import numba 3 | import numpy as np 4 | 5 | from .node_types import NodeTypes 6 | 7 | # ----------------------------------------------------------------------------- 8 | # Making hard decisions during the decoding 9 | # ----------------------------------------------------------------------------- 10 | 11 | 12 | @numba.njit 13 | def zero( 14 | llr: np.array, 15 | mask_steps: int = 0, 16 | last_chunk_type: int = 0, 17 | ) -> np.array: 18 | """Makes hard decision based on soft input values (LLR).""" 19 | return np.zeros(llr.size, dtype=np.int8) 20 | 21 | 22 | @numba.njit 23 | def make_hard_decision( 24 | llr: np.array, 25 | mask_steps: int = 0, 26 | last_chunk_type: int = 0, 27 | ) -> np.array: 28 | """Makes hard decision based on soft input values (LLR).""" 29 | return np.array([s < 0 for s in llr], dtype=np.int8) 30 | 31 | 32 | @numba.njit 33 | def single_parity_check( 34 | llr: np.array, 35 | mask_steps: int = 0, 36 | last_chunk_type: int = 0, 37 | ) -> np.array: 38 | """Compute bits for Single Parity Check node. 39 | 40 | Based on: https://arxiv.org/pdf/1307.7154.pdf, Section IV, A. 41 | 42 | """ 43 | bits = make_hard_decision(llr) 44 | parity = np.sum(bits) % 2 45 | arg_min = np.abs(llr).argmin() 46 | bits[arg_min] = (bits[arg_min] + parity) % 2 47 | return bits 48 | 49 | 50 | @numba.njit 51 | def repetition( 52 | llr: np.array, 53 | mask_steps: int = 0, 54 | last_chunk_type: int = 0, 55 | ) -> np.array: 56 | """Compute bits for Repetition node. 57 | 58 | Based on: https://arxiv.org/pdf/1307.7154.pdf, Section IV, B. 59 | 60 | """ 61 | return ( 62 | np.zeros(llr.size, dtype=np.int8) if np.sum(llr) >= 0 63 | else np.ones(llr.size, dtype=np.int8) 64 | ) 65 | 66 | 67 | @numba.njit 68 | def g_repetition( 69 | llr: np.array, 70 | mask_steps: int, 71 | last_chunk_type: int, 72 | ) -> np.array: 73 | """Compute bits for Generalized Repetition node. 74 | 75 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, A. 76 | 77 | """ 78 | N = llr.size 79 | step = N // mask_steps # step is equal to a chunk size 80 | 81 | last_alpha = np.zeros(step) 82 | for i in range(step): 83 | last_alpha[i] = np.sum(np.array([ 84 | llr[i + j * step] for j in range(mask_steps) 85 | ])) 86 | 87 | last_beta = ( 88 | make_hard_decision(last_alpha) if last_chunk_type == 1 89 | else single_parity_check(last_alpha) 90 | ) 91 | 92 | result = np.zeros(N) 93 | for i in range(0, N, step): 94 | result[i: i + step] = last_beta 95 | 96 | return result 97 | 98 | 99 | @numba.njit 100 | def rg_parity( 101 | llr: np.array, 102 | mask_steps: int, 103 | last_chunk_type: int = 0, 104 | ) -> np.array: 105 | """Compute bits for Relaxed Generalized Parity Check node. 106 | 107 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, B. 108 | 109 | """ 110 | N = llr.size 111 | step = N // mask_steps # step is equal to a chunk size 112 | result = np.zeros(N) 113 | 114 | for i in range(step): 115 | alpha = np.zeros(mask_steps) 116 | for j in range(mask_steps): 117 | alpha[j] = llr[i + j * step] 118 | 119 | beta = single_parity_check(alpha) 120 | result[i:N:step] = beta 121 | 122 | return result 123 | 124 | 125 | # Mapping between decoding node types and corresponding decoding methods 126 | _methods_map = { 127 | NodeTypes.ZERO: zero, 128 | NodeTypes.ONE: make_hard_decision, 129 | NodeTypes.SINGLE_PARITY_CHECK: single_parity_check, 130 | NodeTypes.REPETITION: repetition, 131 | NodeTypes.RG_PARITY: rg_parity, 132 | NodeTypes.G_REPETITION: g_repetition, 133 | } 134 | 135 | 136 | def compute_beta_hard( 137 | node_type: str, 138 | llr: np.array, 139 | mask_steps: int = 0, 140 | last_chunk_type: int = 0, 141 | *args, **kwargs, 142 | ) -> np.array: 143 | """Unites functions for making hard decisions during decoding.""" 144 | method = _methods_map[node_type] 145 | return method(llr, mask_steps, last_chunk_type, *args, **kwargs) 146 | 147 | 148 | @numba.njit 149 | def compute_parent_beta_hard(left: np.array, right: np.array) -> np.array: 150 | """Compute Beta values for parent Node.""" 151 | N = left.size 152 | result = np.zeros(N * 2, dtype=np.int8) 153 | result[:N] = (left + right) % 2 154 | result[N:] = right 155 | 156 | return result 157 | -------------------------------------------------------------------------------- /python_polar_coding/simulation/simulation.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | from typing import Dict 3 | from concurrent import futures 4 | 5 | from ..channels import SimpleAWGNChannel 6 | from ..modems import SimpleBPSKModem 7 | from ..polar_codes import ( 8 | FastSSCPolarCodec, 9 | GFastSSCPolarCodec, 10 | RCSCANPolarCodec, 11 | ) 12 | from ..polar_codes.fast_scan import FastSCANCodec 13 | from ..polar_codes.g_fast_scan import GFastSCANCodec 14 | from . import functions, http 15 | 16 | 17 | class CodeTypes: 18 | """Code types""" 19 | FAST_SSC = 'fast-ssc' 20 | RC_SCAN = 'rc_scan' 21 | G_FAST_SSC = 'g-fast-ssc' 22 | FAST_SCAN = 'fast-scan' 23 | G_FAST_SCAN = 'g-fast-scan' 24 | ALL = [ 25 | FAST_SSC, 26 | RC_SCAN, 27 | G_FAST_SSC, 28 | FAST_SCAN, 29 | G_FAST_SCAN, 30 | ] 31 | SCAN = [ 32 | RC_SCAN, 33 | FAST_SCAN, 34 | G_FAST_SCAN, 35 | ] 36 | GENERALIZED = [ 37 | G_FAST_SSC, 38 | G_FAST_SCAN, 39 | ] 40 | 41 | 42 | class ChannelTypes: 43 | SIMPLE_BPSK = 'simple-bpsk' 44 | 45 | 46 | CODE_MAP = { 47 | CodeTypes.FAST_SSC: FastSSCPolarCodec, 48 | CodeTypes.RC_SCAN: RCSCANPolarCodec, 49 | CodeTypes.G_FAST_SSC: GFastSSCPolarCodec, 50 | CodeTypes.FAST_SCAN: FastSCANCodec, 51 | CodeTypes.G_FAST_SCAN: GFastSCANCodec, 52 | } 53 | 54 | 55 | MODEM_MAP = { 56 | ChannelTypes.SIMPLE_BPSK: SimpleBPSKModem, 57 | } 58 | 59 | 60 | def simulate(code_type: str, channel_type: str, snr: float, messages: int, 61 | code_params: Dict) -> Dict: 62 | """Simulate polar codes transmission.""" 63 | code = CODE_MAP[code_type](**code_params) 64 | modem = MODEM_MAP[channel_type](fec_rate=code.K/code.N, snr_db=snr) 65 | channel = SimpleAWGNChannel() 66 | 67 | bit_errors, frame_errors = 0, 0 68 | 69 | for _ in range(messages): 70 | message = functions.generate_binary_message(code.K) 71 | encoded = code.encode(message) 72 | modulated = modem.modulate(encoded) 73 | transmitted = channel.transmit(modulated) 74 | demodulated = modem.demodulate(transmitted) 75 | decoded = code.decode(demodulated) 76 | 77 | be, fe = functions.compute_fails(expected=message, decoded=decoded) 78 | bit_errors += be 79 | frame_errors += fe 80 | 81 | return { 82 | 'snr_db': snr, 83 | 'bits': messages * code.K, 84 | 'bit_errors': bit_errors, 85 | 'frames': messages, 86 | 'frame_errors': frame_errors, 87 | } 88 | 89 | 90 | def simulate_from_params(url: str): 91 | """Simulate polar code chain using remote params.""" 92 | status_code, experiment = http.get_params(url=url) 93 | if status_code != 200: 94 | print('No experiment data!') 95 | return 96 | 97 | channel_type = experiment.pop('channel_type') 98 | code_id = experiment.pop('code_id') 99 | code_type = experiment.pop('code_type') 100 | snr = experiment.pop('snr') 101 | messages = experiment.pop('messages') 102 | # Pop `type` to prevent problems with initialization 103 | cls = experiment.pop('type') 104 | 105 | result = simulate( 106 | code_type=code_type, 107 | channel_type=channel_type, 108 | snr=snr, 109 | messages=messages, 110 | code_params=experiment, 111 | ) 112 | 113 | result_log = ( 114 | f'Result: {result}\n' 115 | f'{code_type.upper()} ({experiment["N"]},{experiment["K"]})' 116 | ) 117 | if code_type in CodeTypes.SCAN: 118 | result_log += f', I = {experiment["I"]}' 119 | if code_type in CodeTypes.GENERALIZED: 120 | result_log += f', AF = {experiment["AF"]}' 121 | print(result_log) 122 | 123 | resp = http.save_result( 124 | url=url, 125 | result=result, 126 | code_id=code_id, 127 | code_type=code_type, 128 | channel_type=channel_type, 129 | cls=cls, 130 | ) 131 | print(f'Status {resp.status_code}: {resp.json()}') 132 | 133 | 134 | def simulate_multi_core(experiments: int, url: str): 135 | """Simulate polar code chain using multiple cores.""" 136 | workers = multiprocessing.cpu_count() 137 | print(f'Workers: {workers}; Number of experiments: {experiments}') 138 | 139 | with futures.ProcessPoolExecutor(max_workers=workers) as ex: 140 | run_tasks = { 141 | ex.submit(simulate_from_params, *(url, )): (url, ) 142 | for _ in range(experiments) 143 | } 144 | for future in futures.as_completed(run_tasks): 145 | try: 146 | future.result() 147 | except Exception as exc: 148 | print(exc) 149 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/functions/node_types.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from python_polar_coding.polar_codes.utils import splits 4 | 5 | 6 | class NodeTypes: 7 | """Types of decoding nodes.""" 8 | ZERO = 'ZERO' 9 | ONE = 'ONE' 10 | SINGLE_PARITY_CHECK = 'SINGLE-PARITY-CHECK' 11 | REPETITION = 'REPETITION' 12 | G_REPETITION = 'G-REPETITION' 13 | RG_PARITY = 'RG-PARITY' 14 | 15 | OTHER = 'OTHER' 16 | 17 | 18 | class NodeTypeDetector: 19 | """Class used to detect the type of decoding node.""" 20 | # Minimal size of Single parity check node 21 | SPC_MIN_SIZE = 4 22 | # Minimal size of Repetition Fast SSC Node 23 | REPETITION_MIN_SIZE = 2 24 | # Minimal number of chunks in generalized nodes 25 | MIN_CHUNKS = 2 26 | 27 | def __init__(self, *args, **kwargs): 28 | self.last_chunk_type = None 29 | self.mask_steps = None 30 | 31 | def __call__( 32 | self, 33 | supported_nodes: list, 34 | mask: np.array, 35 | AF: int = 0, 36 | ) -> str: 37 | """Get type of decoding Node.""" 38 | self.N = mask.size 39 | self.AF = AF 40 | 41 | if (NodeTypes.ONE in supported_nodes 42 | and self._is_one(mask)): 43 | return NodeTypes.ONE 44 | if (NodeTypes.ZERO in supported_nodes 45 | and self._is_zero(mask)): 46 | return NodeTypes.ZERO 47 | if (NodeTypes.SINGLE_PARITY_CHECK in supported_nodes 48 | and self._is_single_parity_check(mask)): 49 | return NodeTypes.SINGLE_PARITY_CHECK 50 | if (NodeTypes.REPETITION in supported_nodes 51 | and self._is_repetition(mask)): 52 | return NodeTypes.REPETITION 53 | if (NodeTypes.RG_PARITY in supported_nodes 54 | and self._is_rg_parity(mask)): 55 | return NodeTypes.RG_PARITY 56 | if (NodeTypes.G_REPETITION in supported_nodes 57 | and self._is_g_repetition(mask)): 58 | return NodeTypes.G_REPETITION 59 | 60 | return NodeTypes.OTHER 61 | 62 | def _is_one(self, mask: np.array) -> bool: 63 | return np.all(mask == 1) 64 | 65 | def _is_zero(self, mask: np.array) -> bool: 66 | return np.all(mask == 0) 67 | 68 | def _is_single_parity_check(self, mask: np.array) -> bool: 69 | return ( 70 | mask.size >= self.SPC_MIN_SIZE and 71 | mask[0] == 0 and 72 | np.sum(mask) == mask.size - 1 73 | ) 74 | 75 | def _is_repetition(self, mask: np.array) -> bool: 76 | return ( 77 | mask.size >= self.REPETITION_MIN_SIZE and 78 | mask[-1] == 1 and 79 | np.sum(mask) == 1 80 | ) 81 | 82 | def _is_g_repetition(self, mask: np.array) -> bool: 83 | """Check the node is Generalized Repetition node. 84 | 85 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, A. 86 | 87 | """ 88 | # 1. Split mask into T chunks, T in range [2, 4, ..., N/2] 89 | for t in splits(self.MIN_CHUNKS, self.N // 2): 90 | chunks = np.split(mask, t) 91 | 92 | last = chunks[-1] 93 | last_ok = self._is_single_parity_check(last) or self._is_one(last) 94 | 95 | if not last_ok: 96 | continue 97 | 98 | others_ok = all(self._is_zero(c) for c in chunks[:-1]) 99 | if not others_ok: 100 | continue 101 | 102 | self.last_chunk_type = 1 if self._is_one(last) else 0 103 | self.mask_steps = t 104 | return True 105 | 106 | return False 107 | 108 | def _is_rg_parity(self, mask: np.array) -> bool: 109 | """Check the node is Relaxed Generalized Parity Check node. 110 | 111 | Based on: https://arxiv.org/pdf/1804.09508.pdf, Section III, B. 112 | 113 | """ 114 | # 1. Split mask into T chunks, T in range [2, 4, ..., N/2] 115 | for t in splits(self.MIN_CHUNKS, self.N // 2): 116 | chunks = np.split(mask, t) 117 | 118 | first = chunks[0] 119 | if not self._is_zero(first): 120 | continue 121 | 122 | ones = 0 123 | spcs = 0 124 | 125 | for c in chunks[1:]: 126 | if self._is_one(c): 127 | ones += 1 128 | elif self._is_single_parity_check(c): 129 | spcs += 1 130 | 131 | others_ok = (ones + spcs + 1) == t and spcs <= self.AF 132 | if not others_ok: 133 | continue 134 | 135 | self.mask_steps = t 136 | return True 137 | 138 | return False 139 | 140 | 141 | get_node_type = NodeTypeDetector() 142 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/base/codec.py: -------------------------------------------------------------------------------- 1 | import abc 2 | from typing import Union 3 | from operator import itemgetter 4 | 5 | import numpy as np 6 | 7 | from python_polar_coding.polar_codes import pcc, utils 8 | 9 | from . import encoder 10 | 11 | 12 | class BasePolarCodec(metaclass=abc.ABCMeta): 13 | """Basic codec for Polar code. 14 | 15 | Includes code construction. 16 | Defines the basic workflow for encoding and decoding. 17 | 18 | Supports creation of a polar code from custom mask. 19 | 20 | """ 21 | encoder_class = encoder.Encoder 22 | decoder_class = None 23 | 24 | BHATTACHARYYA = 'bhattacharyya' 25 | GAUSSIAN = 'gaussian' 26 | MONTE_CARLO = 'monte carlo' 27 | 28 | PCC_METHODS = { 29 | BHATTACHARYYA: pcc.bhattacharyya_bounds, 30 | } 31 | 32 | def __init__(self, N: int, K: int, 33 | design_snr: float = 0.0, 34 | is_systematic: bool = True, 35 | mask: Union[str, None] = None, 36 | pcc_method: str = BHATTACHARYYA): 37 | 38 | assert K < N, (f'Cannot create Polar code with N = {N}, K = {K}.' 39 | f'\nN must be bigger than K.') 40 | 41 | self.N = N 42 | self.K = K 43 | self.n = int(np.log2(N)) 44 | self.design_snr = design_snr 45 | self.is_systematic = is_systematic 46 | 47 | self.pcc_method = pcc_method 48 | self.channel_estimates = self._compute_channels_estimates( 49 | N=self.N, n=self.n, design_snr=design_snr, pcc_method=pcc_method) 50 | self.mask = self._polar_code_construction(mask) 51 | 52 | self.encoder = self.init_encoder() 53 | self.decoder = self.init_decoder() 54 | 55 | def __str__(self): 56 | return (f'({self.N}, {self.K}) Polar code.\n' 57 | f'Design SNR: {self.design_snr} dB\n' 58 | f'Systematic: {str(self.is_systematic)}\n') 59 | 60 | def to_dict(self): 61 | """Get code parameters as a dict.""" 62 | return { 63 | 'type': self.__class__.__name__, 64 | 'N': self.N, 65 | 'K': self.K, 66 | 'is_systematic': self.is_systematic, 67 | 'design_snr': self.design_snr, 68 | 'pcc_method': self.pcc_method, 69 | 'mask': ''.join(str(m) for m in self.mask), 70 | } 71 | 72 | def init_encoder(self): 73 | """Get Polar Encoder instance.""" 74 | return self.encoder_class(mask=self.mask, n=self.n, 75 | is_systematic=self.is_systematic) 76 | 77 | @abc.abstractmethod 78 | def init_decoder(self): 79 | """Get Polar Decoder instance.""" 80 | 81 | def encode(self, message: np.array) -> np.array: 82 | """Encode binary message.""" 83 | return self.encoder.encode(message) 84 | 85 | def decode(self, received_message: np.array) -> np.array: 86 | """Decode received message presented as LLR values.""" 87 | return self.decoder.decode(received_message) 88 | 89 | def _compute_channels_estimates(self, N: int, n: int, design_snr: float, 90 | pcc_method: str): 91 | """Compute bit channels estimates for the polar code.""" 92 | if pcc_method not in self.PCC_METHODS.keys(): 93 | return None 94 | 95 | pcc_method = self.PCC_METHODS[pcc_method] 96 | channel_estimates = pcc_method(N, design_snr) 97 | 98 | # bit-reversal approach https://arxiv.org/abs/1307.7154 (Section III-D) 99 | return np.array([ 100 | channel_estimates[utils.reverse_bits(i, n)] for i in range(N) 101 | ]) 102 | 103 | def _polar_code_construction(self, custom_mask=None) -> np.array: 104 | """Construct polar mask. 105 | 106 | If a mask was given as a string of 1s and 0s, it converts it to array. 107 | 108 | """ 109 | if custom_mask: 110 | return np.array([int(b) for b in custom_mask]) 111 | return self._construct_polar_mask(self.K) 112 | 113 | def _construct_polar_mask(self, K): 114 | """Build polar code Mask based on channels estimates. 115 | 116 | 0 means frozen bit, 1 means information position. 117 | 118 | Supports bit-reversal approach, described in Section III-D of 119 | https://arxiv.org/abs/1307.7154 120 | 121 | """ 122 | # represent each bit as tuple of 3 parameters: 123 | # (order, channels estimate, frozen / information position) 124 | mask = [[i, b, 0] for i, b in enumerate(self.channel_estimates)] 125 | 126 | # sort channels due to estimates 127 | mask = sorted(mask, key=itemgetter(1)) 128 | # set information position for first `info_length` channels 129 | for m in mask[:K]: 130 | m[2] = 1 131 | # sort channels due to order 132 | mask = sorted(mask, key=itemgetter(0)) 133 | # return mask, contains 0s or 1s due to frozen/info position 134 | return np.array([m[2] for m in mask]) 135 | -------------------------------------------------------------------------------- /tests/test_g_fast_ssc/test_node.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.base import NodeTypes 6 | from python_polar_coding.polar_codes.g_fast_ssc import GFastSSCNode 7 | 8 | 9 | class GeneralizedFastSSCNodeTest(TestCase): 10 | 11 | def setUp(self): 12 | self.llr = np.array([ 13 | -2.7273, -8.7327, 0.1087, -1.6463, 14 | 2.7273, -8.7327, -0.1087, 1.6463, 15 | -2.7273, -8.7327, -0.1087, 1.6463, 16 | 2.7273, 8.7326, 1.1087, -1.6463, 17 | ]) 18 | self.g_rep_one = np.array([ 19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 20 | ]) 21 | self.g_rep_one_long = np.array([ 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 24 | ]) 25 | self.g_rep_spc = np.array([ 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 27 | ]) 28 | self.g_rep_spc_long = np.array([ 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 31 | ]) 32 | 33 | self.rg_parity_ones = np.array([ 34 | 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 35 | ]) 36 | self.rg_parity_ones_long = np.array([ 37 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 38 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39 | ]) 40 | 41 | self.rg_parity_ones_spc = np.array([ 42 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 43 | ]) 44 | self.rg_parity_ones_spc_long = np.array([ 45 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 46 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47 | ]) 48 | 49 | def test_g_repetition_one(self): 50 | node = GFastSSCNode(mask=self.g_rep_one) 51 | self.assertEqual(node.node_type, NodeTypes.G_REPETITION) 52 | 53 | expected_result = np.array([0, 1, 0, 0, 54 | 0, 1, 0, 0, 55 | 0, 1, 0, 0, 56 | 0, 1, 0, 0, ]) 57 | 58 | node.alpha = self.llr 59 | node() 60 | np.testing.assert_array_equal(node.beta, expected_result) 61 | 62 | def test_g_repetition_one_long(self): 63 | node = GFastSSCNode(mask=self.g_rep_one_long) 64 | self.assertEqual(node.node_type, NodeTypes.G_REPETITION) 65 | 66 | def test_g_repetition_spc(self): 67 | node = GFastSSCNode(mask=self.g_rep_spc) 68 | self.assertEqual(node.node_type, NodeTypes.G_REPETITION) 69 | 70 | expected_result = np.array([1, 1, 0, 0, 71 | 1, 1, 0, 0, 72 | 1, 1, 0, 0, 73 | 1, 1, 0, 0, ]) 74 | 75 | node.alpha = self.llr 76 | node() 77 | np.testing.assert_array_equal(node.beta, expected_result) 78 | 79 | def test_g_repetition_spc_long(self): 80 | node = GFastSSCNode(mask=self.g_rep_spc_long) 81 | self.assertEqual(node.node_type, NodeTypes.G_REPETITION) 82 | 83 | def test_rg_parity_ones(self): 84 | node = GFastSSCNode(mask=self.rg_parity_ones) 85 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 86 | 87 | expected_result = np.array([1, 1, 0, 1, 88 | 0, 1, 1, 0, 89 | 1, 1, 1, 0, 90 | 0, 1, 0, 1, ]) 91 | 92 | node.alpha = self.llr 93 | node() 94 | np.testing.assert_array_equal(node.beta, expected_result) 95 | 96 | def test_rg_parity_ones_long(self): 97 | node = GFastSSCNode(mask=self.rg_parity_ones_long) 98 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 99 | 100 | def test_rg_parity_spc_ones_spc_AF_1(self): 101 | node = GFastSSCNode(mask=self.rg_parity_ones_spc, AF=1) 102 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 103 | 104 | expected_result = np.array([1, 1, 0, 1, 105 | 0, 1, 1, 0, 106 | 1, 1, 1, 0, 107 | 0, 1, 0, 1, ]) 108 | 109 | node.alpha = self.llr 110 | node() 111 | np.testing.assert_array_equal(node.beta, expected_result) 112 | 113 | def test_rg_parity_spc_ones_long_AF_1(self): 114 | node = GFastSSCNode(mask=self.rg_parity_ones_spc_long, AF=1) 115 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 116 | 117 | def test_rg_parity_spc_ones_spc_AF_2(self): 118 | mask = np.array([0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, ]) 119 | node = GFastSSCNode(mask=mask) 120 | self.assertNotEqual(node.node_type, NodeTypes.RG_PARITY) 121 | 122 | node = GFastSSCNode(mask=mask, AF=2) 123 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 124 | 125 | def test_rg_parity_spc_ones_spc_AF_3(self): 126 | mask = np.array([0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, ]) 127 | node = GFastSSCNode(mask=mask, AF=2) 128 | self.assertNotEqual(node.node_type, NodeTypes.RG_PARITY) 129 | 130 | node = GFastSSCNode(mask=mask, AF=3) 131 | self.assertEqual(node.node_type, NodeTypes.RG_PARITY) 132 | -------------------------------------------------------------------------------- /python_polar_coding/polar_codes/sc/decoder.py: -------------------------------------------------------------------------------- 1 | import numba 2 | import numpy as np 3 | 4 | from ..base import decoder, functions 5 | 6 | 7 | class SCDecoder(decoder.BaseDecoder): 8 | """Implements SC decoding algorithm. 9 | 10 | Stores initial and intermediate LLR values, intermediate bit values and 11 | metrics for forking SC List decoder tree. 12 | 13 | Args: 14 | mask (np.array): Polar code mask. 15 | is_systematic (bool): Systematic code or not 16 | 17 | """ 18 | 19 | def __init__(self, n: int, mask: np.array, is_systematic: bool = True): 20 | super().__init__(n=n, mask=mask, is_systematic=is_systematic) 21 | 22 | self._current_decision = 0 23 | 24 | # LLR values at intermediate steps 25 | self.intermediate_llr = None 26 | # Bit values at intermediate steps 27 | self.intermediate_bits = None 28 | self.current_state = np.zeros(self.n, dtype=np.int8) 29 | self.previous_state = np.ones(self.n, dtype=np.int8) 30 | 31 | def decode_internal(self, received_llr: np.array) -> np.array: 32 | """Implementation of SC decoding method.""" 33 | self._set_initial_state(received_llr) 34 | 35 | for pos in range(self.N): 36 | self._decode_position(pos) 37 | 38 | return self.result 39 | 40 | @property 41 | def result(self): 42 | if self.is_systematic: 43 | return self.intermediate_bits[0] 44 | return self.intermediate_bits[-1] 45 | 46 | def _set_initial_state(self, received_llr): 47 | """Initialize decoder with received message""" 48 | self.current_state = np.zeros(self.n, dtype=np.int8) 49 | self.previous_state = np.ones(self.n, dtype=np.int8) 50 | # LLR values at intermediate steps 51 | self.intermediate_llr = self._get_intermediate_llr_structure( 52 | received_llr) 53 | # Bit values at intermediate steps 54 | self.intermediate_bits = self._get_intermediate_bits_structure() 55 | 56 | def _get_intermediate_llr_structure(self, received_llr): 57 | intermediate_llr = [received_llr, ] 58 | length = self.N // 2 59 | while length > 0: 60 | intermediate_llr.append(np.zeros(length, dtype=np.double)) 61 | length //= 2 62 | return intermediate_llr 63 | 64 | def _get_intermediate_bits_structure(self): 65 | return [np.zeros(self.N, dtype=np.int8) for _ in range(self.n + 1)] 66 | 67 | def _decode_position(self, position): 68 | """Decode single position.""" 69 | self._set_decoder_state(position) 70 | self._compute_intermediate_alpha(position) 71 | self._compute_beta(position) 72 | self._compute_intermediate_beta(position) 73 | self._update_decoder_state() 74 | 75 | def _set_decoder_state(self, position): 76 | """Set current state of the decoder.""" 77 | bits = np.unpackbits( 78 | np.array([position], dtype=np.uint32).byteswap().view(np.uint8) 79 | ) 80 | self.current_state = bits[-self.n:] 81 | 82 | def _compute_intermediate_alpha(self, position): 83 | """Compute intermediate LLR values.""" 84 | for i in range(1, self.n + 1): 85 | llr = self.intermediate_llr[i - 1] 86 | 87 | if self.current_state[i - 1] == self.previous_state[i - 1]: 88 | continue 89 | 90 | if self.current_state[i - 1] == 0: 91 | self.intermediate_llr[i] = functions.compute_left_alpha(llr) 92 | continue 93 | 94 | end = position 95 | start = end - np.power(2, self.n - i) 96 | left_bits = self.intermediate_bits[i][start: end] 97 | self.intermediate_llr[i] = functions.compute_right_alpha(llr, left_bits) 98 | 99 | def _compute_beta(self, position): 100 | """Make decision about current decoding value.""" 101 | mask_bit = self.mask[position] 102 | self._current_decision = (int(self.intermediate_llr[-1][0] < 0) 103 | if mask_bit == 1 else 0) 104 | 105 | @staticmethod 106 | @numba.njit 107 | def _compute_left_alpha(llr): 108 | """Compute Alpha (LLR) for left node.""" 109 | N = llr.size // 2 110 | left_llr = np.zeros(N, dtype=np.double) 111 | for i in range(N): 112 | left = llr[i] 113 | right = llr[i + N] 114 | left_llr[i] = (np.sign(left) * np.sign(right) 115 | * np.fabs(np.array([left, right])).min()) 116 | return left_llr 117 | 118 | @staticmethod 119 | @numba.njit 120 | def _compute_right_alpha(llr, left_beta): 121 | """Compute Alpha (LLR) for right node.""" 122 | N = llr.size // 2 123 | right_llr = np.zeros(N, dtype=np.double) 124 | for i in range(N): 125 | right_llr[i] = (llr[i + N] - (2 * left_beta[i] - 1) * llr[i]) 126 | return right_llr 127 | 128 | def _compute_intermediate_beta(self, position): 129 | """Compute intermediate BIT values.""" 130 | self.intermediate_bits[-1][position] = self._current_decision 131 | 132 | for i in range(self.n - 1, -1, -1): 133 | source = self.intermediate_bits[i + 1] 134 | result = self.intermediate_bits[i] 135 | 136 | self.intermediate_bits[i] = functions.compute_encoding_step( 137 | i, self.n, source, result 138 | ) 139 | 140 | def _update_decoder_state(self): 141 | """Set next decoding position.""" 142 | self.previous_state.ravel()[:self.n] = self.current_state 143 | -------------------------------------------------------------------------------- /tests/test_base/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.base import ( 6 | BaseCRCPolarCodec, 7 | BaseDecoder, 8 | BasePolarCodec, 9 | make_hard_decision, 10 | ) 11 | 12 | 13 | class SimpleDecoder(BaseDecoder): 14 | """Simple decoder for testing.""" 15 | def decode_internal(self, received_llr: np.array): 16 | return make_hard_decision(received_llr) 17 | 18 | 19 | class SimplePC(BasePolarCodec): 20 | """Simple polar code for testing.""" 21 | decoder_class = SimpleDecoder 22 | 23 | def init_decoder(self): 24 | return self.decoder_class( 25 | n=self.n, mask=self.mask, is_systematic=self.is_systematic 26 | ) 27 | 28 | 29 | class SimplePCCRC(BaseCRCPolarCodec): 30 | """Simple polar code with CRC support for testing.""" 31 | decoder_class = SimpleDecoder 32 | 33 | def init_decoder(self): 34 | return self.decoder_class( 35 | n=self.n, mask=self.mask, is_systematic=self.is_systematic 36 | ) 37 | 38 | 39 | class TestBasicPolarCode(TestCase): 40 | """Tests for `BasicPolarCode`.""" 41 | 42 | @classmethod 43 | def setUpClass(cls): 44 | cls.codeword_length = 64 45 | cls.info_length = 32 46 | cls.design_snr = 0.0 47 | cls.non_systematic_code = SimplePC( 48 | N=cls.codeword_length, 49 | K=cls.info_length, 50 | is_systematic=False, 51 | design_snr=cls.design_snr, 52 | ) 53 | 54 | cls.systematic_code = SimplePC( 55 | N=cls.codeword_length, 56 | K=cls.info_length, 57 | design_snr=cls.design_snr, 58 | ) 59 | 60 | cls.systematic_crc_code = SimplePCCRC( 61 | N=cls.codeword_length, 62 | K=cls.info_length, 63 | design_snr=cls.design_snr, 64 | crc_size=16, 65 | ) 66 | 67 | # Raw test data 68 | cls.raw_message = '11011001110110100111101111101010' 69 | cls.raw_mask = ( 70 | '0000000000000001000000010011111100000011011111110111111111111111') 71 | cls.raw_mask_crc = ( 72 | '0000000100010111000101111111111101111111111111111111111111111111') 73 | cls.non_sys_encoded = ( 74 | '1100000100100110111101110100010101000110010111101000111111000010') 75 | cls.sys_encoded = ( 76 | '0100001100000111001101111101100110001111001101001111101111101010') 77 | cls.sys_crc_encoded = ( 78 | '0001010101111011001000111011010001111011111010100111000111100110') 79 | 80 | @property 81 | def message(self): 82 | """Info message""" 83 | return np.array([int(m) for m in self.raw_message]) 84 | 85 | @property 86 | def mask(self): 87 | """Expected mask""" 88 | return np.array([int(m) for m in self.raw_mask]) 89 | 90 | @property 91 | def mask_crc(self): 92 | """Expected mask for code with CRC""" 93 | return np.array([int(m) for m in self.raw_mask_crc]) 94 | 95 | @property 96 | def non_sys_enc_msg(self): 97 | """Expected non-systematically encoded message""" 98 | return np.array([int(i) for i in self.non_sys_encoded]) 99 | 100 | @property 101 | def sys_enc_msg(self): 102 | """Expected systematically encoded message""" 103 | return np.array([int(i) for i in self.sys_encoded]) 104 | 105 | @property 106 | def sys_crc_enc_msg(self): 107 | """Expected systematically encoded message with CRC""" 108 | return np.array([int(i) for i in self.sys_crc_encoded]) 109 | 110 | def test_building_polar_mask(self): 111 | """Test `build_polar_code_mask` method.""" 112 | mask1 = self.non_systematic_code._polar_code_construction() 113 | mask2 = self.systematic_code._polar_code_construction() 114 | mask3 = self.systematic_crc_code._polar_code_construction() 115 | 116 | self.assertEqual(np.sum(mask1), self.info_length) 117 | self.assertTrue(all(mask1 == self.mask)) 118 | 119 | self.assertEqual(np.sum(mask2), self.info_length) 120 | self.assertTrue(all(mask2 == self.mask)) 121 | 122 | # CRC code's mask has additional 16 info positions for CRC 123 | self.assertEqual(np.sum(mask3), self.info_length + 16) 124 | self.assertTrue(all(mask3 == self.mask_crc)) 125 | 126 | def test_precode_and_extract(self): 127 | """Test `_precode` and `_extract` methods""" 128 | precoded = self.systematic_code.encoder._precode(self.message) 129 | self.assertEqual(precoded.size, self.codeword_length) 130 | 131 | extracted = self.systematic_code.decoder.extract_result(precoded) 132 | self.assertEqual(extracted.size, self.info_length) 133 | 134 | self.assertTrue(all(extracted == self.message)) 135 | 136 | def test_non_systematic_encode(self): 137 | """Test `encode` method for non-systematic code.""" 138 | encoded = self.non_systematic_code.encode(self.message) 139 | self.assertTrue(all(encoded == self.non_sys_enc_msg)) 140 | 141 | def test_systematic_encode(self): 142 | """Test `encode` method for systematic code.""" 143 | encoded = self.systematic_code.encode(self.message) 144 | self.assertTrue(all(encoded == self.sys_enc_msg)) 145 | 146 | extracted = self.systematic_code.decoder.extract_result(encoded) 147 | self.assertTrue(all(extracted == self.message)) 148 | 149 | def test_systematic_encode_with_crc(self): 150 | """Test for systematic encoding with CRC support""" 151 | encoded = self.systematic_crc_code.encode(self.message) 152 | self.assertTrue(all(encoded == self.sys_crc_enc_msg)) 153 | 154 | extracted = self.systematic_crc_code.decoder.extract_result(encoded) 155 | self.assertTrue(all(extracted[:self.info_length] == self.message)) 156 | -------------------------------------------------------------------------------- /tests/test_sc/test_decoder.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.sc import SCDecoder 6 | 7 | 8 | class TestSCDecoder(TestCase): 9 | @classmethod 10 | def setUpClass(cls): 11 | cls.received_llr = np.array([ 12 | -2.7273, -8.7327, 0.1087, 1.6463, 13 | 0.0506, -0.0552, -1.5304, -2.1233, 14 | ]) 15 | cls.mask = np.array([0, 1, 0, 1, 0, 1, 1, 1, ], dtype=np.int8) 16 | cls.steps = cls.mask.size 17 | cls.decoder = SCDecoder(mask=cls.mask, is_systematic=False, n=3) 18 | 19 | cls.expected_llrs = [ 20 | [ 21 | cls.received_llr, 22 | np.array([-0.0506, 0.0552, -0.1087, -1.6463]), 23 | np.array([0.0506, -0.0552]), 24 | np.array([-0.0506]), 25 | ], 26 | [ 27 | cls.received_llr, 28 | np.array([-0.0506, 0.0552, -0.1087, -1.6463]), 29 | np.array([0.0506, -0.0552]), 30 | np.array([-0.0046]), 31 | ], 32 | [ 33 | cls.received_llr, 34 | np.array([-0.0506, 0.0552, -0.1087, -1.6463]), 35 | np.array([-0.0581, -1.7015]), 36 | np.array([0.0581]), 37 | ], 38 | [ 39 | cls.received_llr, 40 | np.array([-0.0506, 0.0552, -0.1087, -1.6463]), 41 | np.array([-0.0581, -1.7015]), 42 | np.array([-1.7596]), 43 | ], 44 | [ 45 | cls.received_llr, 46 | np.array([-2.6767, -8.7879, -1.6391, -3.7696]), 47 | np.array([1.6391, 3.7696]), 48 | np.array([1.6391]), 49 | ], 50 | [ 51 | cls.received_llr, 52 | np.array([-2.6767, -8.7879, -1.6391, -3.7696]), 53 | np.array([1.6391, 3.7696]), 54 | np.array([5.4087]), 55 | ], 56 | [ 57 | cls.received_llr, 58 | np.array([-2.6767, -8.7879, -1.6391, -3.7696]), 59 | np.array([-4.3158, -12.5575]), 60 | np.array([4.3158]), 61 | ], 62 | [ 63 | cls.received_llr, 64 | np.array([-2.6767, -8.7879, -1.6391, -3.7696]), 65 | np.array([-4.3158, -12.5575]), 66 | np.array([-16.8733]), 67 | ], 68 | ] 69 | cls.expected_decoded = np.array([0, 1, 0, 1, 0, 0, 0, 1]) 70 | cls.expected_bits = [ 71 | [ 72 | np.array([0, 0, 0, 0, 0, 0, 0, 0, ]), 73 | np.array([0, 0, 0, 0, 0, 0, 0, 0, ]), 74 | np.array([0, 0, 0, 0, 0, 0, 0, 0, ]), 75 | np.array([0, 0, 0, 0, 0, 0, 0, 0, ]), 76 | ], 77 | [ 78 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 79 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 80 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 81 | np.array([0, 1, 0, 0, 0, 0, 0, 0, ]), 82 | ], 83 | [ 84 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 85 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 86 | np.array([1, 1, 0, 0, 0, 0, 0, 0, ]), 87 | np.array([0, 1, 0, 0, 0, 0, 0, 0, ]), 88 | ], 89 | [ 90 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 91 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 92 | np.array([1, 1, 1, 1, 0, 0, 0, 0, ]), 93 | np.array([0, 1, 0, 1, 0, 0, 0, 0, ]), 94 | ], 95 | [ 96 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 97 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 98 | np.array([1, 1, 1, 1, 0, 0, 0, 0, ]), 99 | np.array([0, 1, 0, 1, 0, 0, 0, 0, ]), 100 | ], 101 | [ 102 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 103 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 104 | np.array([1, 1, 1, 1, 0, 0, 0, 0, ]), 105 | np.array([0, 1, 0, 1, 0, 0, 0, 0, ]), 106 | ], 107 | [ 108 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 109 | np.array([0, 0, 1, 1, 0, 0, 0, 0, ]), 110 | np.array([1, 1, 1, 1, 0, 0, 0, 0, ]), 111 | np.array([0, 1, 0, 1, 0, 0, 0, 0, ]), 112 | ], 113 | [ 114 | np.array([1, 1, 0, 0, 1, 1, 1, 1, ]), 115 | np.array([0, 0, 1, 1, 1, 1, 1, 1, ]), 116 | np.array([1, 1, 1, 1, 0, 0, 1, 1, ]), 117 | np.array([0, 1, 0, 1, 0, 0, 0, 1, ]), 118 | ], 119 | ] 120 | 121 | def _decoding_step(self, position): 122 | """Single step of decoding process.""" 123 | self.decoder._set_decoder_state(position) 124 | 125 | # Check intermediate LLRs computation 126 | expected_llr = self.expected_llrs[position] 127 | self.decoder._compute_intermediate_alpha(position) 128 | for i in range(self.decoder.n + 1): 129 | np.testing.assert_array_almost_equal( 130 | self.decoder.intermediate_llr[i], 131 | expected_llr[i] 132 | ) 133 | 134 | # Check decoding result 135 | self.decoder._compute_beta(position) 136 | decoded = self.decoder._current_decision 137 | self.assertEqual(decoded, self.expected_decoded[position]) 138 | 139 | # Check intermediate bits computation 140 | self.decoder._compute_intermediate_beta(position) 141 | expected_bits = self.expected_bits[position] 142 | for i in range(self.decoder.n + 1): 143 | np.testing.assert_array_almost_equal( 144 | self.decoder.intermediate_bits[i], 145 | expected_bits[i] 146 | ) 147 | 148 | self.decoder._update_decoder_state() 149 | 150 | def test_decoding_steps(self): 151 | """Test SC decoding process step-by-step.""" 152 | self.decoder._set_initial_state(self.received_llr) 153 | for i in range(self.steps): 154 | self._decoding_step(i) 155 | -------------------------------------------------------------------------------- /tests/test_g_fast_scan/test_codec.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from python_polar_coding.polar_codes.g_fast_scan import GFastSCANCodec 4 | from tests.base import BasicVerifyPolarCode 5 | 6 | # Iterations 2 7 | 8 | 9 | class TestGFastSCANCodec_1024_512_iter_2_AF_0(BasicVerifyPolarCode, TestCase): 10 | polar_code_class = GFastSCANCodec 11 | code_parameters = { 12 | 'N': 1024, 13 | 'K': 512, 14 | 'I': 2, 15 | 'AF': 0, 16 | } 17 | 18 | 19 | class TestGFastSCANCodec_1024_512_iter_2_AF_1(BasicVerifyPolarCode, TestCase): 20 | polar_code_class = GFastSCANCodec 21 | code_parameters = { 22 | 'N': 1024, 23 | 'K': 512, 24 | 'I': 2, 25 | 'AF': 1, 26 | } 27 | 28 | 29 | class TestGFastSCANCodec_1024_512_iter_2_AF_2(BasicVerifyPolarCode, TestCase): 30 | polar_code_class = GFastSCANCodec 31 | code_parameters = { 32 | 'N': 1024, 33 | 'K': 512, 34 | 'I': 2, 35 | 'AF': 2, 36 | } 37 | 38 | 39 | class TestGFastSCANCodec_1024_512_iter_2_AF_3(BasicVerifyPolarCode, TestCase): 40 | polar_code_class = GFastSCANCodec 41 | code_parameters = { 42 | 'N': 1024, 43 | 'K': 512, 44 | 'I': 2, 45 | 'AF': 3, 46 | } 47 | 48 | 49 | class TestGFastSCANCodec_1024_256_iter_2_AF_0(BasicVerifyPolarCode, TestCase): 50 | polar_code_class = GFastSCANCodec 51 | code_parameters = { 52 | 'N': 1024, 53 | 'K': 256, 54 | 'I': 2, 55 | 'AF': 0, 56 | } 57 | 58 | 59 | class TestGFastSCANCodec_1024_256_iter_2_AF_1(BasicVerifyPolarCode, TestCase): 60 | polar_code_class = GFastSCANCodec 61 | code_parameters = { 62 | 'N': 1024, 63 | 'K': 256, 64 | 'I': 2, 65 | 'AF': 1, 66 | } 67 | 68 | 69 | class TestGFastSCANCodec_1024_256_iter_2_AF_2(BasicVerifyPolarCode, TestCase): 70 | polar_code_class = GFastSCANCodec 71 | code_parameters = { 72 | 'N': 1024, 73 | 'K': 256, 74 | 'I': 2, 75 | 'AF': 2, 76 | } 77 | 78 | 79 | class TestGFastSCANCodec_1024_256_iter_2_AF_3(BasicVerifyPolarCode, TestCase): 80 | polar_code_class = GFastSCANCodec 81 | code_parameters = { 82 | 'N': 1024, 83 | 'K': 256, 84 | 'I': 2, 85 | 'AF': 3, 86 | } 87 | 88 | 89 | class TestGFastSCANCodec_1024_768_iter_2_AF_0(BasicVerifyPolarCode, TestCase): 90 | polar_code_class = GFastSCANCodec 91 | code_parameters = { 92 | 'N': 1024, 93 | 'K': 768, 94 | 'I': 2, 95 | 'AF': 0, 96 | } 97 | 98 | 99 | class TestGFastSCANCodec_1024_768_iter_2_AF_1(BasicVerifyPolarCode, TestCase): 100 | polar_code_class = GFastSCANCodec 101 | code_parameters = { 102 | 'N': 1024, 103 | 'K': 768, 104 | 'I': 2, 105 | 'AF': 1, 106 | } 107 | 108 | 109 | class TestGFastSCANCodec_1024_768_iter_2_AF_2(BasicVerifyPolarCode, TestCase): 110 | polar_code_class = GFastSCANCodec 111 | code_parameters = { 112 | 'N': 1024, 113 | 'K': 768, 114 | 'I': 2, 115 | 'AF': 2, 116 | } 117 | 118 | 119 | class TestGFastSCANCodec_1024_768_iter_2_AF_3(BasicVerifyPolarCode, TestCase): 120 | polar_code_class = GFastSCANCodec 121 | code_parameters = { 122 | 'N': 1024, 123 | 'K': 768, 124 | 'I': 2, 125 | 'AF': 3, 126 | } 127 | 128 | 129 | # Iterations 4 130 | 131 | 132 | class TestGFastSCANCodec_1024_512_iter_4_AF_0(BasicVerifyPolarCode, TestCase): 133 | polar_code_class = GFastSCANCodec 134 | code_parameters = { 135 | 'N': 1024, 136 | 'K': 512, 137 | 'I': 4, 138 | 'AF': 0, 139 | } 140 | 141 | 142 | class TestGFastSCANCodec_1024_512_iter_4_AF_1(BasicVerifyPolarCode, TestCase): 143 | polar_code_class = GFastSCANCodec 144 | code_parameters = { 145 | 'N': 1024, 146 | 'K': 512, 147 | 'I': 4, 148 | 'AF': 1, 149 | } 150 | 151 | 152 | class TestGFastSCANCodec_1024_512_iter_4_AF_2(BasicVerifyPolarCode, TestCase): 153 | polar_code_class = GFastSCANCodec 154 | code_parameters = { 155 | 'N': 1024, 156 | 'K': 512, 157 | 'I': 4, 158 | 'AF': 2, 159 | } 160 | 161 | 162 | class TestGFastSCANCodec_1024_512_iter_4_AF_3(BasicVerifyPolarCode, TestCase): 163 | polar_code_class = GFastSCANCodec 164 | code_parameters = { 165 | 'N': 1024, 166 | 'K': 512, 167 | 'I': 4, 168 | 'AF': 3, 169 | } 170 | 171 | 172 | class TestGFastSCANCodec_1024_256_iter_4_AF_0(BasicVerifyPolarCode, TestCase): 173 | polar_code_class = GFastSCANCodec 174 | code_parameters = { 175 | 'N': 1024, 176 | 'K': 256, 177 | 'I': 4, 178 | 'AF': 0, 179 | } 180 | 181 | 182 | class TestGFastSCANCodec_1024_256_iter_4_AF_1(BasicVerifyPolarCode, TestCase): 183 | polar_code_class = GFastSCANCodec 184 | code_parameters = { 185 | 'N': 1024, 186 | 'K': 256, 187 | 'I': 4, 188 | 'AF': 1, 189 | } 190 | 191 | 192 | class TestGFastSCANCodec_1024_256_iter_4_AF_2(BasicVerifyPolarCode, TestCase): 193 | polar_code_class = GFastSCANCodec 194 | code_parameters = { 195 | 'N': 1024, 196 | 'K': 256, 197 | 'I': 4, 198 | 'AF': 2, 199 | } 200 | 201 | 202 | class TestGFastSCANCodec_1024_256_iter_4_AF_3(BasicVerifyPolarCode, TestCase): 203 | polar_code_class = GFastSCANCodec 204 | code_parameters = { 205 | 'N': 1024, 206 | 'K': 256, 207 | 'I': 4, 208 | 'AF': 3, 209 | } 210 | 211 | 212 | class TestGFastSCANCodec_1024_768_iter_4_AF_0(BasicVerifyPolarCode, TestCase): 213 | polar_code_class = GFastSCANCodec 214 | code_parameters = { 215 | 'N': 1024, 216 | 'K': 768, 217 | 'I': 4, 218 | 'AF': 0, 219 | } 220 | 221 | 222 | class TestGFastSCANCodec_1024_768_iter_4_AF_1(BasicVerifyPolarCode, TestCase): 223 | polar_code_class = GFastSCANCodec 224 | code_parameters = { 225 | 'N': 1024, 226 | 'K': 768, 227 | 'I': 4, 228 | 'AF': 1, 229 | } 230 | 231 | 232 | class TestGFastSCANCodec_1024_768_iter_4_AF_2(BasicVerifyPolarCode, TestCase): 233 | polar_code_class = GFastSCANCodec 234 | code_parameters = { 235 | 'N': 1024, 236 | 'K': 768, 237 | 'I': 4, 238 | 'AF': 2, 239 | } 240 | 241 | 242 | class TestGFastSCANCodec_1024_768_iter_4_AF_3(BasicVerifyPolarCode, TestCase): 243 | polar_code_class = GFastSCANCodec 244 | code_parameters = { 245 | 'N': 1024, 246 | 'K': 768, 247 | 'I': 4, 248 | 'AF': 3, 249 | } -------------------------------------------------------------------------------- /tests/test_fast_ssc/test_decoder.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import numpy as np 4 | 5 | from python_polar_coding.polar_codes.fast_ssc import FastSSCDecoder 6 | 7 | 8 | class TestFastSSCDecoder(TestCase): 9 | @classmethod 10 | def setUpClass(cls): 11 | cls.received_llr = np.array([ 12 | -2.7273, 13 | -8.7327, 14 | 0.1087, 15 | 1.6463, 16 | 0.0506, 17 | -0.0552, 18 | -1.5304, 19 | -2.1233, 20 | ]) 21 | cls.length = cls.received_llr.size 22 | cls.n = 3 23 | 24 | def test_zero_node_decoder(self): 25 | mask = np.zeros(self.length, dtype=np.int8) 26 | decoder = FastSSCDecoder(mask=mask, n=self.n) 27 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 28 | 29 | decoder.decode(self.received_llr) 30 | np.testing.assert_equal( 31 | decoder.result, 32 | np.zeros(self.length, dtype=np.int8) 33 | ) 34 | 35 | def test_one_node_decoder(self): 36 | mask = np.ones(self.length, dtype=np.int8) 37 | decoder = FastSSCDecoder(mask=mask, n=self.n) 38 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 39 | 40 | decoder.decode(self.received_llr) 41 | np.testing.assert_equal( 42 | decoder.result, 43 | np.array(self.received_llr < 0, dtype=np.int8) 44 | ) 45 | 46 | def test_spc_node_decoder(self): 47 | mask = np.array([0, 1, 1, 1, 1, 1, 1, 1], dtype=np.int8) 48 | decoder = FastSSCDecoder(mask=mask, n=self.n) 49 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 50 | 51 | decoder.decode(self.received_llr) 52 | np.testing.assert_equal( 53 | decoder.result, 54 | np.array([1, 1, 0, 0, 1, 1, 1, 1], dtype=np.int8) 55 | ) 56 | 57 | def test_repetition_node_decoder(self): 58 | mask = np.array([0, 0, 0, 0, 0, 0, 0, 1], dtype=np.int8) 59 | decoder = FastSSCDecoder(mask=mask, n=self.n) 60 | self.assertEqual(len(decoder._decoding_tree.leaves), 1) 61 | 62 | decoder.decode(self.received_llr) 63 | np.testing.assert_equal( 64 | decoder.result, 65 | np.ones(self.length, dtype=np.int8) 66 | ) 67 | 68 | def test_repetition_spc_node_decoder(self): 69 | mask = np.array([0, 0, 0, 1, 0, 1, 1, 1], dtype=np.int8) 70 | decoder = FastSSCDecoder(mask=mask, n=self.n) 71 | self.assertEqual(len(decoder._decoding_tree.leaves), 2) 72 | 73 | decoder.decode(self.received_llr) 74 | 75 | # Check nodes 76 | # Left node 77 | exp_left_llrs = np.array([-0.0506, 0.0552, -0.1087, -1.6463, ]) 78 | exp_left_bits = np.array([1, 1, 1, 1, ]) 79 | np.testing.assert_array_almost_equal( 80 | decoder._decoding_tree.leaves[0].alpha, 81 | exp_left_llrs 82 | ) 83 | np.testing.assert_equal( 84 | decoder._decoding_tree.leaves[0].beta, 85 | exp_left_bits 86 | ) 87 | 88 | # Right node 89 | exp_right_llrs = np.array([2.7779, 8.6775, -1.6391, -3.7696, ]) 90 | exp_right_bits = np.array([0, 0, 1, 1, ]) 91 | np.testing.assert_array_almost_equal( 92 | decoder._decoding_tree.leaves[1].alpha, 93 | exp_right_llrs 94 | ) 95 | np.testing.assert_equal( 96 | decoder._decoding_tree.leaves[1].beta, 97 | exp_right_bits 98 | ) 99 | 100 | # Overall result 101 | exp_result = np.array([1, 1, 0, 0, 0, 0, 1, 1, ]) 102 | np.testing.assert_equal(decoder.result, exp_result) 103 | 104 | def test_spc_repetition_node_decoder(self): 105 | mask = np.array([0, 1, 1, 1, 0, 0, 0, 1], dtype=np.int8) 106 | decoder = FastSSCDecoder(mask=mask, n=self.n) 107 | self.assertEqual(len(decoder._decoding_tree.leaves), 2) 108 | 109 | decoder.decode(self.received_llr) 110 | 111 | # Check nodes 112 | # Left node 113 | exp_left_llrs = np.array([-0.0506, 0.0552, -0.1087, -1.6463, ]) 114 | exp_left_bits = np.array([0, 0, 1, 1, ]) 115 | np.testing.assert_array_almost_equal( 116 | decoder._decoding_tree.leaves[0].alpha, 117 | exp_left_llrs 118 | ) 119 | np.testing.assert_equal( 120 | decoder._decoding_tree.leaves[0].beta, 121 | exp_left_bits 122 | ) 123 | 124 | # Right node 125 | exp_right_llrs = np.array([-2.6767, -8.7879, -1.6391, -3.7696, ]) 126 | exp_right_bits = np.array([1, 1, 1, 1, ]) 127 | np.testing.assert_array_almost_equal( 128 | decoder._decoding_tree.leaves[1].alpha, 129 | exp_right_llrs 130 | ) 131 | np.testing.assert_equal( 132 | decoder._decoding_tree.leaves[1].beta, 133 | exp_right_bits 134 | ) 135 | 136 | # Overall result 137 | exp_result = np.array([1, 1, 0, 0, 1, 1, 1, 1, ]) 138 | np.testing.assert_equal(decoder.result, exp_result) 139 | 140 | def test_complex(self): 141 | long_msg = np.array([ 142 | 0.1139, 1.4662, 2.8427, 0.8675, 1.2576, -1.1791, 0.7535, 2.2528, 143 | -0.3653, 0.6884, -0.9574, -0.2793, -0.8862, -1.7831, 1.7425, -3.0953, 144 | ]) 145 | mask = np.array( 146 | [1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, ], dtype=np.int8) 147 | sub_codes = [ 148 | np.array([1, 1, ], dtype=np.int8), 149 | np.array([0, 1, ], dtype=np.int8), 150 | np.array([0, 0, 0, 1, ], dtype=np.int8), 151 | np.array([1, ], dtype=np.int8), 152 | np.array([0, ], dtype=np.int8), 153 | np.array([1, ], dtype=np.int8), 154 | np.array([0, ], dtype=np.int8), 155 | np.array([0, 1, 1, 1, ], dtype=np.int8), 156 | ] 157 | decoder = FastSSCDecoder(mask=mask, n=4) 158 | 159 | # Check tree structure 160 | self.assertEqual(len(decoder._decoding_tree.leaves), len(sub_codes)) 161 | for i, leaf in enumerate(decoder._decoding_tree.leaves): 162 | np.testing.assert_equal(leaf.mask, sub_codes[i]) 163 | 164 | decoder.decode(long_msg) 165 | 166 | # Check nodes 167 | expected_llr = [ 168 | np.array([-0.1139, 0.2793, ]), 169 | np.array([-0.8674, 0.9677, ]), 170 | np.array([-0.7723, 1.8675, -0.2039, -2.5321, ]), 171 | np.array([-0.2514, ]), 172 | np.array([0, ]), 173 | np.array([-1.2404, ]), 174 | np.array([0, ]), 175 | np.array([-2.3952, -1.3818, 4.7891, -6.4949, ]), 176 | ] 177 | 178 | expected_bits = [ 179 | np.array([1, 0, ], dtype=np.int8), 180 | np.array([0, 0, ], dtype=np.int8), 181 | np.array([1, 1, 1, 1, ], dtype=np.int8), 182 | np.array([1, ], dtype=np.int8), 183 | np.array([0, ], dtype=np.int8), 184 | np.array([1, ], dtype=np.int8), 185 | np.array([0, ], dtype=np.int8), 186 | np.array([1, 0, 0, 1, ], dtype=np.int8), 187 | ] 188 | 189 | for i, leaf in enumerate(decoder._decoding_tree.leaves): 190 | np.testing.assert_almost_equal(leaf.alpha, expected_llr[i]) 191 | np.testing.assert_equal(leaf.beta, expected_bits[i]) 192 | 193 | # Check result 194 | np.testing.assert_equal( 195 | decoder.result, 196 | np.array([1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, ], 197 | dtype=np.int8) 198 | ) 199 | -------------------------------------------------------------------------------- /results/sc_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "SCListPolarCode", 4 | "codeword_length": 1024, 5 | "info_length": 512, 6 | "design_snr": 0.0, 7 | "is_systematic": true, 8 | "is_crc_aided": false, 9 | "polar_mask": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000001110001011101111111000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000010111000000010001011100111111111111110000000000000001000000010001111100000011011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000000000000000000000000000000000000100000001000101110000000000000001000000110111111100010111011111110111111111111111000000000000011100010111011111110001011101111111111111111111111100011111111111111111111111111111111111111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 10 | "extra_params": { 11 | "list_size": 2 12 | }, 13 | "bit_error_rate": { 14 | "1.0": 0.05369921875, 15 | "1.5": 0.014716015625, 16 | "2.0": 0.0031791015625, 17 | "2.5": 0.00032890625 18 | }, 19 | "frame_error_rate": { 20 | "1.0": 0.6049, 21 | "1.5": 0.2498, 22 | "2.0": 0.077, 23 | "2.5": 0.0125 24 | }, 25 | "messages": 10000 26 | }, 27 | { 28 | "type": "SCListPolarCode", 29 | "codeword_length": 1024, 30 | "info_length": 512, 31 | "design_snr": 0.0, 32 | "is_systematic": true, 33 | "is_crc_aided": false, 34 | "polar_mask": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000001110001011101111111000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000010111000000010001011100111111111111110000000000000001000000010001111100000011011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000000000000000000000000000000000000100000001000101110000000000000001000000110111111100010111011111110111111111111111000000000000011100010111011111110001011101111111111111111111111100011111111111111111111111111111111111111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 35 | "extra_params": { 36 | "list_size": 4 37 | }, 38 | "bit_error_rate": { 39 | "1.0": 0.0426021484375, 40 | "1.5": 0.0083537109375, 41 | "2.0": 0.001446484375, 42 | "2.5": 0.0002865234375 43 | }, 44 | "frame_error_rate": { 45 | "1.0": 0.5177, 46 | "1.5": 0.1636, 47 | "2.0": 0.0449, 48 | "2.5": 0.0105 49 | }, 50 | "messages": 10000 51 | }, 52 | { 53 | "type": "SCListPolarCode", 54 | "codeword_length": 1024, 55 | "info_length": 512, 56 | "design_snr": 0.0, 57 | "is_systematic": true, 58 | "is_crc_aided": true, 59 | "polar_mask": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000000000000000000000000000000000000000000011100000000000000000000000100010111000000010001111101111111111111110000000000000001000000010011111100000111011111110111111111111111000101110111111101111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000100000001000111110000000000000001000001110111111100010111011111110111111111111111000000000001011100010111011111110001111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000100010111001111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 60 | "extra_params": { 61 | "list_size": 4 62 | }, 63 | "bit_error_rate": { 64 | "1.0": 0.001296875, 65 | "1.5": 0.0004439453125, 66 | "2.0": 0.0001052734375, 67 | "2.5": 1.54296875e-05 68 | }, 69 | "frame_error_rate": { 70 | "1.0": 0.664, 71 | "1.5": 0.2273, 72 | "2.0": 0.0539, 73 | "2.5": 0.0079 74 | }, 75 | "messages": 10000 76 | }, 77 | { 78 | "type": "SCListPolarCode", 79 | "codeword_length": 1024, 80 | "info_length": 512, 81 | "design_snr": 0.0, 82 | "is_systematic": true, 83 | "is_crc_aided": false, 84 | "polar_mask": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000001110001011101111111000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000010111000000010001011100111111111111110000000000000001000000010001111100000011011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000000000000000000000000000000000000100000001000101110000000000000001000000110111111100010111011111110111111111111111000000000000011100010111011111110001011101111111111111111111111100011111111111111111111111111111111111111111111111111111111111110000000100010111000111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 85 | "extra_params": { 86 | "list_size": 8 87 | }, 88 | "bit_error_rate": { 89 | "1.0": 0.029534375, 90 | "1.5": 0.006350390625, 91 | "2.0": 0.0008841796875, 92 | "2.5": 0.0002134765625 93 | }, 94 | "frame_error_rate": { 95 | "1.0": 0.4097, 96 | "1.5": 0.1335, 97 | "2.0": 0.0306, 98 | "2.5": 0.008 99 | }, 100 | "messages": 10000 101 | }, 102 | { 103 | "type": "SCListPolarCode", 104 | "codeword_length": 1024, 105 | "info_length": 512, 106 | "design_snr": 0.0, 107 | "is_systematic": true, 108 | "is_crc_aided": true, 109 | "polar_mask": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000000000000000000000000000000000000000000011100000000000000000000000100010111000000010001111101111111111111110000000000000001000000010011111100000111011111110111111111111111000101110111111101111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000100000001000111110000000000000001000001110111111100010111011111110111111111111111000000000001011100010111011111110001111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000100010111001111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 110 | "extra_params": { 111 | "list_size": 8 112 | }, 113 | "bit_error_rate": { 114 | "1.0": 0.001058203125, 115 | "1.5": 0.000410546875, 116 | "2.0": 0.0001646484375, 117 | "2.5": 2.8125e-05 118 | }, 119 | "frame_error_rate": { 120 | "1.0": 0.5418, 121 | "1.5": 0.2102, 122 | "2.0": 0.0843, 123 | "2.5": 0.0144 124 | }, 125 | "messages": 10000 126 | } 127 | ] -------------------------------------------------------------------------------- /results/8192_4096.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FastSSCPolarCode", 3 | "codeword_length": 8192, 4 | "info_length": 4096, 5 | "design_snr": 1.4, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000100000001000101110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000010000000000010111000101110111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000000000000000000000100000000000000010000000100010111000000000000000100000001000101110000000100010111001111111111111100000000000000010000001101111111000101110111111101111111111111110001011101111111011111111111111101111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010111000000000000000000000000000000000000000000000001000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000000010001011100000000000000000000000000000001000000000000000100000001011111110000000000000111000101110111111100010111011111110111111111111111000000000000000000000000000001110000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000001000101110000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000000000001000000010001011100000001000101110001011101111111000000010001011100010111011111110001011101111111111111111111111100000001000101110001111111111111001111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000000000000100000001000101110000000100010111000101111111111100000001001111110111111111111111011111111111111111111111111111110000011101111111011111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000010000000100010111000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000001000000000000000100000111011111110000000000000000000000000000011100000001000101110001011101111111000000010001011100010111011111110001011101111111011111111111111100000000000000000000000000000000000000000000000000000000000000010000000000000000000000010001011100000001000101110001011101111111000000000000000100000001000101110000000100010111000101110111111100000001000101110001011101111111000101110111111111111111111111110000000000000001000000010001011100000001000101110001011111111111000000010011111101111111111111110111111111111111111111111111111100000111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000001000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000001000000010001011100000011011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000010001011101111111000101110111111101111111111111110001011101111111011111111111111101111111111111111111111111111111000101110111111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000101110111111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000001000000000000000100000001000101110000000000000001000001110111111100010111011111110111111111111111000000000001011100010111011111110001011101111111011111111111111100010111011111110111111111111111011111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000010001011100010111011111110001011101111111011111111111111100010111011111110111111111111111011111111111111111111111111111110001011101111111011111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": {}, 10 | "bit_error_rate": { 11 | "-1": 0.0, 12 | "0.0": 0.2320851806640625, 13 | "0.5": 0.220201220703125, 14 | "1.0": 0.1956243408203125, 15 | "1.5": 0.0911792236328125, 16 | "2.0": 0.004543212890625, 17 | "2.5": 2.705078125e-05, 18 | "3.0": 0.0, 19 | "3.5": 0.0, 20 | "4.0": 0.0 21 | }, 22 | "frame_error_rate": { 23 | "-1": 0.0, 24 | "0.0": 1.0, 25 | "0.5": 1.0, 26 | "1.0": 0.9991, 27 | "1.5": 0.7115, 28 | "2.0": 0.0648, 29 | "2.5": 0.0008, 30 | "3.0": 0.0, 31 | "3.5": 0.0, 32 | "4.0": 0.0 33 | }, 34 | "messages": 10000 35 | } -------------------------------------------------------------------------------- /results/8192_4096_I_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "RCSCANPolarCode", 3 | "codeword_length": 8192, 4 | "info_length": 4096, 5 | "design_snr": 1.4, 6 | "is_systematic": true, 7 | "is_crc_aided": false, 8 | "polar_mask": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000100000001000101110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000010000000000010111000101110111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000000000000000000000100000000000000010000000100010111000000000000000100000001000101110000000100010111001111111111111100000000000000010000001101111111000101110111111101111111111111110001011101111111011111111111111101111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010111000000000000000000000000000000000000000000000001000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000000010001011100000000000000000000000000000001000000000000000100000001011111110000000000000111000101110111111100010111011111110111111111111111000000000000000000000000000001110000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000100000000000101110001011101111111000000000000000000000001000101110000000100010111000101110111111100000001000101110001011101111111000101110111111101111111111111110000000000000001000000010001011100000001000101110001011101111111000000010001011100010111011111110001011101111111111111111111111100000001000101110001111111111111001111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000000000000100000001000101110000000100010111000101111111111100000001001111110111111111111111011111111111111111111111111111110000011101111111011111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000010000000100010111000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000001000000000000000100000111011111110000000000000000000000000000011100000001000101110001011101111111000000010001011100010111011111110001011101111111011111111111111100000000000000000000000000000000000000000000000000000000000000010000000000000000000000010001011100000001000101110001011101111111000000000000000100000001000101110000000100010111000101110111111100000001000101110001011101111111000101110111111111111111111111110000000000000001000000010001011100000001000101110001011111111111000000010011111101111111111111110111111111111111111111111111111100000111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000001000000010001011100000000000000010000000100010111000000010001011100010111011111110000000000000001000000010001011100000011011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100000000000000010001011101111111000101110111111101111111111111110001011101111111011111111111111101111111111111111111111111111111000101110111111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000101110111111101111111111111110111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000001000000000000000100000001000101110000000000000001000001110111111100010111011111110111111111111111000000000001011100010111011111110001011101111111011111111111111100010111011111110111111111111111011111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111000000010001011100010111011111110001011101111111011111111111111100010111011111110111111111111111011111111111111111111111111111110001011101111111011111111111111101111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111100010111011111110111111111111111011111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000000100010111000101110111111100010111011111110111111111111111000101110111111101111111111111110111111111111111111111111111111100010111011111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 9 | "extra_params": { 10 | "iterations": 1 11 | }, 12 | "bit_error_rate": { 13 | "-1": 0.0, 14 | "0.0": 0.186663916015625, 15 | "0.5": 0.172091259765625, 16 | "1.0": 0.14732138671875, 17 | "1.5": 0.0665339599609375, 18 | "2.0": 0.002488818359375, 19 | "2.5": 2.14111328125e-05, 20 | "3.0": 0.0, 21 | "3.5": 0.0, 22 | "4.0": 0.0 23 | }, 24 | "frame_error_rate": { 25 | "-1": 0.0, 26 | "0.0": 1.0, 27 | "0.5": 1.0, 28 | "1.0": 1.0, 29 | "1.5": 0.7993, 30 | "2.0": 0.065, 31 | "2.5": 0.0006, 32 | "3.0": 0.0, 33 | "3.5": 0.0, 34 | "4.0": 0.0 35 | }, 36 | "messages": 10000 37 | } --------------------------------------------------------------------------------