Exercises

ex-sp-ch01-01

Easy

Create a conda environment named test-env with Python 3.11, NumPy, and SciPy. Export it to environment.yml, delete the environment, and recreate it from the export file. Verify that import numpy works in the recreated environment.

ex-sp-ch01-02

Easy

Implement a __repr__ and __str__ for a SimResult dataclass that stores method (str), snr_db (float), and ber (float). __repr__ should be unambiguous; __str__ should be human-friendly.

ex-sp-ch01-03

Easy

Using collections.Counter, write a function that takes a string of DNA bases (e.g., "ATCGATCGAATTCCGG") and returns a dictionary of base frequencies as percentages.

ex-sp-ch01-04

Easy

Write a generator function snr_range(start_db, stop_db, step_db) that yields SNR values in dB from start_db to stop_db (inclusive if exactly reached) with step step_db. Do not use range() or numpy.

ex-sp-ch01-05

Easy

Using a dict comprehension, convert a list of SNR values in dB to a dictionary mapping each dB value to its linear equivalent: snr_linear = 10^(snr_db / 10).

ex-sp-ch01-06

Easy

Write a function format_results_table(results) that takes a list of dicts with keys method, ber, and runtime_s, and prints a nicely formatted table with aligned columns. BER should be in scientific notation with 2 decimal places; runtime in fixed-point with 3 decimal places.

ex-sp-ch01-07

Medium

Implement a Matrix class that supports:

  • __init__(self, rows: list[list[float]]) β€” store a 2D matrix
  • __repr__ β€” unambiguous representation
  • __getitem__(self, key) β€” support both M[i] (row) and M[i, j] (element)
  • __matmul__ β€” matrix multiplication via @
  • shape property returning (rows, cols)

ex-sp-ch01-08

Medium

Write a generator sliding_window(iterable, size) that yields tuples of size consecutive elements from iterable. For example, sliding_window([1,2,3,4,5], 3) yields (1,2,3), (2,3,4), (3,4,5). Use collections.deque internally.

ex-sp-ch01-09

Medium

Use itertools.product to generate all 2x2 matrices whose entries are from {0,1}\{0, 1\}. Count how many of them are invertible (determinant β‰ 0\neq 0). Print each invertible matrix.

ex-sp-ch01-10

Medium

Write a script that reads a CSV file with columns timestamp, snr_db, ber, groups the data by SNR value using collections.defaultdict, and prints the average BER for each SNR level. Use f-strings with proper formatting.

ex-sp-ch01-11

Medium

Implement a @dataclass called AntennaArray with fields n_elements (int), spacing_wavelengths (float, default 0.5), and geometry (str, default "ula"). Add a method steering_vector(theta) that returns the ULA steering vector a(ΞΈ)=[1,ej2Ο€dsin⁑θ,…,ej2Ο€(Nβˆ’1)dsin⁑θ]T\mathbf{a}(\theta) = [1, e^{j2\pi d \sin\theta}, \ldots, e^{j2\pi (N-1)d \sin\theta}]^T as a list of complex numbers, where dd is the spacing in wavelengths.

ex-sp-ch01-12

Medium

Write a functools.lru_cache-decorated function factorial(n) that computes n!n! recursively. Then write a function binomial(n, k) that uses factorial to compute (nk)=n!k!(nβˆ’k)!\binom{n}{k} = \frac{n!}{k!(n-k)!}. Print factorial.cache_info() to show cache hits.

ex-sp-ch01-13

Medium

Write a YAML configuration file for a simulation with nested parameters (channel model, SNR sweep, antenna config). Then write Python code to load it, run a mock simulation for each parameter combination (using itertools.product), and save results to a JSON file with metadata including the timestamp and Python version.

ex-sp-ch01-14

Hard

Implement a SortedList class that maintains elements in sorted order. It should support:

  • add(value) β€” insert maintaining sort order (use bisect)
  • __contains__(value) β€” O(log⁑n)O(\log n) membership test
  • __getitem__(index) β€” indexing
  • __len__() β€” length
  • __iter__() β€” iteration
  • __repr__() β€” representation

Demonstrate that __contains__ is O(log⁑n)O(\log n) while the equivalent list search is O(n)O(n).

ex-sp-ch01-15

Hard

Write a generator infinite_primes() that yields prime numbers indefinitely using the Sieve of Eratosthenes adapted for unbounded generation. Use it to find the 10,000th prime number.

ex-sp-ch01-16

Hard

Implement __eq__, __lt__, and __hash__ for a ComplexNumber class that stores real and imaginary parts. Use functools.total_ordering to get all comparison operators from just __eq__ and __lt__. Ordering should be by magnitude. Demonstrate that instances can be used as dict keys and in sets.

ex-sp-ch01-17

Hard

Write a Pipeline class that chains generator functions together. It should support a >> operator (using __rshift__) to compose stages. Each stage is a function that takes an iterable and yields transformed items.

Example usage:

pipeline = source >> filter_stage >> transform_stage >> sink_stage
pipeline.run()

ex-sp-ch01-18

Hard

Implement a timed_generator(gen_func) wrapper that measures and prints the total time to exhaust a generator, the number of items yielded, and the average time per item. It should work as a decorator. Test it on a generator that computes prime factors.

ex-sp-ch01-19

Challenge

Implement a LazyMatrix class that represents a matrix without storing it. Instead, it stores a function f(i, j) that computes element (i,j)(i, j) on demand. Support @ (matrix multiply by computing elements of the result lazily), __getitem__, and a to_list() method that materializes the full matrix.

Use this to represent a 1000Γ—10001000 \times 1000 DFT matrix Fkn=eβˆ’j2Ο€kn/NF_{kn} = e^{-j2\pi kn/N} without storing 10610^6 complex numbers, and compute a single row of FFH\mathbf{F} \mathbf{F}^H.

ex-sp-ch01-20

Challenge

Build a ConfigManager class that:

  1. Reads a YAML configuration file
  2. Allows dot-notation access (config.simulation.snr_db)
  3. Supports nested defaults via __getattr__
  4. Can be frozen (made immutable) after loading
  5. Provides a to_dict() method for serialization
  6. Is iterable (yields key-value pairs)

This pattern is used extensively in research code (similar to OmegaConf / Hydra configurations).