FA-91526 / Digital signal filters / Open access
Frequency response evaluates z^-k as e^{+jwk} · case 01
Magnitudes are right but every phase has the opposite sign; a pure delay shows phase lead.
ROOT CAUSE
Both polynomials use e^{+jwk}, conjugating the response.
VERIFIED REPAIR
Use e^{-jwk} for both numerator and denominator.
Unsuccessful approach: The attempted repair fixes the numerator only, so IIR phase is still wrong.
Case contract
Input [b, a, freqs]; coefficients and frequencies are numbers or rational strings, frequencies are fractions of Nyquist. For w = pi f evaluate H = B(e^{jw})/A(e^{jw}) with z^-k = e^{-jwk}; return per frequency [20 log10 |H| rounded to 4, phase in degrees rounded to 3], "pole" if |A| < 1e-12, "zero" if |H| < 1e-9.
Why this case matters
Magnitude and phase plots are how filters are verified; unit or sign slips misreport cutoff and delay.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import cmath
from fractions import Fraction
N = 1
observations = []
def solve(x):
b, a, fs = x
b = [float(Fraction(v)) for v in b]
a = [float(Fraction(v)) for v in a]
out = []
for f in fs:
w = math.pi * float(Fraction(f))
B = sum(c * cmath.exp(1j * w * k) for k, c in enumerate(b))
A = sum(c * cmath.exp(1j * w * k) for k, c in enumerate(a))
if abs(A) < 1e-12:
out.append('pole')
continue
H = B / A
mag = abs(H)
if mag < 1e-9:
out.append('zero')
continue
out.append([round(20 * math.log10(mag), 4) + 0.0, round(math.degrees(cmath.phase(H)), 3) + 0.0])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: first difference', [[1, -1], [1], ['0', '1/2', '1']], ['zero', [3.0103, 45.0], [6.0206, 0.0]]], ['regression: one pole', [[1], [1, '-1/2'], ['0', '1/4', '1/2']], [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]]], ['regression: random response 1', [[0, -1, 1, 0], [1, '1/3', '1/3'], ['1/2', '3/4', '1/8']], [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: pure delay phase', [[0, 1], [1], ['1/4', '1/2']], [[0.0, -45.0], [0.0, -90.0]]], ['regression: random response 0', [['-1/4', 1], [1, 0], ['1/3', '1/8', '1/8']], [[-0.9018, -73.898], [-2.2144, -29.591], [-2.2144, -29.591]]], ['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['regression: random response 3', [[0, 0, -1], [1], ['1/2', '1/8', '1']], [[0.0, 0.0], [0.0, 135.0], [0.0, -180.0]]], ['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['regression: random response 6', [[1, -1], [1, '1/3', '1/3'], ['1/8', '1', '1/2']], [[-12.1798, 91.992], [6.0206, 0.0], [5.563, 71.565]]], ['regression: random response 7', [[2, -1], [1, '1/2'], ['3/4', '1/3', '1/3']], [[11.5896, 43.314], [2.3408, 49.107], [2.3408, 49.107]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 8', [[1, 2, -1, 1], [1, '-1/4', 0], ['1', '1/4', '0']], [[7.6042, -180.0], [7.6969, -45.419], [12.0412, 0.0]]], ['regression: random response 9', [[1], [1, '1/2'], ['1', '1/4', '1']], [[6.0206, 0.0], [-2.9161, 14.639], [6.0206, 0.0]]], ['regression: random response 12', [['-1/4'], [1, '1/2', 0], ['1/8', '0', '0']], [[-15.4136, -172.543], [-15.563, 180.0], [-15.563, 180.0]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: first difference | ['zero', [3.0103, -45.0], [6.0206, 0.0]] | ['zero', [3.0103, 45.0], [6.0206, 0.0]] | Failed |
| regression: one pole | [[6.0206, 0.0], [2.6529, 28.675], [-0.9691, 26.565]] | [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]] | Failed |
| regression: random response 1 | [[5.563, -161.565], [7.5974, -60.22], [-12.1798, 110.508]] | [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]] | Failed |
| control: two-tap average at nyquist | ['zero'] | ['zero'] | Passed |
| control: pole on unit circle | ['pole'] | ['pole'] | Passed |
| control: small magnitude | [[-80.0, 0.0]] | [[-80.0, 0.0]] | Passed |
| control: random response 16 | ['zero', [3.5218, 0.0], 'zero'] | ['zero', [3.5218, 0.0], 'zero'] | Passed |
SHA-256 / c32e0f933c50b6e1ee8431b26ad7759df8388dc62d2cf1e5883248b918d574b7
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
import cmath
from fractions import Fraction
N = 1
observations = []
def solve(x):
b, a, fs = x
b = [float(Fraction(v)) for v in b]
a = [float(Fraction(v)) for v in a]
out = []
for f in fs:
w = math.pi * float(Fraction(f))
B = sum(c * cmath.exp(-1j * w * k) for k, c in enumerate(b))
A = sum(c * cmath.exp(1j * w * k) for k, c in enumerate(a))
if abs(A) < 1e-12:
out.append('pole')
continue
H = B / A
mag = abs(H)
if mag < 1e-9:
out.append('zero')
continue
out.append([round(20 * math.log10(mag), 4) + 0.0, round(math.degrees(cmath.phase(H)), 3) + 0.0])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: first difference', [[1, -1], [1], ['0', '1/2', '1']], ['zero', [3.0103, 45.0], [6.0206, 0.0]]], ['regression: one pole', [[1], [1, '-1/2'], ['0', '1/4', '1/2']], [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]]], ['regression: random response 1', [[0, -1, 1, 0], [1, '1/3', '1/3'], ['1/2', '3/4', '1/8']], [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: pure delay phase', [[0, 1], [1], ['1/4', '1/2']], [[0.0, -45.0], [0.0, -90.0]]], ['regression: random response 0', [['-1/4', 1], [1, 0], ['1/3', '1/8', '1/8']], [[-0.9018, -73.898], [-2.2144, -29.591], [-2.2144, -29.591]]], ['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['regression: random response 3', [[0, 0, -1], [1], ['1/2', '1/8', '1']], [[0.0, 0.0], [0.0, 135.0], [0.0, -180.0]]], ['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['regression: random response 6', [[1, -1], [1, '1/3', '1/3'], ['1/8', '1', '1/2']], [[-12.1798, 91.992], [6.0206, 0.0], [5.563, 71.565]]], ['regression: random response 7', [[2, -1], [1, '1/2'], ['3/4', '1/3', '1/3']], [[11.5896, 43.314], [2.3408, 49.107], [2.3408, 49.107]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 8', [[1, 2, -1, 1], [1, '-1/4', 0], ['1', '1/4', '0']], [[7.6042, -180.0], [7.6969, -45.419], [12.0412, 0.0]]], ['regression: random response 9', [[1], [1, '1/2'], ['1', '1/4', '1']], [[6.0206, 0.0], [-2.9161, 14.639], [6.0206, 0.0]]], ['regression: random response 12', [['-1/4'], [1, '1/2', 0], ['1/8', '0', '0']], [[-15.4136, -172.543], [-15.563, 180.0], [-15.563, 180.0]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: first difference | ['zero', [3.0103, 45.0], [6.0206, 0.0]] | ['zero', [3.0103, 45.0], [6.0206, 0.0]] | Passed |
| regression: one pole | [[6.0206, 0.0], [2.6529, 28.675], [-0.9691, 26.565]] | [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]] | Failed |
| regression: random response 1 | [[5.563, 108.435], [7.5974, 74.78], [-12.1798, -136.992]] | [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]] | Failed |
| control: two-tap average at nyquist | ['zero'] | ['zero'] | Passed |
| control: pole on unit circle | ['pole'] | ['pole'] | Passed |
| control: small magnitude | [[-80.0, 0.0]] | [[-80.0, 0.0]] | Passed |
| control: random response 16 | ['zero', [3.5218, 0.0], 'zero'] | ['zero', [3.5218, 0.0], 'zero'] | Passed |
SHA-256 / c44f08a4d26510a97736148150dba0a1c3f6e4f7f35fb9cb453a6d67a7627ef2
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
import cmath
from fractions import Fraction
N = 1
observations = []
def solve(x):
b, a, fs = x
b = [float(Fraction(v)) for v in b]
a = [float(Fraction(v)) for v in a]
out = []
for f in fs:
w = math.pi * float(Fraction(f))
B = sum(c * cmath.exp(-1j * w * k) for k, c in enumerate(b))
A = sum(c * cmath.exp(-1j * w * k) for k, c in enumerate(a))
if abs(A) < 1e-12:
out.append('pole')
continue
H = B / A
mag = abs(H)
if mag < 1e-9:
out.append('zero')
continue
out.append([round(20 * math.log10(mag), 4) + 0.0, round(math.degrees(cmath.phase(H)), 3) + 0.0])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: first difference', [[1, -1], [1], ['0', '1/2', '1']], ['zero', [3.0103, 45.0], [6.0206, 0.0]]], ['regression: one pole', [[1], [1, '-1/2'], ['0', '1/4', '1/2']], [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]]], ['regression: random response 1', [[0, -1, 1, 0], [1, '1/3', '1/3'], ['1/2', '3/4', '1/8']], [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: pure delay phase', [[0, 1], [1], ['1/4', '1/2']], [[0.0, -45.0], [0.0, -90.0]]], ['regression: random response 0', [['-1/4', 1], [1, 0], ['1/3', '1/8', '1/8']], [[-0.9018, -73.898], [-2.2144, -29.591], [-2.2144, -29.591]]], ['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 2', [['1/2', '-1/4', 0, 2], [1, '1/2', '1/2'], ['1/2', '1/2', '1/2']], [[10.2633, 122.471], [10.2633, 122.471], [10.2633, 122.471]]], ['regression: random response 3', [[0, 0, -1], [1], ['1/2', '1/8', '1']], [[0.0, 0.0], [0.0, 135.0], [0.0, -180.0]]], ['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 5', [[1], [1, '1/2', '1/2'], ['1/4', '1', '1/8']], [[-4.0835, 32.236], [0.0, 0.0], [-5.5545, 16.706]]], ['regression: random response 6', [[1, -1], [1, '1/3', '1/3'], ['1/8', '1', '1/2']], [[-12.1798, 91.992], [6.0206, 0.0], [5.563, 71.565]]], ['regression: random response 7', [[2, -1], [1, '1/2'], ['3/4', '1/3', '1/3']], [[11.5896, 43.314], [2.3408, 49.107], [2.3408, 49.107]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]], [['regression: random response 8', [[1, 2, -1, 1], [1, '-1/4', 0], ['1', '1/4', '0']], [[7.6042, -180.0], [7.6969, -45.419], [12.0412, 0.0]]], ['regression: random response 9', [[1], [1, '1/2'], ['1', '1/4', '1']], [[6.0206, 0.0], [-2.9161, 14.639], [6.0206, 0.0]]], ['regression: random response 12', [['-1/4'], [1, '1/2', 0], ['1/8', '0', '0']], [[-15.4136, -172.543], [-15.563, 180.0], [-15.563, 180.0]]], ['control: two-tap average at nyquist', [[1, 1], [1], ['1']], ['zero']], ['control: pole on unit circle', [[1], [1, -1], ['0']], ['pole']], ['control: small magnitude', [[1, '-0.9999'], [1], ['0']], [[-80.0, 0.0]]], ['control: random response 16', [['-1/4', -1, '-1/4', -1], [1], ['1/2', '1', '1/2']], ['zero', [3.5218, 0.0], 'zero']]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: first difference | ['zero', [3.0103, 45.0], [6.0206, 0.0]] | ['zero', [3.0103, 45.0], [6.0206, 0.0]] | Passed |
| regression: one pole | [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]] | [[6.0206, 0.0], [2.6529, -28.675], [-0.9691, -26.565]] | Passed |
| regression: random response 1 | [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]] | [[5.563, 161.565], [7.5974, 60.22], [-12.1798, -110.508]] | Passed |
| control: two-tap average at nyquist | ['zero'] | ['zero'] | Passed |
| control: pole on unit circle | ['pole'] | ['pole'] | Passed |
| control: small magnitude | [[-80.0, 0.0]] | [[-80.0, 0.0]] | Passed |
| control: random response 16 | ['zero', [3.5218, 0.0], 'zero'] | ['zero', [3.5218, 0.0], 'zero'] | Passed |
SHA-256 / cd7b93b4322c1d056358756a31162a8897c62776e6944dac85226f2e2a33bd1b
Verification & scope
A deterministic bounded teaching model with a stipulated toy contract; exact rational arithmetic or fixed-decimal rounding keeps outputs strict JSON. It is not a production DSP library and claims no standards conformance. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:36.852155+00:00.
Case digest / 00acabc027ee049fe8ebd6f7e00ff3611033100e3b5fa377767d1bc156d20ff2