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

lambda

lam

in

Use comparison operators

and, or

Use and, or

True

Use 1

False

Use 0

None

Use 0

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