Debugging Functions
If your PMF or PDF is not behaving as expected, this page provides a systematic process for finding and fixing the problem.
Step 1 — Read the Auto-Correction Notice
After entering a function, ToFUL shows the interpreted form before computing. Check this carefully:
Is the expression ToFUL interpreted the same as what you intended?
If corrections were applied (shown in the collapsible notice), do they match your intention?
You typed: 0.3 * 0.7^x if x >= 0 else 0
Interpreted: 0.3 · 0.7^x if x ≥ 0 else 0 ← display form
The display form uses Unicode exponents and operators for readability.
The actual eval form used internally is 0.3 * 0.7**x if x >= 0 else 0.
Step 2 — Check the Validation Badge
After clicking Compute, the validation result appears immediately below the corrections notice.
Green “valid distribution” badge — PMF/PDF integrates/sums to 1. The issue (if any) is in the moment computation, not the function definition.
Red message — The PMF/PDF failed validation. The message shows the computed sum/integral and the deviation from 1.
If the sum is far from 1 (e.g. 0.3 or 45.2), the normalising constant or the formula itself is wrong.
If the sum is close but not equal (e.g. 0.9999 or 1.0001), it may be a precision issue with an infinite series — see Convergence Issues.
Step 3 — Test a Single Value Mentally
Pick a simple support value and compute the PMF/PDF by hand (or with a calculator) to verify the formula is correct at that point.
Example — Geometric(p=0.3) at x=0:
0.3 * (0.7 ** 0) = 0.3 * 1 = 0.3 (correct)
Example — Poisson(λ=2) at x=1:
(exp(-2) * 2**1) / factorial(1) = 2 * exp(-2) ~ 0.2707 (correct)
Step 4 — Verify the Guard Condition
The if ... else 0 guard must cover the entire intended support and
return 0 everywhere else.
# Bug — guard uses >, missing x=0
0.3 * (0.7 ** x) if x > 0 else 0
# Fix — use >=
0.3 * (0.7 ** x) if x >= 0 else 0
For continuous distributions, verify the guard matches your bounds:
# Bug — guard uses x<=2 but upper bound is set to 3
x if 0 <= x <= 2 else 0 # with upper bound 3
# Fix — match the guard to the upper bound
x if 0 <= x <= 3 else 0 # or adjust the upper bound to 2
Step 5 — Check for Python Keyword Conflicts
Several common mathematical names are Python reserved words or built-in names that conflict with the expression evaluator:
Problematic |
Use instead |
|---|---|
|
|
|
Use comparison operators |
|
Use |
|
Use |
|
Use |
|
Use |
The name e is safe — it is pre-bound to Euler’s number.
Step 6 — Isolate Multi-Part Expressions
For complex piecewise functions, test each piece separately first.
Example — Triangular distribution:
# Full expression
x if 0<=x<=1 else (2-x) if 1<x<=2 else 0
# Test piece 1: does x work on [0,1]?
# Validate with range 0,0.25,0.5,0.75,1 and PMF: x
# Expected integral on [0,1] = 0.5
# Test piece 2: does (2-x) work on [1,2]?
# Validate with range [1,2] and PDF: 2-x
# Expected integral on [1,2] = 0.5
Once each piece validates individually, combine them with the full piecewise expression and bounds [0,2].
Step 7 — Check for Operator Precedence Issues
Python’s operator precedence can produce surprising results without explicit brackets:
# Bug — parsed as (1/2) * x**2 = 0.5*x**2
# because ** binds tighter than /
1/2*x**2
# Fix — use explicit brackets
(1/2) * x**2
# or equivalently
0.5 * x**2
# Bug — ternary chains sometimes need brackets
a if cond1 else b if cond2 else c
# is parsed as a if cond1 else (b if cond2 else c) — usually correct
# but for complex conditions, use explicit brackets to be safe
a if cond1 else (b if cond2 else c)
Step 8 — Verify with a Known Distribution
If you are implementing a well-known distribution, compare your first moment against the analytical mean formula. If they agree, the PMF/PDF is almost certainly correct.
Distribution |
Analytical mean |
|---|---|
Binomial(n, p) |
n·p |
Geometric(p) |
(1-p)/p |
Poisson(λ) |
λ |
Exponential(λ) |
1/λ |
Normal(μ, σ) |
μ |
Uniform(a, b) |
(a+b)/2 |
Gamma(α, β) |
α/β |
Beta(α, β) |
α/(α+β) |
Diagnostic Checklist
Work through this list in order when a function produces unexpected results:
□ Auto-correction notice shows the expression I intended
□ Validation badge is green (sum/integral = 1.0)
□ Manual check at one support point gives the right value
□ Guard condition covers the full intended support
□ No Python keyword conflicts (lambda, in, etc.)
□ Brackets are balanced and precedence is explicit
□ First moment matches the known analytical mean
If all boxes are checked and results still seem wrong, check the Convergence Issues page — the issue may be numerical rather than in the function definition itself.
See also
Common Errors — error message reference
Convergence Issues — when moments do not converge
Input Syntax Guide — full syntax documentation