komm.FixedToVariableCode
Fixed-to-variable length code. A fixed-to-variable length code with source alphabet $\mathcal{S}$, target alphabet $\mathcal{T}$, and source block size $k$ is defined by an injective encoding mapping $\Enc : \mathcal{S}^k \to \mathcal{T}^+$, where the domain is the set of all $k$-tuples with entries in $\mathcal{S}$, and the co-domain is the set of all finite-length, non-empty tuples with entries in $\mathcal{T}$. Here we assume that $\mathcal{S} = [0:S)$ and $\mathcal{T} = [0:T)$, for integers $S \geq 2$ and $T \geq 2$. The elements in the image of $\Enc$ are called codewords.
from_enc_mapping
classmethod
Constructs a fixed-to-variable length code from the encoding mapping $\Enc$.
Parameters:
-
enc_mapping
(dict[Word, Word]
) –The encoding mapping $\Enc$. Must be a dictionary whose keys are all the $k$-tuples of integers in $[0:S)$ and whose values are distinct non-empty tuples of integers in $[0:T)$.
Notes
The source block size $k$ is inferred from the domain of the encoding mapping, and the source and target cardinalities $S$ and $T$ are inferred from the maximum values in the domain and co-domain, respectively.
Examples:
>>> code = komm.FixedToVariableCode.from_enc_mapping({
... (0,): (0,),
... (1,): (1, 0),
... (2,): (1, 1),
... })
>>> code.source_cardinality, code.target_cardinality, code.source_block_size
(3, 2, 1)
>>> code.codewords
[(0,), (1, 0), (1, 1)]
>>> code = komm.FixedToVariableCode.from_enc_mapping({
... (0, 0): (0,),
... (0, 1): (1, 1),
... (1, 0): (1, 1, 0),
... (1, 1): (1, 0, 1),
... })
>>> code.source_cardinality, code.target_cardinality, code.source_block_size
(2, 2, 2)
>>> code.codewords
[(0,), (1, 1), (1, 1, 0), (1, 0, 1)]
from_codewords
classmethod
Constructs a fixed-to-variable length code from the source cardinality $S$ and a list of codewords.
Parameters:
-
source_cardinality
(int
) –The source cardinality $S$. Must be an integer greater than or equal to $2$.
-
codewords
(list[Word]
) –The codewords of the code. Must be a list of length $S^k$ containing tuples of integers in $[0:T)$, where $T$ is the target cardinality of the code. The tuple in position $i$ must be equal to $\Enc(u)$, where $u$ is the $i$-th element in the lexicographic ordering of $[0:S)^k$.
Notes
The source block size $k$ is inferred from the length of the codewords, and the target cardinality $T$ is inferred from the maximum value in the codewords.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(
... source_cardinality=3,
... codewords=[(0,), (1,0), (1,1)],
... )
>>> code.source_cardinality, code.target_cardinality, code.source_block_size
(3, 2, 1)
>>> code.enc_mapping
{(0,): (0,),
(1,): (1, 0),
(2,): (1, 1)}
>>> code = komm.FixedToVariableCode.from_codewords(
... source_cardinality=2,
... codewords=[(0,), (1,1), (1,1,0), (1,0,1)]
... )
>>> (code.source_cardinality, code.target_cardinality, code.source_block_size)
(2, 2, 2)
>>> code.enc_mapping
{(0, 0): (0,),
(0, 1): (1, 1),
(1, 0): (1, 1, 0),
(1, 1): (1, 0, 1)}
source_cardinality: int
property
The source cardinality $S$ of the code. It is the number of symbols in the source alphabet.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.source_cardinality
3
target_cardinality: int
property
The target cardinality $T$ of the code. It is the number of symbols in the target alphabet.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.target_cardinality
2
source_block_size: int
property
The source block size $k$ of the code. It is the number of symbols in each source block.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.source_block_size
1
size: int
property
The number of codewords in the code. It is equal to $S^k$.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.size
3
enc_mapping: dict[Word, Word]
property
The encoding mapping $\Enc$ of the code. It is a dictionary of length $S^k$ whose keys are all the $k$-tuples of integers in $[0:S)$ and whose values are the corresponding codewords.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.enc_mapping
{(0,): (0,), (1,): (1, 0), (2,): (1, 1)}
inv_enc_mapping: dict[Word, Word]
property
The inverse encoding mapping $\Enc^{-1}$ of the code. It is a dictionary of length $S^k$ whose keys are all the codewords of the code and whose values are the corresponding source words.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.inv_enc_mapping
{(0,): (0,), (1, 0): (1,), (1, 1): (2,)}
codewords: list[Word]
property
The codewords of the code. They correspond to the image of the encoding mapping $\Enc$.
Examples:
>>> code = komm.FixedToVariableCode.from_enc_mapping({
... (0,): (0,),
... (1,): (1, 0),
... (2,): (1, 1),
... })
>>> code.codewords
[(0,), (1, 0), (1, 1)]
is_uniquely_decodable
Returns whether the code is uniquely decodable. A code is uniquely decodable if there is a unique way to parse any concatenation of codewords.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.is_uniquely_decodable()
True
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,1)])
>>> code.is_uniquely_decodable()
True
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,0)])
>>> code.is_uniquely_decodable() # 010 can be parsed as 0|10 or 01|0
False
is_prefix_free
Returns whether the code is prefix-free. A code is prefix-free if no codeword is a prefix of any other codeword.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.is_prefix_free()
True
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,1)])
>>> code.is_prefix_free()
False
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,0)])
>>> code.is_prefix_free()
False
kraft_parameter
Computes the Kraft parameter $K$ of the code. This quantity is given by $$ K = \sum_{u \in \mathcal{S}^k} T^{-{\ell_u}}, $$ where $\ell_u$ is the length of the codeword $\Enc(u)$, $T$ is the target cardinality, and $k$ is the source block size.
Returns:
-
kraft_parameter
(float
) –The Kraft parameter $K$ of the code.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(5, [(0,0,0), (0,0,1), (0,1,0), (1,0,1), (1,1)])
>>> code.kraft_parameter()
np.float64(0.75)
>>> code = komm.FixedToVariableCode.from_codewords(4, [(0,), (1,0), (1,1,0), (1,1,1)])
>>> code.kraft_parameter()
np.float64(1.0)
>>> code = komm.FixedToVariableCode.from_codewords(4, [(0,0), (1,1), (0,), (1,)])
>>> code.kraft_parameter()
np.float64(1.5)
rate
Computes the expected rate $R$ of the code, considering a given pmf. This quantity is given by $$ R = \frac{\bar{n}}{k}\vphantom{\Bigg|}, $$ where $\bar{n}$ is the expected codeword length, assuming iid source symbols drawn from $p_X$, and $k$ is the source block size. It is measured in $T$-ary digits per source symbol.
Parameters:
-
pmf
(ArrayLike
) –The (first-order) probability mass function $p_X$ to be considered.
Returns:
-
rate
(float
) –The expected rate $R$ of the code.
Examples:
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (1,0), (1,1)])
>>> code.rate([1/2, 1/4, 1/4])
np.float64(1.5)
encode
Encodes a sequence of source symbols using the code, which must be uniquely decodable.
Parameters:
-
input
(ArrayLike
) –The sequence of symbols to be encoded. Must be a 1D-array with elements in $[0:S)$ (where $S$ is the source cardinality of the code) and have a length that is a multiple of the source block size $k$.
Returns:
-
output
(NDArray[integer]
) –The sequence of encoded symbols. It is a 1D-array with elements in $[0:T)$ (where $T$ is the target cardinality of the code).
Examples:
>>> code = komm.FixedToVariableCode.from_enc_mapping({
... (0, 0): (0,),
... (0, 1): (1, 1),
... (1, 0): (1, 0, 0),
... (1, 1): (1, 0, 1),
... })
>>> code.encode([0, 1, 0, 0])
array([1, 1, 0])
>>> code.encode([0, 1, 0]) # Not a multiple of the source block size
Traceback (most recent call last):
...
ValueError: length of input must be a multiple of block size 2 (got 3)
>>> code.encode([0, 7, 0, 0]) # 07 is not a valid source word
Traceback (most recent call last):
...
ValueError: input contains invalid word
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,0)])
>>> code.encode([0, 1, 0]) # Code is not uniquely decodable
Traceback (most recent call last):
...
ValueError: code is not uniquely decodable
decode
Decodes a sequence of target symbols using the code, which must be uniquely decodable.
Warning
Decoding for non-prefix-free codes is not implemented yet.
Parameters:
-
input
(ArrayLike
) –The sequence of symbols to be decoded. Must be a 1D-array with elements in $[0:T)$ (where $T$ is the target cardinality of the code). Also, the sequence must be a concatenation of codewords (i.e., the output of the
encode
method).
Returns:
-
output
(NDArray[integer]
) –The sequence of decoded symbols. It is a 1D-array with elements in $[0:S)$ (where $S$ is the source cardinality of the code) with a length that is a multiple of the source block size $k$.
Examples:
>>> code = komm.FixedToVariableCode.from_enc_mapping({
... (0, 0): (0,),
... (0, 1): (1, 1),
... (1, 0): (1, 0, 0),
... (1, 1): (1, 0, 1),
... })
>>> code.decode([1, 1, 0])
array([0, 1, 0, 0])
>>> code.decode([0, 0, 1]) # Not a concatenation of codewords
Traceback (most recent call last):
...
ValueError: input contains invalid word
>>> code = komm.FixedToVariableCode.from_codewords(3, [(0,), (0,1), (1,0)])
>>> code.decode([0, 1, 0]) # Code is not uniquely decodable
Traceback (most recent call last):
...
ValueError: code is not uniquely decodable