Coding
The dit.coding module builds both source codes (which compress a source
Distribution) and channel codes (which add redundancy to protect
against channel noise), and exposes their code-theoretic properties.
Source coding
A source code maps the outcomes of a source to codewords over a radix-ary
alphabet (binary by default), with the goal of minimizing the expected number of
code symbols per source symbol – the rate. By the source coding theorem
[CT06], no uniquely decodable code can have a rate below the source
entropy \(\H{X}\).
Base classes
All source codes derive from SourceCoding, which defines the
encode(), decode(), and
rate() interface and provides the comparisons to the source
entropy that are common to every source code:
source_entropy()– \(\H{X}\) inradix-ary digits,redundancy()–rate - source_entropy,efficiency()–source_entropy / rate.
Channel codes derive from the companion ChannelCoding base class,
described under Channel coding below.
Symbol codes
A SymbolCode assigns one codeword to each source outcome. The classic
constructions are available:
shannon()– lengths \(\lceil \log_D 1/p \rceil\), codewords from the cumulative distribution,fano()– top-down balanced partitioning,shannon_fano_elias()– lengths \(\lceil \log_D 1/p \rceil + 1\), codewords from the midpoint cumulative distribution,huffman()– the optimal symbol code,length_limited_huffman()– the optimal code subject to a maximum codeword length (via package-merge),golomb()/rice()– optimal codes for geometric integer sources.
Beyond the rate-based properties, a SymbolCode reports the Kraft sum
(kraft_sum()), whether it is complete
(is_complete()), prefix-free (is_prefix_free()),
uniquely decodable (is_uniquely_decodable(), via
Sardinas-Patterson), and optimal (is_optimal()).
Example
In [1]: from dit.coding import huffman, shannon
In [2]: d = dit.Distribution(['a', 'b', 'c', 'd', 'e'], [0.4, 0.2, 0.2, 0.1, 0.1])
In [3]: code = huffman(d)
In [4]: code.average_length(), code.source_entropy()
Out[4]: (2.2, 2.1219280948873624)
In [5]: code.is_optimal(), code.is_prefix_free(), code.is_complete()
Out[5]: (True, True, True)
In [6]: seq = list(d.outcomes)
In [7]: code.decode(code.encode(seq)) == seq
Out[7]: True
Huffman is optimal, so its average length is no larger than the Shannon code’s:
In [8]: shannon(d).average_length()
Out[8]: 2.8000000000000003
Universal integer codes
The universal codes encode the positive integers without reference to a source
distribution, yet stay within a constant factor of optimal across a wide range of
sources: unary(), elias_gamma(), elias_delta(),
elias_omega(), and fibonacci(). The universal_code() helper wraps
a chosen family into a SymbolCode over an integer-valued source.
Tunstall codes
Where a symbol code maps each symbol to a variable-length codeword, a
tunstall() code parses the source into variable-length words and maps each
word to a fixed-length codeword. It is the variable-to-fixed dual of Huffman and
is realized by TunstallCode.
Polar source coding
Source polarization [Arikan10] applies the Arikan transform on the source side. For \(N = 2^m\) i.i.d. copies of a binary source \(X\) (optionally with side information \(Y\)), the transform \(U^N = X^N G_N\) produces synthesized coordinates whose conditional entropies \(\H{U_i \mid U^{i-1}, Y^N}\) polarize toward \(0\) (deterministic given the past) or \(1\) (uniform given the past) as \(N\) grows. The coordinates that stay near one – the high-entropy set – are exactly what a lossless source code must store; the rest are recovered by sequential decisions and the inverse transform. With side information this is the polar route to Slepian-Wolf coding.
The finite-block utilities are exact (no density evolution), so they are limited to small \(N\):
source_bhattacharyya()– the source Bhattacharyya parameter \(Z(X \mid Y) = 2 \sum_y \sqrt{p(0, y) p(1, y)}\), small when \(X\) is nearly determined by \(Y\) and near one when \(X\) is nearly uniform,source_polarization_profile()– the per-coordinate conditional entropy and source Bhattacharyya (and an optional Goela-stylemax_correlation_with_pastdiagnostic [Goe14]),source_high_entropy_set()– the coordinates a code keeps; by default the lossless set (every coordinate whose conditional entropy exceeds a tolerance).
The polar_source() constructor builds a PolarSourceCode – an exact
finite-block source code (binary source, power-of-two block length, optional
decoder side information). Its encode() returns the
high-entropy coordinates and decode() fills the low-entropy
coordinates by exact maximum a posteriori decisions before inverting the
transform. Because the joint table is enumerated exactly, a max_states guard
prevents accidental exponential blowups.
In [9]: from dit.coding import polar_source, source_polarization_profile
In [10]: dsbs = dit.Distribution(['00', '01', '10', '11'], [0.45, 0.05, 0.05, 0.45])
In [11]: [round(row['entropy'], 3) for row in source_polarization_profile(dsbs, 4, rv=0, crvs=[1])]
Out[11]: [0.875, 0.485, 0.438, 0.078]
In [12]: code = polar_source(dsbs, 8, rv=0, crvs=[1])
In [13]: code.rate(), code.high_entropy_set
Out[13]: (1.0, [0, 1, 2, 3, 4, 5, 6, 7])
Channel coding
A channel code adds redundancy to a message so that it can be recovered after
transmission over a noisy channel. All channel codes in dit.coding are
binary (over \(\mathrm{GF}(2)\)). The usual channel is a conditional
Distribution \(p(Y \mid X)\) – a discrete memoryless channel
applied independently to each transmitted symbol. The Channels page
catalogs ready-made channels, including binary_symmetric_channel()
and binary_erasure_channel() (re-exported from
dit.coding for convenience). Channels with memory can be attached too; see
Channels with memory.
Every channel code derives from ChannelCoding, which provides the
information-theoretic evaluation common to all of them:
capacity_gap()–channel_capacity(channel) - rate, the gap to the channel coding theorem’s limit [CT06] (capacity is computed with the Blahut–Arimoto solver described on Channels; a channel that only brackets its capacity yields a(lower, upper)bracket on the gap),probability_of_error()– the block-error probability under the code’s own decoder, computed exactly for small codes (enumerating every message and received word) or by Monte Carlo otherwise.
Linear block codes
A LinearCode is specified by a generator matrix G; the parity-check
matrix H is derived as a basis for its null space. It supports
hard-decision syndrome decoding (and maximum-likelihood decoding when a channel is
given), and reports the minimum_distance(),
weight_enumerator(), and
error_correcting_capability(). The classical families are
available as constructors: repetition(), parity_check(),
hamming(), reed_muller(), and golay().
Modern codes
Three modern codes specialize the decoder:
ldpc()/gallager()build anLDPCCodedecoded by sum-product belief propagation over its Tanner graph [CT06],polar()builds aPolarCode, freezing the least reliable synthesized bit-channels (by Bhattacharyya parameter) and decoding by successive cancellation,convolutional()builds aConvolutionalCodefrom generator polynomials, decoded with the Viterbi algorithm (soft-decision when a channel is given).
Channel coding example
The Hamming [7, 4, 3] code corrects any single error; over a binary symmetric
channel its exact block-error probability matches the closed form
\(1 - (1-p)^7 - 7p(1-p)^6\):
In [14]: from dit.coding import hamming, binary_symmetric_channel
In [15]: code = hamming(3)
In [16]: code.length, code.dimension, code.minimum_distance()
Out[16]: (7, 4, 3)
In [17]: bsc = binary_symmetric_channel(0.05)
In [18]: code.capacity_gap(bsc)
Out[18]: 0.14217447145547246
In [19]: code.probability_of_error(bsc, method='exact')
Out[19]: 0.044380542187499934
In [20]: 1 - 0.95**7 - 7 * 0.05 * 0.95**6
Out[20]: 0.044380542187500316
Channels with memory
Codes do not require a Distribution: every channel-aware method
first calls as_channel(), which wraps a conditional distribution as a
MemorylessChannel and accepts any other object implementing a small
duck-typed protocol:
inputsandoutputs– the alphabets, as tuples,log_likelihood(codeword, received)– \(\log p(y^n \mid x^n)\), used by maximum-likelihood decoding and by the exact block-error probability,sample(codeword, prng)– one received word, used by Monte Carlo,symbol_log_likelihoods(received)– an(n, len(inputs))array of \(\log p(y^n \mid x_i = a)\) up to a per-row constant, from which belief propagation, successive cancellation, and soft Viterbi form their per-position metrics,optionally
capacity(**kwargs)(a float or a(lower, upper)bracket) andbhattacharyya()(needed only to design a polar code),optionally
feedback_initial(),feedback_step(belief, x)andfeedback_capacity(**kwargs), used only by the feedback codes.
A finite-state channel \(T(y, s' \mid s, x)\) computes log_likelihood
with the forward algorithm and symbol_log_likelihoods with the
forward-backward (BCJR) recursions [BCJR74] under uniform i.i.d. inputs
– the standard separate detect-then-decode approach. Maximum-likelihood decoding
and the exact error probability remain exact over such a channel; the per-symbol
decoders are approximate because they ignore the correlations the channel
induces between positions.
An object may also define __dit_channel__() returning a protocol object, so
another library can make its own channel types attachable without dit
knowing about them. For example, sofic finite-state channels (Mealy
machines such as the trapdoor channel) work directly:
from dit.coding import hamming
from sofic.examples import trapdoor
code = hamming(3)
code.probability_of_error(trapdoor(), method='exact')
code.capacity_gap(trapdoor()) # a (lower, upper) bracket
Codes with feedback
With a noiseless feedback link the transmitter sees each output before choosing the next input, so a code is a strategy \(x_t = f(m, y_{0:t})\) and the number of channel uses \(N\) may be random. Feedback does not increase the capacity of a memoryless channel [CT06], but it makes capacity-achieving schemes much simpler and improves the error exponent [Bur76]. On channels with memory it can raise the capacity itself; the trapdoor channel goes from about \(0.58\) bits without feedback to \(\log_2 \varphi \approx 0.694\) with it [PCVRW08].
A FeedbackCode keeps a state that both ends know, updated from each
output alone. The channel is stepped one use at a time through
feedback_initial() and feedback_step(belief, x), which returns every
(y, p(y | x, belief), next_belief); MemorylessChannel provides
both, and a finite-state channel uses the normalized forward vector as its
belief. The exact block-error probability and \(\mathbb{E}[N]\) come from
walking the tree of outputs, merging branches that reach the same state, and
the rate is \(k / \mathbb{E}[N]\).
erasure_arq()resends each bit until it arrives unerased. On the binary erasure channel it is zero-error with \(\mathbb{E}[N] = k / (1 - \epsilon)\), so it achieves capacity exactly.stop_and_wait_arq()resends a block code’s codeword until the received word is a codeword; it fails only when the error pattern is itself a codeword.horstein()is Horstein’s bisection scheme [Hor63]: send whether the message lies above the receiver’s posterior median, stopping afternuses or once the posterior reaches1 - delta. Posterior matching [SF11] generalizes it, and the Schalkwijk–Kailath scheme [SK66] is its Gaussian-channel analogue (not implemented, sinceditchannels are discrete).
In [21]: from dit.coding import erasure_arq, horstein, binary_erasure_channel, binary_symmetric_channel
In [22]: erasure_arq(3).performance(binary_erasure_channel(0.3), method='exact')
Out[22]: (1.9231633866980787e-30, 4.285714285714281)
In [23]: 3 / 0.7
Out[23]: 4.285714285714286
In [24]: bsc = binary_symmetric_channel(0.1)
In [25]: [horstein(3, n=n).probability_of_error(bsc, method='exact') for n in (4, 8, 12)]
Out[25]: [0.2709999999999998, 0.07529265999999983, 0.017922541167996155]
APIs
- class SourceCoding(dist=None, radix=2)[source]
Abstract base class for source codes.
A source code maps the outcomes of a
Distributionto codewords over a radix-ary code alphabet. Concrete subclasses define how outcomes are encoded and decoded, and what the code’s rate is; this base class provides the comparisons to the source entropy that are common to all source codes.- Parameters:
dist (Distribution, None) – The source distribution the code is built for. Required for any of the rate-based properties.
radix (int) – The size of the code alphabet. Default is 2 (binary).
- abstractmethod encode(source)[source]
Encode a sequence of source outcomes into a sequence of code symbols.
- abstractmethod decode(encoded)[source]
Decode a sequence of code symbols back into source outcomes.
- abstractmethod rate()[source]
The expected number of code symbols emitted per source symbol.
- source_entropy()[source]
The entropy of the source, in units of radix-ary digits.
This is the fundamental lower bound on the rate of any uniquely decodable source code (the source coding theorem; Cover & Thomas, Ch. 5).
- Returns:
H – The source entropy,
H[X] / log2(radix).- Return type:
- redundancy()[source]
The rate in excess of the source entropy,
rate - source_entropy.- Returns:
r – The redundancy, in radix-ary digits per source symbol. Always non-negative for a uniquely decodable code.
- Return type:
- class ChannelCoding(channel=None, radix=2)[source]
Abstract base class for channel codes.
A channel code adds redundancy to a message so that it can be recovered after transmission across a noisy channel. This class is scaffolding for future channel codes (repetition, polar, LDPC, …); no concrete subclass is provided yet.
Concrete subclasses are expected to implement
encode()(message -> codeword),decode()(received word -> message), andrate()(message symbols per channel use), and may additionally expose channel-code properties such as the minimum distance, the block error probability for a given channel, and the gap to capacity (viadit.algorithms.channel_capacity()).- Parameters:
channel (Distribution, object, None) – The channel, as a conditional distribution
p(output | input)or any object implementing the channel protocol (seeas_channel()).radix (int) – The size of the code alphabet. Default is 2 (binary).
- abstractmethod encode(message)[source]
Encode a message into a channel codeword.
- abstractmethod decode(received, channel=None)[source]
Decode a received word back into a message.
Soft-decision decoders use channel (a conditional
p(Y|X)distribution, or any object implementing the channel protocol) to form log-likelihoods; hard-decision decoders ignore it.
- abstractmethod rate()[source]
The number of message symbols transmitted per channel use.
- capacity_gap(channel, **kwargs)[source]
The gap between the channel capacity and the code rate.
- Parameters:
channel (Distribution or object) – The channel: a conditional distribution
p(Y|X), or any object implementing the channel protocol (seeas_channel()) and providingcapacity().kwargs (dict) – Passed to the channel’s
capacity().
- Returns:
gap –
capacity(channel) - rate, in bits per channel use. Positive when the code operates below capacity. When the channel only brackets its capacity, a(lower, upper)bracket on the gap.- Return type:
- probability_of_error(channel, method='auto', samples=10000, prng=None)[source]
The block error probability of the code over a channel.
The code’s own
decode()is used (passing channel so soft-decision decoders engage). Withmethod='exact'every message and every received word is enumerated – feasible only for small codes – whilemethod='montecarlo'estimates the probability by sampling.- Parameters:
channel (Distribution or object) – The channel: a conditional distribution
p(Y|X), or any object implementing the channel protocol (seeas_channel()), such as a finite-state channel with memory.method (str) –
'exact','montecarlo', or'auto'(exact when small).samples (int) – The number of Monte Carlo samples.
prng (numpy.random.Generator, None) – The random number generator for Monte Carlo sampling.
- Returns:
pe – The probability that the decoded message differs from the sent one.
- Return type:
- class SymbolCode(codebook, dist=None, radix=2)[source]
A source code that maps each outcome to a single codeword.
- Parameters:
codebook (dict) – A mapping from source outcomes to codeword strings (e.g.
'010').dist (Distribution, None) – The source distribution. Required for the rate-based properties (
rate(),average_length(),redundancy(), …).radix (int) – The size of the code alphabet. Default is 2 (binary).
- encode(source)[source]
Encode a sequence of source outcomes into a string of code symbols.
- Parameters:
source (iterable) – A sequence of source outcomes.
- Returns:
encoded – The concatenated codewords.
- Return type:
- decode(encoded)[source]
Decode a string of code symbols back into source outcomes.
- average_length()[source]
The expected codeword length,
sum_x p(x) * len(codeword(x)).- Returns:
L
- Return type:
- rate()[source]
The expected number of code symbols per source symbol.
For a symbol code this is exactly the average codeword length.
- length_variance()[source]
The variance of the codeword length under the source distribution.
Among optimal codes (which share the minimal average length) one often prefers the one of least length variance.
- Returns:
var
- Return type:
- kraft_sum()[source]
The Kraft sum
sum_x radix ** -len(codeword(x)).By the Kraft-McMillan inequality this is
<= 1for any uniquely decodable code, with equality iff the code is complete.- Returns:
K
- Return type:
- is_complete()[source]
Whether the code is complete (its Kraft sum equals 1).
- Returns:
complete
- Return type:
- is_prefix_free()[source]
Whether no codeword is a prefix of another (the code is instantaneous).
- Returns:
prefix_free
- Return type:
- is_uniquely_decodable()[source]
Whether the code is uniquely decodable, via the Sardinas-Patterson test.
- Returns:
unique
- Return type:
- class TunstallCode(word_to_code, word_probs, code_length, dist=None, radix=2)[source]
A variable-to-fixed-length source code.
- Parameters:
word_to_code (dict) – A mapping from source words (tuples of outcomes) to fixed-length codeword strings.
word_probs (dict) – A mapping from source words to their probabilities (used for the rate).
code_length (int) – The fixed codeword length, in code symbols.
dist (Distribution, None) – The source distribution.
radix (int) – The size of the code alphabet. Default is 2.
- expected_word_length()[source]
The expected number of source symbols per dictionary word.
- Returns:
L
- Return type:
- rate()[source]
The expected number of code symbols per source symbol.
Each word maps to
code_lengthcode symbols and spansexpected_word_lengthsource symbols on average.
- encode(source)[source]
Encode a sequence of source outcomes by greedily parsing it into words.
- Parameters:
source (iterable) – A sequence of source outcomes whose symbols form complete words.
- Returns:
encoded
- Return type:
- class PolarSourceCode(dist, block_length, rv=0, crvs=None, rank_by='entropy', rate=None, size=None, tol=1e-09, max_states=65536)[source]
An exact finite-block polar source code for a small binary source.
The code applies the Arikan transform
U^N = X^N G_Nto a block ofNsource bits and stores only the coordinates in the high-entropy set (those that stay nearly uniform given the past and any side information). Decoding fills in the remaining low-entropy coordinates by sequential maximum a posteriori (MAP) decisions against the exactp(U^N, Y^N)table and then inverts the transform.Because the code enumerates the joint distribution exactly (no density evolution or list decoding), it is intended for small blocks: the number of enumerated states is
|copy support|^N, guarded bymax_states.- Parameters:
dist (Distribution) – A single-copy source
(X)or source-with-side-information(X, Y, ...)distribution.X(indexed byrv) must be binary.block_length (int) – The block length
N; must be a power of two.rv (int) – The index of the binary source variable. Default is 0.
crvs (list, None) – Indices of the side-information variables available at the decoder. If None, the code is a plain (side-information-free) polar source code.
rank_by (str) – How to rank coordinates for the high-entropy set:
"entropy"(default) or"bhattacharyya".rate (float, None) – A fixed target rate in
[0, 1](keepround(rate * N)coordinates). Mutually exclusive withsize.size (int, None) – The exact high-entropy set size. By default the set is chosen losslessly – every coordinate whose conditional entropy exceeds
tolis kept, so the code reconstructs each block exactly. Fixingsize(orrate) below the lossless size yields a fixed-rate code that may not be exact.tol (float) – The conditional-entropy threshold used by the lossless default.
max_states (int) – A guard on the number of enumerated joint outcomes.
- property message_length
The number of stored (high-entropy) coordinates per block.
- encode(x_block)[source]
Encode a block of
Nsource bits into the high-entropy coordinates.
- decode(encoded, side_information=None)[source]
Decode a block from its high-entropy coordinates (and any side info).
The high-entropy coordinates are filled from
encoded; the low-entropy coordinates are recovered one at a time by an exact MAP decision againstp(U_i | U^{i-1}, Y^N). The inverse Arikan transform then returns the source bits.- Parameters:
encoded (sequence of int) – The stored high-entropy bits, in ascending index order.
side_information (sequence, None) – The decoder side information
Y^N: one symbol per copy when the code was built withcrvs(flattened copy-major when multiple side variables). Required iff the code has side information.
- Returns:
x_block – The recovered length-
Nblock of source bits.- Return type:
- rate()[source]
The code rate,
|high_entropy_set| / block_length.
- polar_source(dist, block_length, rv=0, crvs=None, rank_by='entropy', rate=None, size=None, tol=1e-09, max_states=65536)[source]
Build an exact finite-block polar source code.
- Parameters:
dist (Distribution) – A single-copy binary source
(X)or(X, Y, ...)distribution.block_length (int) – The block length
N; must be a power of two.rv (int) – The index of the binary source variable. Default is 0.
crvs (list, None) – Indices of the side-information variables available at the decoder.
rank_by (str) –
"entropy"(default) or"bhattacharyya".rate (float, None) – A fixed target rate in
[0, 1]. Mutually exclusive withsize.size (int, None) – The high-entropy set size. Defaults to the lossless set (see
PolarSourceCode).tol (float) – The conditional-entropy threshold used by the lossless default.
max_states (int) – A guard on the number of enumerated joint outcomes.
- Returns:
code
- Return type:
- source_bhattacharyya(dist, rv=0, crvs=None)[source]
The source Bhattacharyya parameter of a binary variable.
For a binary
Xwith side informationY,\[Z(X \mid Y) = 2 \sum_y \sqrt{p(0, y)\,p(1, y)},\]which reduces to
2 sqrt(p(0) p(1))when there is no side information. It satisfies0 <= Z <= 1, withZnear1whenXis nearly uniform givenYand near0whenXis nearly determined byY.- Parameters:
dist (Distribution) – The source distribution.
rv (int) – The index of the binary source variable. Default is 0.
crvs (list, None) – Indices of the side-information variables. If None,
Z(X)is computed.
- Returns:
Z
- Return type:
- source_polarization_profile(dist, block_length, rv=0, crvs=None, metrics=('entropy', 'bhattacharyya'))[source]
The exact source-polarization profile of
N = block_lengthi.i.d. copies.Applies the Arikan transform
U^N = X^N G_Nand reports, for each synthesized coordinatei, the requested metrics conditioned on the pastU^{i-1}and the side informationY^N:"entropy"– the conditional entropyH(U_i | U^{i-1}, Y^N),"bhattacharyya"– the conditional source Bhattacharyya parameterZ(U_i | U^{i-1}, Y^N).
The conditional entropies sum to
N * H(X | Y)(entropy conservation), and asNgrows they polarize toward0or1.A third, optional Goela-style diagnostic reuses
dit.divergences.maximum_correlation():"max_correlation_with_past"– the maximal correlationrho_m(U_i ; U^{i-1})between each coordinate and its past (0.0ati = 0, and ignoring side information). Goela et al. (2014) show these also polarize.
- Parameters:
dist (Distribution) – The source distribution for a single copy
(X)or(X, Y, ...).block_length (int) – The number of i.i.d. copies
N; must be a power of two.rv (int) – The index of the binary source variable. Default is 0.
crvs (list, None) – Indices of the side-information variables. If None, no side information.
metrics (tuple of str) – Which metrics to compute per coordinate.
- Returns:
profile – One dict per coordinate
i(in[0, N)), keyed"index"plus the requested metric names.- Return type:
- source_high_entropy_set(dist, block_length, rate=None, size=None, rv=0, crvs=None, rank_by='entropy', tol=1e-09)[source]
The high-entropy indices selected by a polar source code.
Ranks the
Nsynthesized coordinates by their conditional metric (largest conditional entropy, or largest source Bhattacharyya, given the past and any side information) and returns the top indices – those a lossless polar source code must transmit. At most one ofrateorsizemay be given:size– keep exactly this many coordinates,rate– keepround(rate * N)coordinates,neither – keep every coordinate whose conditional entropy exceeds
tol, i.e. the lossless set (the dropped coordinates are deterministic given the past and side information, so the code reconstructs the block exactly).
- Parameters:
dist (Distribution) – The source distribution.
block_length (int) – The number of i.i.d. copies
N; must be a power of two.rate (float, None) – The target fraction of coordinates to keep, in
[0, 1]. The set size isround(rate * N).size (int, None) – The exact number of coordinates to keep.
rv (int) – The index of the binary source variable. Default is 0.
crvs (list, None) – Indices of the side-information variables.
rank_by (str) –
"entropy"(default) or"bhattacharyya".tol (float) – The conditional-entropy threshold for the lossless default; coordinates at or below
tolare treated as deterministic and dropped.
- Returns:
indices – The selected coordinate indices, sorted ascending.
- Return type:
- shannon(dist, radix=2)[source]
Build a Shannon code.
Codeword lengths are
ceil(log_radix(1/p))and codewords are read off the base-radix expansion of the cumulative distribution (outcomes sorted by decreasing probability).- Parameters:
dist (Distribution) – The source distribution.
radix (int) – The size of the code alphabet. Default is 2.
- Returns:
code
- Return type:
- fano(dist, radix=2)[source]
Build a Fano code (the Shannon-Fano top-down splitting method).
Outcomes are sorted by probability and recursively partitioned into two groups of as-equal-as-possible total probability; one bit distinguishes the groups at each level.
- Parameters:
dist (Distribution) – The source distribution.
radix (int) – The size of the code alphabet. Only
radix=2is currently supported.
- Returns:
code
- Return type:
- shannon_fano_elias(dist, radix=2)[source]
Build a Shannon-Fano-Elias code.
Codeword lengths are
ceil(log_radix(1/p)) + 1and codewords are read off the base-radix expansion of the midpoint cumulative distributionF_bar(x) = sum_{y<x} p(y) + p(x)/2. The result is prefix-free.- Parameters:
dist (Distribution) – The source distribution.
radix (int) – The size of the code alphabet. Default is 2.
- Returns:
code
- Return type:
- huffman(dist, radix=2)[source]
Build a Huffman code, the optimal symbol code for the source.
For
radix > 2the alphabet is padded with zero-probability dummy symbols so that every internal node has exactly radix children.- Parameters:
dist (Distribution) – The source distribution.
radix (int) – The size of the code alphabet. Default is 2.
- Returns:
code
- Return type:
- length_limited_huffman(dist, max_length, radix=2)[source]
Build an optimal prefix code whose codewords are at most max_length long.
Uses the package-merge algorithm (Larmore & Hirschberg, 1990).
- Parameters:
dist (Distribution) – The source distribution.
max_length (int) – The maximum allowed codeword length.
radix (int) – The size of the code alphabet. Only
radix=2is currently supported.
- Returns:
code
- Return type:
- golomb(dist, m=None, radix=2)[source]
Build a Golomb code over non-negative-integer outcomes.
Each codeword is the unary-coded quotient
n // mfollowed by the truncated-binary remaindern % m. Golomb codes are optimal prefix codes for geometrically distributed sources.- Parameters:
dist (Distribution) – The source distribution; its outcomes must be non-negative integers.
m (int, None) – The Golomb parameter. If None, the parameter optimal for the (assumed geometric) source is chosen from its mean.
radix (int) – The size of the code alphabet. Only
radix=2is currently supported.
- Returns:
code
- Return type:
- rice(dist, k=None)[source]
Build a Rice code, the Golomb code with parameter
m = 2 ** k.- Parameters:
dist (Distribution) – The source distribution; its outcomes must be non-negative integers.
k (int, None) – The Rice parameter. If None, it is chosen from the optimal Golomb parameter for the source.
- Returns:
code
- Return type:
- tunstall(dist, code_length, radix=2)[source]
Build a Tunstall code for a memoryless source.
- Parameters:
dist (Distribution) – The source distribution over single symbols (assumed i.i.d.).
code_length (int) – The fixed codeword length, in code symbols. The dictionary holds up to
radix ** code_lengthwords.radix (int) – The size of the code alphabet. Default is 2.
- Returns:
code
- Return type:
- universal_code(dist, kind='gamma')[source]
Build a
SymbolCodeover integer outcomes using a universal code.- Parameters:
dist (Distribution) – The source distribution; its outcomes must be positive integers.
kind (str) – One of
'unary','gamma','delta','omega', or'fibonacci'.
- Returns:
code
- Return type:
- unary(n)[source]
The unary codeword for
n >= 1:n - 1ones followed by a zero.
- elias_gamma(n)[source]
The Elias gamma codeword for
n >= 1.The codeword is
floor(log2(n))zeros followed by the binary representation ofn.
- elias_delta(n)[source]
The Elias delta codeword for
n >= 1.The codeword is the gamma code of the bit-length of
nfollowed by the bits ofnbelow its leading one.
- elias_omega(n)[source]
The Elias omega (recursive) codeword for
n >= 1.
- fibonacci(n)[source]
The Fibonacci codeword for
n >= 1(Zeckendorf representation + a1).
- as_channel(channel)[source]
Coerce channel to an object implementing the channel protocol.
- Parameters:
channel (Distribution or object) – A conditional distribution
p(Y | X); an object with a__dit_channel__()method; or an object already implementing the channel protocol described indit.coding._channel.- Returns:
channel – An object implementing the channel protocol.
- Return type:
- class MemorylessChannel(dist)[source]
A discrete memoryless channel, applying
p(Y | X)independently to each symbol.- Parameters:
dist (Distribution) – A conditional distribution
p(Y | X).
- log_likelihood(codeword, received)[source]
The log-probability
log p(received | codeword).
- sample(codeword, prng)[source]
Pass codeword through the channel once.
- symbol_log_likelihoods(received)[source]
The
(n, len(inputs))array oflog p(y_i | x).
- feedback_initial()[source]
The transmitter’s initial belief about the channel;
None, since there is no state.
- feedback_step(belief, x)[source]
The outcomes
(y, p(y | x), None)of one channel use with input x.
- capacity()[source]
The channel capacity, in bits per channel use.
- bhattacharyya()[source]
The Bhattacharyya parameter of a binary-input channel.
- class LinearCode(G, channel=None)[source]
A linear block code over GF(2).
- Parameters:
G (array-like) – A
k x nbinary generator matrix of full row rank.channel (Distribution, None) – A default channel, as a conditional distribution
p(Y|X).
- property length
The block length
n.
- property dimension
The number of information bits
k.
- property message_length
The number of message bits per codeword (
k).
- rate()[source]
The code rate
k / n.
- encode(message)[source]
Encode a length-
kmessage into a length-ncodeword.
- decode(received, channel=None)[source]
Decode a received word.
With no channel, hard-decision syndrome decoding is used. With a channel, maximum-likelihood decoding over the codebook is used.
- codewords()[source]
The list of all codewords.
- weight_enumerator()[source]
A
Countermapping each codeword weight to its multiplicity.
- minimum_distance()[source]
The minimum distance: the least weight among nonzero codewords.
- error_correcting_capability()[source]
The number of errors the code can correct,
floor((d - 1) / 2).
- property generator_matrix
The generator matrix
G.
- property parity_check_matrix
The parity-check matrix
H.
- class LDPCCode(H, channel=None, max_iterations=50)[source]
A linear code decoded by belief propagation on a sparse parity-check matrix.
- Parameters:
H (array-like) – An
m x nbinary parity-check matrix (typically sparse).channel (Distribution, None) – A default channel.
max_iterations (int) – The maximum number of belief-propagation iterations.
- decode(received, channel=None)[source]
Decode by belief propagation when a channel is given, else hard decoding.
- class PolarCode(n, k, channel)[source]
A polar code of length
n = 2^mwithkinformation bits.- Parameters:
n (int) – The block length; must be a power of two.
k (int) – The number of information bits.
channel (Distribution or object) – The channel used both to select the frozen set and to decode. It must provide a Bhattacharyya parameter; to decode over a channel that does not (e.g. one with memory), design on a memoryless channel and pass the other channel to
decode().
- property message_length
The number of message bits per codeword (
k).
- decode(received, channel=None)[source]
Decode by successive cancellation using channel LLRs.
- class ConvolutionalCode(generators, message_length, channel=None)[source]
A rate-
1/nconvolutional code.- Parameters:
generators (sequence of int) – The generator polynomials, in octal (e.g.
(0o7, 0o5)).message_length (int) – The number of information bits per encoded block.
channel (Distribution, None) – A default channel.
- rate()[source]
The code rate
1 / n_outputs.
- encode(message)[source]
Encode a message, appending
K - 1zeros to terminate the trellis.
- decode(received, channel=None)[source]
Decode by the Viterbi algorithm (soft when a channel is given).
- repetition(n, channel=None)[source]
The
[n, 1, n]repetition code.- Parameters:
n (int) – The block length.
channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- parity_check(k, channel=None)[source]
The
[k+1, k, 2]single-parity-check code.- Parameters:
k (int) – The number of information bits.
channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- hamming(r, channel=None)[source]
The
[2^r - 1, 2^r - 1 - r, 3]Hamming code.- Parameters:
r (int) – The number of parity bits (
r >= 2).channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- reed_muller(r, m, channel=None)[source]
The Reed-Muller code
RM(r, m).- Parameters:
r (int) – The order (
0 <= r <= m).m (int) – The number of variables; the block length is
2^m.channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- golay(extended=False, channel=None)[source]
The binary Golay code.
- Parameters:
extended (bool) – If True, return the extended
[24, 12, 8]code; otherwise the perfect[23, 12, 7]code.channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- ldpc(H, channel=None, max_iterations=50)[source]
Build an LDPC code from a parity-check matrix.
- Parameters:
H (array-like) – An
m x nbinary parity-check matrix.channel (Distribution, None) – A default channel.
max_iterations (int) – The maximum number of belief-propagation iterations.
- Returns:
code
- Return type:
- gallager(n, wc, wr, prng=None, channel=None)[source]
Build a regular Gallager LDPC code.
- Parameters:
n (int) – The block length; must be divisible by
wr.wc (int) – The column weight (variable degree).
wr (int) – The row weight (check degree);
wc < wr.prng (numpy.random.Generator, None) – The random number generator used to permute the sub-bands.
channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- polar(n, k, channel)[source]
Build a polar code.
- Parameters:
n (int) – The block length; must be a power of two.
k (int) – The number of information bits.
channel (Distribution) – The channel used to select the frozen set and to decode.
- Returns:
code
- Return type:
- convolutional(generators, message_length, channel=None)[source]
Build a rate-
1/nconvolutional code.- Parameters:
generators (sequence of int) – The generator polynomials in octal (e.g.
(0o7, 0o5)).message_length (int) – The number of information bits per encoded block.
channel (Distribution, None) – A default channel.
- Returns:
code
- Return type:
- class FeedbackCode(k)[source]
Abstract base class for channel codes with noiseless feedback.
Subclasses implement
start(),next_input(),update()anddecode(). States must be treated as immutable:update()returns a new state, so thatprobability_of_error()can branch on every channel output.- Parameters:
k (int) – The number of message bits.
- abstractmethod start(channel)[source]
The shared state before the first channel use.
- abstractmethod next_input(state, message)[source]
The next channel input for message, or
Noneto stop transmitting.
- abstractmethod update(state, y)[source]
The shared state after the channel output y is received (and fed back).
- abstractmethod decode(state)[source]
The receiver’s estimate of the message, as a tuple of bits.
- transmit(message, channel, prng=None, max_uses=64)[source]
Send message across channel once.
- Parameters:
message (sequence of int) – The
kmessage bits.channel (Distribution or object) – The channel, implementing
feedback_initialandfeedback_step.prng (numpy.random.Generator, None) – The random number generator.
max_uses (int) – Stop (and decode) after this many channel uses regardless.
- Returns:
decoded (tuple) – The receiver’s estimate of the message.
uses (int) – The number of channel uses.
- performance(channel, method='auto', samples=10000, prng=None, max_uses=64)[source]
The block error probability and the expected number of channel uses.
- Parameters:
channel (Distribution or object) – The channel, implementing
feedback_initialandfeedback_step.method (str) –
'exact'enumerates every message and every output sequence;'montecarlo'samples;'auto'is exact unless the output tree exceeds about a million branches.samples (int) – The number of Monte Carlo samples.
prng (numpy.random.Generator, None) – The random number generator for Monte Carlo sampling.
max_uses (int) – Transmission is cut off (and decoded) after this many channel uses, so the exact values are those of the truncated code.
- Returns:
pe (float) – The probability that the decoded message differs from the sent one.
uses (float) – The expected number of channel uses.
- probability_of_error(channel, **kwargs)[source]
The block error probability over channel; keyword arguments as in
performance().
- expected_uses(channel, **kwargs)[source]
The expected number of channel uses; keyword arguments as in
performance().
- rate(channel, **kwargs)[source]
The rate
k / E[N]in bits per channel use, whereNis the number of uses.The rate of a variable-length code depends on the channel, so unlike
ChannelCoding.rateit takes one. Keyword arguments are as inperformance().
- class ErasureARQ(k, erasure=2)[source]
Send each bit until it arrives unerased.
On the binary erasure channel this is zero-error and uses
k / (1 - epsilon)channel uses on average, so it achieves the capacity1 - epsilonexactly [CT06]. Any output other than the erasure symbol is taken at face value, so on a channel that also flips bits it makes errors.
- class StopAndWaitARQ(code, max_rounds=None)[source]
Stop-and-wait automatic repeat request over a linear block code.
The codeword is sent; if the received word is not a codeword (a nonzero syndrome, or any non-binary output such as an erasure) the receiver asks for it again. An error goes undetected only when the error pattern is itself a nonzero codeword. After max_rounds failed attempts the last received word is syndrome-decoded.
- Parameters:
code (LinearCode) – The block code used for error detection.
max_rounds (int, None) – The maximum number of transmissions of the block; unlimited by default.
- class HorsteinCode(k, n=None, delta=None)[source]
Horstein’s bisection scheme over the
2^kmessages.Both ends keep the receiver’s posterior over the messages, in a fixed order. At each use the transmitter sends
1if its message lies above the posterior median and0otherwise, so every output roughly halves the receiver’s uncertainty [Hor63]. Posterior matching [SF11] generalizes the idea; Schalkwijk and Kailath’s scheme is its Gaussian-channel counterpart [SK66].The posterior is updated through the channel’s
feedback_step, as a joint belief over the message and the channel’s hidden state, so the same code runs on finite-state channels. Inputs must be the bits{0, 1}.
- erasure_arq(k, erasure=2)[source]
An
ErasureARQcode for k bits: resend each bit until it is not erased.
- stop_and_wait_arq(code, max_rounds=None)[source]
A
StopAndWaitARQcode: resend code’s block until it is received as a codeword.
- horstein(k, n=None, delta=None)[source]
A
HorsteinCodefor k bits, stopping after n uses or at posterior1 - delta.