발화율 부호화 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""발화율 부호화 검증.
문서: 05.발화율 부호화 (예시로 보기, 정의, 카드 C2·C3)
주장:
1. 불응기가 t_ref이면 스파이크 사이 간격은 t_ref 이상이므로 발화율은 1/t_ref 이하다.
t_ref = 1 ms이면 1,000회/초 이하다.
2. 실제 최대 발화율 500~800회/초는 이 상한 안에 있다. 스파이크 간격으로는 2 ms, 1.25 ms다.
3. 불응기가 있는 적분-발화 모형에서 발화율은 r(I) = 1 / (t_ref + theta/I)다.
세기 I가 커질수록 r이 커지지만 1/t_ref를 넘지 못하고 포화한다.
방법: (1) 산술 확인 (2) 0.001 ms 단위로 모형을 직접 시뮬레이션해 공식과 비교한다.
단위: 시간은 ms, 발화율은 회/초.
"""
from fractions import Fraction as F
def rate_formula(t_ref_ms: F, theta: F, current: F) -> F:
"""불응기 t_ref, 문턱 theta, 입력 세기 current(단위/ms)일 때의 발화율(회/초)."""
interval_ms = t_ref_ms + theta / current
return 1000 / interval_ms
def simulate_rate(t_ref_ms: F, theta: F, current: F, total_ms: int = 200) -> F:
"""막전위를 0에서 쌓아 theta에 닿으면 발화, 0으로 되돌리고 t_ref 동안 멈춘다."""
dt = F(1, 1000) # 0.001 ms
v = F(0)
t = F(0)
refractory_until = F(-1)
spikes = []
end = F(total_ms)
while t < end:
if t >= refractory_until:
v += current * dt
if v >= theta:
spikes.append(t)
v = F(0)
refractory_until = t + t_ref_ms
t += dt
intervals = [b - a for a, b in zip(spikes, spikes[1:])]
mean_interval = sum(intervals) / len(intervals)
return 1000 / mean_interval
def main() -> None:
t_ref = F(1) # 1 ms
upper = 1000 / t_ref
assert upper == 1000
print(f"[OK] 불응기 1 ms -> 발화율 상한 {upper}회/초")
for r in (500, 800):
interval = F(1000, r)
assert interval >= t_ref and r <= upper
print(f"[OK] {r}회/초 -> 간격 {float(interval)} ms (>= 1 ms)")
# 모형: 문턱 1, 세기를 바꿔 가며 시뮬레이션과 공식 비교
theta = F(1)
rates = []
for current in (F(1, 4), F(1, 2), F(1), F(2), F(4)):
sim = simulate_rate(t_ref, theta, current)
formula = rate_formula(t_ref, theta, current)
# 시뮬레이션은 0.001 ms 격자에 맞춰지므로 1% 안에서 일치하면 된다
assert abs(sim - formula) / formula < F(1, 100), (current, sim, formula)
assert formula < upper
rates.append(formula)
print(f"[OK] 세기 {float(current):>5}: 공식 {float(formula):7.2f}회/초, 시뮬레이션 {float(sim):7.2f}회/초")
assert all(a < b for a, b in zip(rates, rates[1:])), "세기가 커지면 발화율도 커져야 한다"
print("[OK] 세기가 커질수록 발화율 증가, 모두 1,000회/초 미만")
# 포화: 세기를 아무리 키워도 상한에 다가갈 뿐
for current in (F(10), F(100), F(1000)):
r = rate_formula(t_ref, theta, current)
assert r < upper
near = rate_formula(t_ref, theta, F(1000))
assert upper - near < 1
print(f"[OK] 세기 1000 -> {float(near):.2f}회/초 (상한 1,000에 붙음)")
# 카드 C3: 문턱까지 쌓는 시간 theta/I = 1 ms이면 간격 2 ms -> 500회/초
assert rate_formula(t_ref, theta, F(1)) == 500
print("[OK] 카드 C3: theta/I = 1 ms -> 500회/초")
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()