Function Notation, Domain and Range
The notation f(x) names outputs, the domain lists allowed inputs, and the range lists possible outputs. Shape errors, NaNs and silent clippings are all domain violations.
Why Does This Exist?
Every function has fine print: which inputs it accepts and which outputs it can produce. Feed a square root a negative, divide by zero, or log a zero, and the math has no answer, so the code invents one, usually NaN. Half of all debugging sessions are domain violations wearing costumes: the embedding lookup past the vocabulary edge, the probability 1.0 inside a log, the division by an empty count.
Range matters just as much. A sigmoid can never output exactly 0 or 1, so a loss waiting for those values waits forever. Stating domain and range up front turns runtime surprises into design facts. Distributions with their supports build on this in probability distributions; the mapping idea itself is in functions and graphs.
The one idea you need first. The notation reads "f of x": the output of function at input . The domain is the allowed-input list, the range the possible-output list. Three words, and they prevent most invalid-input bugs.
Think of It Like This
A parking garage with a height bar
A garage hangs a bar at 2 metres: taller vans cannot enter, full stop. Inside, every bay is on floors 1 to 5: no bay exists elsewhere. The bar is the domain, the floors are the range.
Code guards are height bars: if x <= 0: reject before the log, vocabulary size before the embedding lookup. And just as a driver who ignores the bar loses a roof rack, a pipeline that ignores the domain loses a night to NaNs.
Where it stops: garages turn away vans politely, while math has no waiting room. An input outside the domain does not queue; the function simply has no value there.
How It Actually Works
Read the formula for forbidden operations. Division forbids zero denominators: has domain . Even roots forbid negatives: needs . Logs forbid non-positives: needs . Polynomials and exponentials accept everything: domain all reals.
Find the range by asking what outputs are reachable. never goes negative: range . Sigmoid squeezes to , endpoints excluded. Interval notation writes it compactly: includes 0, excludes both ends.
Worked example: one over x minus 2
Function: . The denominator vanishes at , so the domain is all reals except 2.
Evaluate around the hole. . . . . The outputs skip nothing except itself: no input gives , since a fraction is zero only with a zero numerator. Range: all nonzero reals. The diagram above maps two of these arrows and leaves with none.
Code
def f(x: float) -> float: # 1/(x - 2); the domain bar hangs here, not inside the division. if x == 2.0: return float("nan") return 1.0 / (x - 2.0)
print(f(0.0)) # -> -0.5print(f(3.0)) # -> 1.0print(f(2.0)) # -> nanThe guard names the domain before computing, turning a crash (ZeroDivisionError) into an explicit, searchable NaN at the exact forbidden input. Production code logs or rejects here; the shape of the guard is what matters.
Watch Out For
Square roots of negatives deep in a pipeline
Standardising features with a variance term that rounds slightly negative hands sqrt a value like . NumPy returns NaN with a warning nobody reads, and the NaN rides every downstream computation to the loss. The symptom is a fully-NaN epoch with healthy-looking inputs.
Clip before rooting: math.sqrt(max(v, 0.0)). Assert non-negativity where the value is produced, not where it is consumed.
Waiting for outputs the range cannot produce
Cross-entropy with a sigmoid output can approach 0 loss but never reach it, because the sigmoid never outputs exactly 0 or 1. A convergence check demanding loss exactly waits forever, and a test asserting exact probabilities fails on correct code.
Set thresholds inside the range: stop below , not at zero. Read the range off the activation before writing the assertion.
The Quick Version
- reads "f of x"; domain lists allowed inputs, range lists reachable outputs.
- Division, even roots and logs restrict domains; polynomials take everything.
- excludes and never outputs ; test around the hole.
- Guard domains in code before computing, with explicit NaNs or rejections.
- Never assert outputs outside the range, like exact 0 from a sigmoid.