FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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