Generators#

class SymbolGenerator(M: int, *, distribution: ndarray | None = None, seed: int | None = None, name: str = 'generator')[source]#

Generator of independent and identically distributed (IID) integer symbols.

Signal Model#

Each output symbol \(y[n]\) is drawn independently from the integer alphabet \(\{0, 1, \ldots, M-1\}\), uniformly by default:

\[\Pr\left(y[n] = m\right) = \frac{1}{M}, \qquad m \in \{0, 1, \ldots, M-1\}\]

A source is not always uniform, though: probabilistic shaping sends inner constellation points more often than outer ones, and distribution= gives that source directly,

\[\Pr\left(y[n] = m\right) = p_m\]

which is what maxwell_boltzmann() returns. Note what this block is not: an i.i.d. draw from \(P_X\) is the idealization a distribution matcher approaches, not what it produces – a matcher maps uniform bits onto a finite set of sequences, so its output is neither independent nor exactly \(P_X\) on a finite block (see DistributionMatcher). Use this block to study what a distribution is worth, and the matcher to produce it.

This is a source block: the call argument is not an input signal \(x\) but the requested output size.

Axes: element-wise – each symbol is drawn independently; the output shape is the requested size (int or tuple).

param M:

Alphabet size \(M\); symbols are drawn from \(\{0, 1, \ldots, M-1\}\).

type M:

int

param distribution:

Probabilities \(p_m\), of length \(M\). Default (None) is the uniform law.

type distribution:

np.ndarray, optional, keyword-only

param seed:

Local RNG seed.

type seed:

int, optional, keyword-only

param name:

Name of the generator instance. Default is "generator".

type name:

str, optional, keyword-only

raises ValueError:

If distribution does not have one non-negative probability per symbol, summing to one.

References

J. G. Proakis, M. Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, Chapter 1; G. Böcherer, F. Steiner, P. Schulte, IEEE Trans. Commun., vol. 63, no. 12, 2015 (non-uniform signaling).

Examples

>>> generator = SymbolGenerator(M=4, seed=42)
>>> symbols_1D = generator(5)  # Generates a 1D array of size 5
>>> print(symbols_1D)
[0 3 2 1 1]
>>> symbols_2D = generator((2, 3))  # Generates a 2D array of shape (2, 3)
>>> print(symbols_2D)
[[3 0 2]
 [0 0 2]]

A shaped source, drawn from a Maxwell-Boltzmann law:

>>> from comnumpy.core.shaping import maxwell_boltzmann
>>> pam = np.arange(-3, 4, 2).astype(float)
>>> shaped = SymbolGenerator(4, distribution=maxwell_boltzmann(
...     pam, entropy=1.5), seed=0)
>>> print(np.bincount(shaped(10000)) / 10000)
[0.0557 0.4433 0.4457 0.0553]
class GaussianGenerator(sigma2: float = 1, *, seed: int | None = None, name: str = 'gaussian_generator')[source]#

Generator of IID circularly-symmetric complex Gaussian samples.

Signal Model#

Each output sample \(y[n]\) is drawn independently from a zero-mean circular complex Gaussian distribution of variance \(\sigma^2\):

\[y[n] \sim \mathcal{CN}\left(0, \sigma^2\right)\]

This is a source block: the call argument is not an input signal \(x\) but the requested output size.

Axes: element-wise – each sample is drawn independently; the output shape is the requested size (int or tuple).

param sigma2:

Variance \(\sigma^2\) of the complex Gaussian distribution. Default is 1.

type sigma2:

float

param seed:

Local RNG seed.

type seed:

int, optional, keyword-only

param name:

Name of the generator instance. Default is "gaussian_generator".

type name:

str, optional, keyword-only

References

J. G. Proakis, M. Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, Chapter 2.

Examples

>>> generator = GaussianGenerator(sigma2=1.0, seed=42)
>>> symbols_1D = generator(5)  # Generates a 1D array of 5 complex symbols
>>> print(np.round(symbols_1D, 3))
[ 0.215-0.921j -0.735+0.09j   0.531-0.224j  0.665-0.012j -1.38 -0.603j]
>>> symbols_2D = generator((3, 3))  # Generates a 2D array of shape (3, 3)
>>> symbols_2D.shape
(3, 3)