Exercises

ex-sp-ch04-01

Easy

Add type annotations to the following function. Include parameter types, return type, and a TypeAlias for the channel matrix:

def make_channel(n_rx, n_tx, seed=None):
    rng = np.random.default_rng(seed)
    return (rng.standard_normal((n_rx, n_tx))
            + 1j * rng.standard_normal((n_rx, n_tx))) / np.sqrt(2)

ex-sp-ch04-02

Easy

Write a function validate_mimo_inputs that takes a channel matrix H and received vector y, checks their shapes, and raises appropriate exceptions (not assertions) with descriptive error messages.

ex-sp-ch04-03

Easy

Write a pytest test function that verifies np.fft.ifft(np.fft.fft(x)) recovers the original signal x for a random input vector. Use np.testing.assert_allclose with an appropriate tolerance.

ex-sp-ch04-04

Easy

Write a parametrized pytest test that checks np.linalg.det(A @ B) == det(A) * det(B) for square matrices of sizes 2, 3, 5, and 10.

ex-sp-ch04-05

Easy

Create a pyproject.toml file for a package named signal-tools with version 0.1.0, Python >= 3.11, dependencies on NumPy and SciPy, and a dev dependency group containing pytest and mypy.

ex-sp-ch04-06

Medium

Write a generic function apply_to_columns that applies a callable to each column of a 2-D NumPy array and returns the stacked results. Use TypeVar and Callable for proper typing. Write a test that uses it with both np.mean and np.fft.fft.

ex-sp-ch04-07

Medium

Use beartype to decorate a function that computes the MMSE equalizer: W=(HHH+σ2I)1HH\mathbf{W} = (\mathbf{H}^H \mathbf{H} + \sigma^2 \mathbf{I})^{-1} \mathbf{H}^H. The function should accept n_rx >= n_tx and noise_var > 0. Write tests that verify beartype catches invalid inputs.

ex-sp-ch04-08

Medium

Write a pytest fixture that provides a "simulation environment" with a channel matrix, noise, and transmitted signal. Parametrize the fixture over (n_rx, n_tx) pairs [(4,2), (8,4), (16,8)]. Use this fixture to test that ZF detection recovers the transmitted signal at high SNR.

ex-sp-ch04-09

Medium

Profile the following two implementations of a correlation matrix computation and report which is faster and why:

def corr_loop(X):
    n = X.shape[1]
    C = np.zeros((n, n))
    for i in range(n):
        for j in range(n):
            C[i, j] = np.mean(X[:, i] * X[:, j])
    return C

def corr_vectorized(X):
    return (X.T @ X) / X.shape[0]

ex-sp-ch04-10

Medium

Use hypothesis to test that for any square matrix AA, the trace equals the sum of eigenvalues: tr(A)=iλi\text{tr}(A) = \sum_i \lambda_i. Handle the fact that eigenvalues may be complex.

ex-sp-ch04-11

Medium

Create a complete project structure with src layout for a package called ofdm-tools. Include:

  • pyproject.toml with dependencies
  • src/ofdm_tools/__init__.py with a public API
  • src/ofdm_tools/modulation.py with a qam_modulate function
  • tests/test_modulation.py with at least 3 tests
  • A CLI entry point that modulates a random bit sequence

ex-sp-ch04-12

Hard

Implement a @validate_shapes decorator that checks array argument shapes against a specification string at runtime. Support:

  • Named dimensions: "(N, M)" — the same letter must match
  • Wildcard: "(*,)" — any shape
  • Fixed: "(3, 3)" — exact shape

Write comprehensive tests including edge cases.

ex-sp-ch04-13

Hard

Write a complete test suite for a KalmanFilter class with at least 10 test functions covering:

  • Constructor validation (dimensions, positive definite covariance)
  • Predict step (state and covariance update)
  • Update step (Kalman gain, innovation)
  • Full filter on a synthetic 1-D tracking problem
  • Edge cases (zero noise, identity matrices)

Use fixtures, parametrize, and assert_allclose.

ex-sp-ch04-14

Hard

Profile a naïve OFDM transmitter that uses Python loops and optimize it to be at least 50x faster using vectorized NumPy operations. Measure and report the speedup using cProfile or time.perf_counter.

The transmitter pipeline: QAM modulation -> serial-to-parallel -> IFFT -> add cyclic prefix -> parallel-to-serial.

ex-sp-ch04-15

Hard

Create a Protocol called Estimator that specifies fit(X, y) and predict(X) methods with full type annotations. Implement two classes satisfying this Protocol: LeastSquares and RidgeRegression. Write property-based tests with hypothesis verifying:

  1. Fitting on noiseless data recovers the true coefficients
  2. Ridge regularization shrinks coefficients toward zero
  3. Both estimators produce predictions with the correct shape

ex-sp-ch04-16

Hard

Build a complete Python package spectral-analyzer with:

  • src layout with two modules: transforms.py and windows.py
  • CLI entry point that reads a WAV file and plots the spectrogram
  • Type annotations on all public functions
  • A test suite with at least 8 tests including fixtures and parametrize
  • pyproject.toml with dependencies and tool configuration for mypy and pytest

ex-sp-ch04-17

Challenge

Implement a shape-aware type system for NumPy arrays using Python's __class_getitem__ protocol. Create a ShapedArray[N, M] type that can be used in annotations and checked at runtime:

def matmul(A: ShapedArray[N, K], B: ShapedArray[K, M]) -> ShapedArray[N, M]:
    ...

The type checker should verify dimension consistency across function arguments. Write a comprehensive test suite.

ex-sp-ch04-18

Challenge

Create a mutation testing framework for numerical code. The framework should:

  1. Parse Python source files and identify numerical operations
  2. Generate mutants (e.g., replace + with -, * with /, swap < and >)
  3. Run the test suite against each mutant
  4. Report the mutation score (fraction of mutants killed by tests)

Apply it to a linear algebra module and analyze which mutations survive (indicating weak tests).