Runtime Validation

When Static Types Are Not Enough

Static type checkers verify that types are consistent, but they cannot check that a matrix has the right shape, that eigenvalues are positive, or that a probability sums to one. These are value constraints that require runtime checks. This section covers the spectrum from simple assert statements to structured validation with beartype.

Definition:

Assertion

An assertion is a runtime check that a condition holds:

assert H.shape[0] == y.shape[0], (
    f"Dimension mismatch: H has {H.shape[0]} rows, "
    f"y has {y.shape[0]} elements"
)

If the condition is False, Python raises AssertionError with the given message. Assertions are disabled when Python runs with the -O (optimize) flag, so they must never be used for input validation in production code β€” only for internal consistency checks.

Definition:

Exception Hierarchy for Scientific Code

Python's built-in exceptions map to common scientific programming errors:

Exception Use case
ValueError Wrong value: negative variance, invalid SNR range
TypeError Wrong type: passing a list where ndarray expected
RuntimeError Algorithm failure: convergence not reached
OverflowError Numerical overflow in computation
IndexError Out-of-bounds antenna or subcarrier index

Raise specific exceptions with descriptive messages:

if n_antennas <= 0:
    raise ValueError(f"n_antennas must be positive, got {n_antennas}")

Definition:

beartype β€” O(1) Runtime Type Checking

beartype is a Python library that enforces type hints at runtime with near-zero overhead. Unlike isinstance checks, beartype understands generic types, unions, and nested containers:

from beartype import beartype

@beartype
def compute_ber(
    tx_bits: np.ndarray,
    rx_bits: np.ndarray,
) -> float:
    return float(np.mean(tx_bits != rx_bits))

If you pass a list instead of np.ndarray, beartype raises a BeartypeCallHintParamViolation immediately β€” no need to wait for a cryptic error three functions deep.

Historical Note: The assert Statement's Dual Identity

1998

The assert statement was added to Python 1.5 (1998) inspired by C's assert.h macro. Like its C ancestor, Python's assert was designed for debugging aids that can be stripped from production builds. The -O flag (optimize) sets __debug__ to False and removes all assert statements from the bytecode. This dual identity β€” present in development, absent in production β€” has been a source of bugs when developers use assertions for input validation.

Theorem: Assertion vs. Exception: Decision Rule

Use assert for conditions that should never be false if the code is correct (internal invariants). Use exceptions (raise ValueError) for conditions that can legitimately be false due to user input or external data. Formally:

  • assert: The violation indicates a bug in the program.
  • raise: The violation indicates invalid input or environment.

An assertion is a note to yourself: "if this fails, I have a bug." An exception is a note to the user: "the input you gave me is wrong." The key test: would the check still be needed if the code were perfect? If yes, use an exception. If no, use an assertion.

Example: Shape Validation for Matrix Operations

Write a function that validates matrix dimensions before performing a MIMO detection, using assertions for internal invariants and exceptions for user-facing checks.

Example: beartype for Scientific Function Interfaces

Decorate a simulation pipeline with beartype to catch type errors at function boundaries rather than deep inside numerical code.

Example: Custom Validators for Array Shapes

Write a decorator that validates array shapes at runtime, using a compact syntax to specify expected dimensions.

Numerical Tolerance Explorer

Explore how different tolerance levels (atol, rtol) affect pass/fail outcomes when comparing floating-point arrays.

Parameters

Common Mistake: Using assert for Input Validation

Mistake:

Using assertions to validate user input or file data:

assert len(data) > 0, "Empty dataset"
assert snr_db >= 0, "SNR must be non-negative"

These checks disappear when running with python -O, silently accepting invalid inputs.

Correction:

Use explicit exceptions for external input:

if len(data) == 0:
    raise ValueError("Empty dataset")
if snr_db < 0:
    raise ValueError(f"SNR must be non-negative, got {snr_db}")

Runtime Validation Methods Compared

MethodOverheadDisabled by -O?Best for
assertZero (removed by compiler)YesInternal invariants, debugging
raise ValueErrorNegligibleNoUser input validation
beartypeO(1) per callNoType contract enforcement at boundaries
isinstance checksNegligibleNoSimple type guards
Pydantic modelsModerate (validation + coercion)NoComplex config/parameter validation

Quick Check

What happens to assert statements when Python runs with the -O flag?

They still execute but do not raise errors

They are completely removed from the bytecode

They are converted to warnings

They are moved to a log file

assertion

A debugging statement (assert condition, message) that raises AssertionError if the condition is False. Removed by python -O.

Related: beartype

beartype

A Python library providing O(1) runtime type checking by validating type hints at function call boundaries via a decorator.

Related: assertion

Runtime Validation Patterns

python
Shape validation, assertion vs. exception patterns, beartype usage, and custom validator decorators for scientific code.
# Code from: ch04/python/runtime_validation.py
# Load from backend supplements endpoint