Math errors - o3 to the rescue
May 31, 2025
How o3 saved me hours of debugging with a math problem
Something’s afoot
So I wanted to implement a frequency detector - easy enough, for a single frequency in a signal I chose the Goertzel algorithm. A simple resonant IIR filter which outputs a complex number which is equal to a Discrete Fourier Transform bin. That’s what I came up with:
samplerate = 1000
N = 1000 # 1 second
f = 50
s = np.cos(2 * np.pi * f * np.arange(0, N)/samplerate) + \
np.cos(2 * np.pi * f*2 * np.arange(0, N)/samplerate)
def goertzel(x, f, samplerate):
w = 2 * np.pi * f/ samplerate
c = 2 * np.cos(w)
# Second order resonant IIR filter
s = [0,0,0]
for n in range(len(x)):
s[0] = x[n] + c * s[1] - s[2]
s[2] = s[1]
s[1] = s[0]
# Use internal filter state to calculate DFT bin
real = s[1] - s[2] * np.cos(w)
imag = s[2] * np.sin(w)
return (real + 1j * imag) / (N/2)
result = goertzel(s, f, samplerate)
np.abs(result), np.angle(result)
> 1.0000000000000004, -36.00000000000132
Magnitude is \(1\), it works! But wait, the phase angle is, uh, -36 degrees? That can’t be right. It should be close to \(0\), because I’m measuring a cosine.
o3 to the rescue
o4 is my goto because of the speed. It handles the usual small queries quite well.
I think by now I can tell if o4 doesn’t know what it’s talking about. It meanders around in the beginning of the response, repeating everything about the problem, and then taking a turn into random-guess land. By contrast when it knows the issue, it loses less time chit-chatting and goes straight at it. But telling it to cut out the bs does not help.
So, this time I switched to o3, and after about 4 minutes, it came back to me with a result, which was spot on: The calculation of the DFT bin coefficients that depend on the internal IIR filter state were switched:
real = s[1] - s[2] * np.cos(w)
imag = s[2] * np.sin(w)
should be
real = s[1] * np.cos(w) - s[2]
imag = s[1] * np.sin(w)
The fun thing about this, this was not clear to o3 at all - it took some time to debug this. It did:
- Question itself on its own knowledge, checking some trigonometry fundamentals by writing small python programs and executing them
- Question my choice of signal generation, again by writing and running small python programs
- Run my python code and checking if it could reproduce my reported numbers
- Wonder about ways in which I could’ve made errors in my algorithm implementation that explain the wrong values
It closes in on the error step by step, until it realizes there’s a index shift error. Those 4 minutes could’ve been one hour of debugging.