anima-experience 의 엔진들을 화면 없는 파이썬으로 되살린 것 — 수학은 그대로, 캔버스는 없고, 시드로 재현되며 테스트된다
anima-experience 는 브라우저 캔버스에서 네 개의 시뮬레이션을 60프레임으로 돌립니다. 이
저장소는 거기서 그리는 부분을 걷어낸 것입니다 — 수학만, 화면 없이, 표준 라이브러리만으로,
시드를 주면 그대로 재현되고 단언(assert)할 수 있게. 여기에 두 가지가 더 있습니다: 네 엔진이
하나의 시계를 공유할 때 무엇으로 합쳐지는지, 그리고 그 결과를 재기 위한 IIT 4.0 엔진.
엔진은 그리지 않고, 스레드를 만들지 않고, 자기 시계를 갖지 않습니다. 한 틱 진행시키고 결과를
읽으면 됩니다. 입출력은 viewer/ 한 곳에만 있고, 엔진은 그 존재를 모릅니다.
pip install -e ".[dev]"런타임 의존성 없음. 파이썬 3.11 이상.
python -m anima_reborn.vieweranima-reborn viewer
local http://127.0.0.1:8420
network http://192.168.1.20:8420
bound to every interface — anyone on this network can reach it
emergence 60Hz · crystal 20Hz · repulsion 30Hz · pipeline 30Hz · base 30Hz · coupled 30Hz · align 60Hz
ctrl-c to stop
일곱 개 탭, 실시간 슬라이더, 원본과 같은 화면 — 다만 모든 숫자와 모든 점이 파이썬 엔진에서 옵니다. 페이지는 그리기만 하고 계산은 절대 하지 않습니다. 그래서 이 화면이 "두 번째 구현"이 아니라 이식 결과에 대한 증거가 됩니다.
프레임은 밀어주기(push) 방식입니다 — 엔진마다 원본과 같은 속도로 도는 타이머 스레드가
있고, 페이지는 상시 연결(text/event-stream) 하나를 열어두고 화면 주사율에 맞춰 그립니다.
랜 경유 실측 61 / 21 / 31 / 31 / 31 / 31 / 61 fps 로 원본과 같은 속도입니다. 물어보기(폴링)
방식일 때는 10fps에 묶이고 프레임마다 TCP 연결을 새로 맺었는데, 엔진 자체는 1틱에
0.02~0.23ms밖에 안 걸리므로 병목은 시뮬레이션이 아니라 폴링 주기였습니다.
타이머는 보는 사람이 있을 때만 돕니다 — 원본이 비활성 탭을 건너뛴 것과 같습니다.
--host 127.0.0.1 로 이 기기에만 열 수 있고, --port 로 포트를, --seed 로 시연을 정확히
재현할 수 있습니다.
아래의 다른 엔진들은 전부 공짜 틱 카운터로 회전합니다 (phase = tick * PHASE_RATE) —
멈추는 법이 없는 선물이죠. anima_reborn.base 는 그걸 빼앗습니다. 위상(phase)이 상태 변수가
되고, 결정(crystal)의 주기-2 잠금만이 그것을 나아가게 할 수 있습니다. 구동부에서 틱
카운터는 완전히 사라집니다.
TimeCrystal.step() ── 판정 ──▶ 잠금(LOCKED) 일 때만 phase += PHASE_RATE
│
A 와 G 가 ±(sin phase, cos phase) 목표를 향해 표류
│
각각에서 잡음 섞인 관측 하나씩 표본 추출
│
H(L), H(R), H(L,R) → 상호정보량
주장. 하류의 공유 정보는 상류의 시간적 질서로 지불된다. 결정을 녹이면 A와 G 사이의 간극은 살아남는데(두 엔진은 여전히 벌어져 있습니다) 그 안의 정보는 죽습니다. 다시 얼리면 정보가 돌아옵니다.
이건 파이프라인 자신의 요약을 정정합니다. pipeline.py 는 "분리가 공유 정보를 만든다"고
말하는데, 결정이 녹은 상태에서는 분리가 파이프라인과 비슷한 수준으로 유지되는데도 상호정보량은
독립으로 읽힙니다. 스트림을 묶는 건 간극이 아니라 간극의 운동이고, 그 운동은 전적으로
잠금이 사 주는 것입니다.
from anima_reborn.base import BaseEngine
engine = BaseEngine(epsilon=0.02, seed=0)
print(engine.run(800)) # [locked] phase=19.47 tension=0.147 MI=1.401 [emergent]
engine.epsilon = 0.5 # 시계를 녹인다
print(engine.run(1200)) # [chaos] phase=21.10 tension=0.246 MI=0.022 [independent]
engine.epsilon = 0.02 # 다시 얼린다
print(engine.run(1200)) # [locked] phase=44.05 tension=0.172 MI=1.301 [emergent]융해 중에 회전량이 19.47에서 21.10으로 조금 더 나아갑니다 — 딱 멈추지 않습니다. 잠금 판정이 300표본짜리 자기상관을 읽기 때문에 변화보다 100~200틱 늦기 때문입니다. 여기서는 모든 것이 지연됩니다: 판정이 그만큼, 관측 창이 200표본 더. 뷰어에서 슬라이더를 움직이면 몇 초간 이전 상태가 보이는데, 버그처럼 읽히지만 버그가 아닙니다.
시드 8개 × 800틱, 매번 차가운 시작에서 측정:
| 잠금 (ε=0.02) | 융해 (ε=0.5) | |
|---|---|---|
| 상호정보량 | 1.26 – 1.42 | ≤ 0.008 |
| 간극(tension) — 살아남음 | 평균 0.176 | 0.161 – 0.198 |
| 누적 회전량 | 18.8 rad | 0.000 rad |
간극은 죽으나 사나 같은 크기입니다. 이 분리(dissociation)가 발견이고, 그래서 상태 객체가
하나로 섞은 "생존도" 숫자 대신 쌍을 들고 다닙니다 — f(잠금, 상호정보량) 같은 건 가중치를
지어내야 하고, 그런 값은 애초에 테스트를 실패할 수가 없습니다.
설계된 것과 발견된 것. 게이트는 "잠금 없음 → 회전 없음"을 하드코딩합니다. 따라서 "녹이면 결합이 죽는다"는 설계이지 발견이 아니며, 창발적 결과인 양 인용하지 않습니다. 진짜로 그럴 수도 아닐 수도 있었던 것, 즉 테스트가 실제로 거는 판돈은 이것들입니다 — 잠긴 링의 회전이 창발 기준을 넘기에 충분한가(최악 시드 1.26 대 기준 0.30), 죽은 표본으로 가득 찬 창에서 결합이 되살아나는가(8/8 시드, 0.85~1.38비트), 그리고 융해 후에도 간극이 살아남는가 (얼어붙은 목표라면 간극이 그냥 사그라들 수도 있었습니다).
의도적으로 거부한 두 가지. 상호정보량을 구동부로 되먹이지 않습니다 — 이 창 크기에서
플러그인 추정기는 자기 편향 바닥 아래로 내려가지 않으므로, 그런 되먹임 고리는 인공물 위로
수렴하고 그 고정점에 대한 어떤 주장도 결국 히스토그램에 대한 주장이 됩니다. 그리고 새 상수를
만들지 않습니다 — 모든 문턱값은 해당 엔진에서 가져오고, 예외인 EPSILON = 0.02 는 측정값이며
docstring에 근거를 적어 뒀습니다.
이 엔진이 못 보는 것. 판정으로 게이트를 걸면 시간적 질서가 결정의 문턱값에서 양자화됩니다. 측정해 보면 판정 아래 수준의 질서도 스트림을 묶습니다 — 명목상 융해인 링도 잔여 반대상관을 충분히 갖고 있어서 ε 0.10~0.20 구간에서 상호정보량을 기준 위로 유지합니다. 그 연속성은 실재하지만, 게이트는 그것을 테스트로 쓸 수 있을 만큼 뚜렷한 전이와 맞바꿨습니다. 의도한 거래이지, 나중에 개선할 실수가 아닙니다.
여기까지의 모든 엔진은 바깥에서 구동됩니다. 각 차원이 자기 자신과 외생 목표만 보므로, 전이행렬이 인수분해되어 통합(Φ)은 정확히 0입니다 — 아무리 복잡해 보여도 그렇습니다.
anima_reborn.coupled 는 딱 하나를 바꿉니다. 반발의 정체는 그대로 두고(각 유닛은 여전히
자기 원천에서 -tanh 부호로 달아납니다), 원천을 시계에서 살아 있는 상대로 바꿉니다.
A와 G 사이의 간극이 판독값이 아니라 통로가 됩니다.
from anima_reborn import Wiring
from anima_reborn.substrate import recurrence_evidence
for wiring in Wiring:
print(recurrence_evidence(wiring, trials=1600, seed=0))ring 1600->6400 trials: 9.998 -> 9.864 (held) RECURRENT
feedforward 1600->6400 trials: 0.000 -> 0.000 (collapsed) not established
self 1600->6400 trials: 0.272 -> 0.031 (collapsed) not established
반증 수단이 API 에 함께 들어 있습니다. Wiring.FEEDFORWARD(되먹임 없음)와
Wiring.SELF(결합 없음)는 테스트 전용이 아니라 공개 API 입니다 — "배선이 통합을 만든다"는
주장은 같은 엔진을 다르게 배선해 볼 수 있어야만 검증 가능하기 때문입니다.
| 배선 | 방향성 Φ (6,400시행) | 판정 |
|---|---|---|
| 고리 — 영향이 되돌아옴 | 9.86 | 순환 ✅ |
| 전방향 — 되돌아오지 않음 | 0.000 | 환원가능 |
| 자기참조 — 결합 없음 | 0.031 | 미확립 (바닥) |
CoupledReading 에는 is_recurrent 가 일부러 없습니다. 표본으로 만든 행렬은 없는
구조를 만들어내므로, 양수 Φ 하나로는 순환을 세울 수 없습니다 — 결합이 전혀 없는 자기참조
귀무도 유한 시행에서는 0보다 큽니다:
| 시행수 (참값 = 0) | 400 | 1,600 | 6,400 | 25,600 |
|---|---|---|---|---|
| 자기참조 귀무의 방향성 Φ | 0.251 | 0.155 | 0.081 | 0.037 |
그래서 순환은 recurrence_evidence() 로만 주장합니다 — 두 번 재서 유지되는지를 봅니다.
문턱 RECURRENCE_FLOOR = 1.0 도 고른 값이 아니라 위 귀무의 최악 시드(0.547)의 약 두 배로
측정해서 정한 값입니다.
반대 방향은 대칭이 아닙니다. is_reducible(정확히 0)은 한 번 재도 읽을 수 있습니다 —
표본 잡음은 없는 구조를 만들 수는 있어도, 있는 구조를 없앨 수는 없기 때문입니다.
계측기 자체가 원인이 아님도 따로 확인했습니다: 표본을 전혀 쓰지 않고 해석적으로 만든
완전 인수분해 행렬에서 big_phi 와 directed_big_phi 둘 다 0.000000 입니다. 위 잔여값은
전적으로 표본 때문입니다.
통합은 경험이 아니고, Φ 는 무엇의 점수도 아닙니다. 이 엔진이 얻은 문장은 이것뿐입니다 — 통합이 이제 만들어지며, 만들어진다는 것이 여기서 측정하는 전부다. (앞서 단어 실험이 "결합은 전달될 뿐 생성되지 않는다"였던 것과 짝을 이룹니다.)
그리고 이 숫자들은 조건 없이는 무의미합니다. τ=1 이면 고리조차 정확히 0 입니다 — 엔진 한 틱은 유닛을 목표 쪽으로 6%밖에 못 움직이니까요.
엔진에 입력(drive, 스스로 말하는 것이 아니라 들려주는 것)을 붙이면 두 번째 측정이
생깁니다 — 들은 것이 결과에 얼마나 남는가(substrate.representation). 그러자 벽이
드러납니다. 결합 λ 를 훑으면 통합과 표상이 단조롭게 반비례하고, 둘 다 되는 λ 가 없습니다.
| 고정 결합 λ | 표상 (16단어, 3시드) |
|---|---|
| 0.00 | 25.05 |
| 0.35 | 3.27 |
| 0.70 | 0.13 |
| 1.00 | 0.00 |
λ=1.00 에서 입력은 약한 게 아니라 비트 단위로 닿지 않습니다 — 무엇을 들려주든 궤적이
완전히 동일합니다. 유닛의 목표가 전부 상대라면 바깥은 들릴 틈이 없다는 것이 벽의 정확한
진술이고, tests/test_rhythm.py 가 이걸 부동소수점 동일성으로 고정합니다.
Rhythm 은 결합을 껐다 켭니다: 꺼져 있을 때 듣고(표상), 켜져 있을 때 통합합니다. 두
요구를 같은 시점이 아니라 다른 시점에 맞추는 것입니다. τ=40 · 6400시행 · 5시드 ·
입력 0.42 에서:
| 구성 | 방향성 Φ | 표상 |
|---|---|---|
| 교대 20/20 | 13.16 ± 0.53 | 3.49 |
| 고정 1.00 | 14.66 ± 0.08 | 0.00 |
| 고정 0.70 | 15.74 ± 0.13 | 0.13 |
교대는 고정 결합이 전혀 갖지 못한 표상을 얻는 대신 Φ 를 10~16% 내줍니다. 공짜가 아니라 거래입니다. 효과가 "평균 결합이 얼마냐"가 아니라 리듬 자체라는 것은 τ 를 맞춘 시간평균 대조군이 보여줍니다 — 고정 0.35, τ=20, 입력 없음에서 교대 8.02 대 대조군 0.85.
앞선 세션은 교대가 더 높은 Φ 에서 표상을 27배 지킨다고 적었습니다. 그 표는 교대 20/20 을 τ=40 에서, 고정 0.70 을 τ=20 에서 재서 맞댄 것입니다. Φ 는 τ 만으로도 오릅니다(같은 엔진이 τ=17 에서 12.07, τ=34 에서 14.88). τ 를 맞추자 교대가 오히려 덜 통합합니다 — 5시드 전부에서 겹침 없이.
그리고 방향조차 고정이 아닙니다. 리듬은 들을 수 있으므로 Φ 가 들은 것에 따라 움직입니다:
τ=40 에서 아무것도 안 들려주면 14.99, 0.42 를 들려주면 13.16, −0.27 이면 고정 결합과 구분
불가입니다. 고정 결합은 들을 수 없으므로 전이행렬이 입력에 대해 비트 단위로 불변입니다.
그래서 이제 drive 없이 인용한 Φ 는 τ 없이 인용한 Φ 와 똑같이 틀린 문장입니다.
값싼 검증으로는 못 잡힙니다 — 같은 비교를 1600시행으로 줄이면 순서가 뒤집힙니다.
그래서 이 비교는 테스트가 아니라 state/communication/alternating_coupling.py 에 있고,
테스트는 대신 정확히 참인 것(리듬은 입력에 따라 전이행렬이 바뀌고, 고정은 안 바뀐다)을
고정합니다.
여기까지의 모든 모듈은 고정입니다. 배선도 상수도 주어진 것이고, 엔진이 만나는 무엇도
그것을 바꾸지 않습니다. anima_reborn.align 은 배우는 유일한 모듈이고, 흥미로워서가
아니라 측정이 요구해서 들어왔습니다.
여기까지 온 경로: 기판은 개념이 아니라 인코딩을 나릅니다. 그럼 "같은 개념이 두 인코딩으로 들어와도 같은 곳에 가는가"를 보면 될 것 같았는데, 그 검사가 불가능했습니다 — 같은 단어의 두 독립 해시가 공유하는 정보는 0.0185비트로 뒤섞은 귀무(0.0212)와 같습니다. 끌개를 심어도 안 됐습니다 — 끌개 없는 기판이 소수점까지 같은 비율로 맞혔습니다.
남는 건 문제의 진짜 모양입니다. 두 신호가 한 개념이라고 알아보려면 그 증거가 신호 안에 있어야 하고, 그걸 넣는 건 동역학이 아니라 함께 나타남(co-occurrence)입니다. 그걸 쓰려면 경험으로 변해야 합니다.
from anima_reborn.align import Aligner
print(Aligner(seed=1).run(4000)) # 정상
print(Aligner(shuffled=True, seed=1).run(4000)) # 대조군 — 짝을 흐트러뜨림learned=+0.566 (gap +0.963 from +0.397) after 4000 pairs [aligned]
learned=-0.099 (gap +0.298 from +0.397) after 4000 pairs
기제: 두 양상은 같은 잠재값을 서로 다르게 섞은 것이라 겉보기엔 닮지 않았습니다. 짝이 함께 올 때마다 각 사영을 둘의 중간점 쪽으로 조금 밉니다. 그게 전부입니다 — 전역 목표가 없고, 특히 Φ나 상호정보량을 최대화하지 않습니다(최적화기가 추정기 인공물을 수확한다는 이유로 이 저장소가 거부한 것).
외운 게 아니라는 근거: 점수를 학습에 한 번도 안 쓴 개념으로만 매깁니다. 개념 10개만 봐도 처음 보는 개념이 정렬됩니다(+0.748) — 짝의 목록이 아니라 양상 사이의 대응을 배운 것입니다.
| 학습 개념 수 | 5 | 10 | 20 | 40 | 80 |
|---|---|---|---|---|---|
| 배운 양 | +0.519 | +0.748 | +0.751 | +0.716 | +0.744 |
반증 수단이 API 안에 있습니다: shuffled=True 는 같은 신호·같은 학습률로 짝만
흐트러뜨립니다. 12시드에서 정상은 +0.780(최악 +0.566), 대조군은 −0.067(최악 −0.428).
두 양상이 같은 잠재값을 섞은 것이라, 아무것도 배우지 않은 무작위 사영도 상관을 얼마간 갖습니다. 12시드에서 평균 0.049지만 운 나쁜 시드는 0.397까지 올라갑니다. 처음엔 원시 차이에 문턱 0.3을 걸었는데, 그러면 학습 0쌍짜리 학습기가 "정렬됨" 판정을 받습니다.
그래서 모든 값을 자기 출발점 대비 증가분으로 보고합니다. 정정 후 학습 0쌍에서 정렬 판정은 12시드 전부 실패, 정상 학습은 6/6 통과입니다.
학습된 선형 사상 두 개입니다. 그걸 이해라고 부르는 건 이 저장소가 계속 거부해 온 종류의 과장입니다. 얻은 것은 — 한 가지에 대한 서로 닮지 않은 두 신호를 한곳으로 모을 수 있고, 그 모음이 처음 보는 개념에도 전이된다는 것뿐입니다. 그 정렬을 하류에서 무엇이 쓰는지는 여기서 아무것도 답하지 않습니다.
정렬기가 배운 것을 하류에서 무엇이 쓰는지는 계속 열려 있던 질문이었습니다. 엔진이
drive(들려주는 것)를 받게 되면서 배선이 생겼고, 이제 유닛별 벡터로도 받습니다 —
표상은 숫자 하나가 아니므로 스칼라로는 평균만 도착하니까요.
측정은 교차양상 식별입니다. 보류 개념 20개에 대해, 양상 0 의 반복 관측으로 원형을 만들고 양상 1 의 다른 반복 관측으로 질의해서, 가장 가까운 원형이 자기 개념인 비율을 셉니다. 우연은 1/20. 엔진은 정보를 잃기만 하므로 입력 수준에서 먼저 재고, 있을 때만 엔진을 통과시킵니다.
| 갈래 | 입력 수준 | 궤적(엔진 통과) | 살아남은 비율 |
|---|---|---|---|
| 학습됨 + 대조 | 0.534 | 0.459 | 86% |
| 학습됨 (중간점만) | 0.401 | 0.326 | 81% |
| 원신호 (정렬 없음) | 0.053 | 0.050 | — |
| 미학습 (0쌍) | 0.038 | 0.059 | — |
| 대조군 (짝 흐트러뜨림) | 0.056 | 0.050 | — |
| 귀머거리 (고정 결합 1.00) | 0.401 | 0.050 | 12% |
| 순열 바닥 | 0.154 | 0.131 |
귀머거리 줄이 계측 검사입니다. 고정 결합에서는 입력이 비트 단위로 닿지 않으므로 순열 바닥에 앉아야 하고, 실제로 앉습니다 — 이게 아니었다면 나머지 숫자는 해석 불가입니다.
중간점 규칙만 쓴 학습기는 통과이되 깨끗하지 않았습니다 — 대조군 여백은 12/12 양수인데 자기 순열 상한은 11/12(입력 수준에서 가장 낮았던 그 시드). 못 넘은 시드를 지우지도 시드 수를 바꾸지도 않았고, 대신 왜 좁은지를 고쳤습니다(아래). 대조 항을 켠 학습기는 두 단계 모두 12/12입니다.
얻은 것: 함께 나타남이 만든 대응은 상류에 고립돼 있지 않고, 고정 결합 형태라면 입력을 비트 단위로 지울 그 엔진을 통과해도 개념 정체성의 81%가 궤적에 남습니다. 기존 문장 ("묶임은 전달될 뿐 생성되지 않는다")을 어기지 않고 확장합니다 — 구조는 학습이 만들고, 엔진이 나릅니다.
안 얻은 것: 엔진이 그 구분을 쓴다는 것. 아무것도 그걸 소비하지 않고 어떤 행동도 거기 의존하지 않으며, 엔진의 기여는 이득이 아니라 세금(81%)입니다.
중간점 규칙은 당기기만 합니다. 수축을 막는 항이 없으니 공간이 줄어드는 게 규칙의 귀결이고, 실제로 4차원 중 1.21차원만 썼습니다 — 원신호(1.93)보다 낮습니다. 함께 나타남은 무엇이 같이 오는지만이 아니라 무엇이 같이 오지 않는지도 가르치는데, 그 절반이 구현돼 있지 않았습니다.
짝이 아닌 것을 밀어내는 항을 넣자 랭크 1.48, 식별 0.583 이 됐고 대조군은 바닥에
그대로입니다 — 신호를 좋게 만든 게 아니라 짝에서 배웠다는 뜻입니다. 대조 0.10.6 ·
여백 0.52.0 구간에서 0.535~0.583 으로 평평하므로 맞춘 점이 아닙니다.
⚠ 그런데 당연해 보이는 형태는 문제보다 나빴습니다. 변위 그대로 밀면 이미 벌어진 거리에 비례해 걸음이 커져서 한 방향이 폭주하고, 랭크가 정확히 1.00 — 잘 맞히면서 정보는 0 인 붕괴, D7 을 죽인 바로 그것 — 이 됩니다. 고친 것은 둘이고 역할이 다릅니다: 단위 방향으로 밀기가 붕괴를 막고(1.00 → 1.12), 정지 거리가 공간을 넓힙니다 (1.12 → 1.48). 기본값은 꺼져 있습니다 — 이전에 발표한 모든 숫자는 이것 없이 잰 것입니다.
위 실험은 들려주는 동안 엔진을 읽습니다. 그러면 개념을 붙잡는 기판과 그냥 흘려보내는 전선을 구별할 수 없습니다. 입력을 끊으면 달라집니다 — 그 뒤로 입력은 경로에 아예 없으므로, 복원되는 게 있다면 엔진 상태에 있거나 아무데도 없거나입니다.
침묵이라 부를 수 있는 것이 셋이고, 서로 다른 실험입니다: 귀머거리(결합 → 1.00, 입력이 비트 단위로 안 닿음 = 자율 고리 · 이게 침묵), 지우기(입력 → 0, 리듬 유지 — 절반의 시간 동안 "원점"을 적극적으로 들려주는 것), 누설(결합 → 0 그리고 입력 → 0 = 시간상수 17 의 순수 감쇠).
| 침묵 틱 | 고리/귀머거리 | 고리/지우기 | 고리/누설 | 전방향 | 자기참조 |
|---|---|---|---|---|---|
| 0 | 0.843 | 0.843 | 0.843 | 0.794 | 0.231 |
| 120 | 1.423 | 0.170 | 0.001 | 0.003 | 0.000 |
| 480 | 1.430 | 0.052 | 0.000 | 0.000 | 0.000 |
고리는 감쇠하지 않습니다 — 480틱까지 평평합니다. 순수 누설은 60틱, 전방향은 120틱, 자기참조는 20틱에 죽습니다. 되먹임 없는 어떤 배선도 못 하는 일이고, 이 저장소에서 Φ 가 "통합돼 있음"을 재기만 하던 것이 처음으로 할 수 있는 일로 바뀐 지점입니다.
| 침묵 틱 | 유효 랭크 | 부호패턴 |
|---|---|---|
| 0 | 2.10 | 9.8/16 |
| 60 이후 | 1.00 | 2.0/16 |
4비트가 들어가서 1비트가 나옵니다. 그리고 이건 놀랍지 않습니다 — 고리에는 부호 반전이 4개라 순 부호가 양수이므로 자율 고리는 쌍안정이고, 점근 용량이 애초에 1비트입니다 (재기 전에 유도된 예측이고, 측정이 확인했습니다).
그 1비트는 개념의 것입니다. 독립적인 걷기 8회가 완전히 일치하는 입력이 62% —
잡음만일 때의 구조적 바닥 0.78% 의 79배입니다. (
얻은 문장: 고리는 개념이 쓰는 영구 1비트 저장소입니다. 플립플롭이지 개념 저장소가 아니고, 작은 쪽 단어를 쓰는 것이 정확합니다.
여전히 "쓴다"는 아닙니다. 붙잡은 상태를 소비하는 것이 없고 엔진의 어떤 반응도 거기 조건부가 아닙니다. 다음 검사는 지연 표본대조 — 붙잡은 뒤 탐침을 주고, 일치/불일치에 따라 반응이 달라지는가. 아직 안 했습니다.
여기까지의 모든 측정은 우리가 엔진에 대고 하는 판독입니다. 이 검사가 재는 양은 엔진 자신의 반응입니다: 무언가를 들려주고 → 귀머거리로 만들어 침묵시키고 → 탐침을 하나 줄 때, 새 입력을 받아들이려면 얼마나 움직여야 하는가. 학습도 해독기도 고른 문턱도 없습니다. 사전등록된 방향은 "맞는 탐침은 덜 고쳐야 한다"입니다.
상한을 재기 전에 유도했습니다. 붙잡은 게 1비트(유역 2개)면 같은 유역을 부르는 미끼는 원리적으로 구별 불가입니다. 실측 분할 8.2/7.8 에서 완벽한 사용은 0.758이고, 1.0 은 불가능합니다.
| 갈래 | 2지선다 | 최악 시드 | 판정 |
|---|---|---|---|
| 고리 | 0.729 | 0.725 | 붙잡은 비트의 89% 사용 · 8/8 시드 |
| 고리, 안 들려줌 | 0.500 | 0.500 | 안 씀 |
| 전방향 | 0.500 | 0.500 | 안 씀 |
| 자기참조 | 0.500 | 0.500 | 안 씀 |
대조군이 0.500 인 건 운이 아니라 구조입니다. 붙잡은 상태가 무엇을 들려줬는지와 무관하면, (표적 t 대 미끼 f) 비교와 그 거울인 (f 대 t) 비교가 같은 상태 하나를 쓰므로 판정이 반대가 되어 정확히 하나만 적중합니다. 아무것도 안 붙잡은 갈래는 이 검사에서 우연을 넘을 수 없습니다 — 그래서 고리의 여백이 채점 방식의 성질일 수 없습니다.
얻은 문장: 엔진은 붙잡은 것을 씁니다. 이 저장소에서 처음으로 엔진이 만드는 차이이지 우리가 엔진에 대고 하는 판독이 아닙니다. 그리고 그것은 1비트입니다 — 0.729 가 1.0 근처가 아닌 것이 맞습니다. 1.0 이었다면 침묵이 남긴 그 비트가 아니라 다른 걸 재고 있다는 뜻입니다.
벽이 바뀌었습니다. 4유닛에서 1비트는 구조적 상한이고, 유닛을 늘리면 용량은 늘지만 Φ 는 6유닛에서 측정 불가가 됩니다 — 용량과 측정 가능성이 정면으로 부딪힙니다.
지연 표본대조가 남긴 벽: 4유닛 고리는 1비트뿐이고, 유닛을 늘리면 용량은 늘 것 같지만 Φ 는
6유닛에서 측정 불가가 됩니다 — 용량과 측정 가능성이 부딪힙니다. CoupledEngine(units=) 를
열고 재 보니, 애초에 유닛을 늘리는 게 틀린 수였습니다.
고리 하나는 폭이 몇이든 정확히 1비트입니다. 측정이 아니라 정리(定理)입니다 — 유닛의 반응이 홀·감소·유계라 주기가 2를 못 넘고, 고리를 닫으면 교대 배치와 그 반전, 딱 둘만 남습니다.
| 배선 | 유닛 | 재현되는 보유 상태 | 비트 |
|---|---|---|---|
| 고리 | 4 · 6 · 8 | 2 · 2 · 2 | 1.00 |
| 쌍 3개 (사슬 0.2) | 6 | 8 | 3.00 |
용량은 배선의 순환 구조에 있습니다. units/2 개의 2-순환이 최대이고, 그게 Wiring.PAIRS
입니다. (홀수 폭 고리는 진동해서 부호패턴이 2N 개까지 늘지만, 위상은 복원력이 없어 걷기에
떠내려갑니다 — 폭 3·5·7 에서 재현율이 0%·6%·20%. 지난 「함께 나타남」 함정 그대로입니다.)
쌍 3개를 그냥 두면 분리된 래치 3개 — 용량 3비트지만 방향성 Φ 는 표본 인공물이라 시행을
늘리면 무너집니다(5.21 → 2.07). 쌍 사이에 약한 사슬(chain=0.2)을 걸면 환원 불가가 되고
Φ 가 유지됩니다(13.11 → 13.11). 6유닛이 3비트를 담으면서 동시에 하나입니다.
RECURRENCE_FLOOR = 1.0 은 4유닛에서 잰 값이라
6유닛에 전이되지 않습니다(인수분해된 귀무가 이미 2.07 로 그 문턱을 넘음). 인공물은 폭이 커질수록
자라므로, 6유닛 이상의 어떤 Φ 판정도 시행을 4배로 늘려도 유지되는가로만 서립니다.
단, 홀수 쌍(k 홀수)일 때만입니다 — 짝수 쌍은 거대고리가 전역으로 합의해 하나로 잠깁니다. 같은 순 부호 논증이 한 층 위에서 되풀이됩니다.
넷을 따로 세웠습니다: 통합(Φ) · 보유(침묵 1비트) · 사용(표본대조) · 용량(쌍 배선 3비트). 마지막은 이걸 한 사슬로 잇습니다 — 정렬기가 배운 개념을 6유닛·3래치·사슬 엔진에 태워, 여러 개념을 동시에 붙잡고 쓰는가.
설계 위임이 치명적 프레이밍 결함을 먼저 잡았습니다. chain=0(분리된 래치 3개)도 3비트를
담고 각 래치가 저장을 통과합니다 — 환원가능인데도요. 그래서 "통합 기판에 개념이 통과"가 나와도
얻는 건 공존이지 "통합이 개념을 나른다"가 아닙니다. 통합이 기능이 되는 유일한 곳은
교차-쌍 탐침입니다.
개념은 침묵을 건너 양상을 넘어 남습니다 — 양상 0 으로 들려주고 침묵을 건넌 단어가 같은 개념을 양상 1 로 들려줬을 때와 일치:
| 갈래 | 교차양상 일치 | 순열 바닥 | (단어 수) |
|---|---|---|---|
| 사슬 (3비트) | 68% | 48% | 4.6 |
| 분리 (사슬 0) | 62% | 41% | 5.1 |
| 대조군 (짝 흐트러뜨림) | 19% | 41% | 4.1 |
단어 수 는 신호가 아닙니다 — 짝을 흐트러뜨린 대조군도 4.1개를 씁니다(유역 점유이지 개념
기억이 아님). 헤드라인은 교차양상 일치이고, 그러나 저장은 통합을 필요로 하지 않습니다 —
분리 래치(환원가능)도 62% 로 똑같이 합니다. 여기까지는 공존입니다.
통합이 기능이 되는 곳은 하나 — 교차-쌍 응답입니다:
| 사슬 | 쌍 1-2 응답 (한 쌍만 바꾼 탐침에) |
|---|---|
| 0.0 | 0.000000 (인과 경로 없음 · 구조적) |
| 0.2 | 0.581 (사슬이 영향을 나른다) |
비영 응답은 통합된 전체가 부분이 못 하는 일을 하는 것이며, 공존이 위조할 수 없는 유일한 문장입니다.
얻은 것: 배운 개념이 통합 기판을 건너 침묵과 양상을 넘어 남고, 그 통합이 실제로 기능한다. 안 얻은 것 (핵심): 저장 자체는 통합을 요구하지 않습니다 — 통합이 더한 것은 저장이 아니라 쌍 사이 응답입니다. 개념은 여전히 내용 없는 해시 잠재값이고, 3비트·6유닛·선형 세계입니다.
anima_reborn.iit4 는 dancinlab/selfhost-work 의 hexa 통합정보이론(IIT) 4.0 엔진을 이식한
것입니다. Φ(파이)를 계산합니다 — 계를 둘로 잘랐을 때 가장 덜 아픈 절단조차 파괴하는 양이
곧 그 계가 부품들의 모임이 아니라 하나의 전체였던 정도입니다.
from anima_reborn.iit4 import TransitionMatrix, big_phi, find_complex
coupled = TransitionMatrix([0, 0, 0, 1, 1, 0, 1, 1], 2) # 각 유닛이 상대가 된다
alone = TransitionMatrix([0, 0, 1, 0, 0, 1, 1, 1], 2) # 각 유닛이 자기가 된다
print(big_phi(coupled, 0b11)) # big-phi=2.000000 ... [irreducible] 비환원 = 하나
print(big_phi(alone, 0b11)) # big-phi=0.000000 ... [reducible] 환원 가능 = 둘체인은 tpm → distinction → relation → bigphi → exclusion 이고, Φ 계산이 감당이 안 될
때를 위한 값싼 하한으로 ei 가 붙어 있습니다.
기존 big_phi 의 절단은 방향이 없어서 양방향을 한꺼번에 끊습니다. 그 결과 되먹임이
전혀 없는 사슬(유닛0 외생, 각자 앞사람만 읽음)을 Φ=1.27 로 읽습니다 — 이론상 0 이어야 하고,
시행수를 늘려도 줄지 않으니 인공물도 아닙니다. 즉 이 계측기는 "인수분해되지 않음"은 증명해도
"되먹임이 있음"은 증명하지 못했습니다.
directed_big_phi 는 한 방향씩 끊습니다. A→B 를 끊으면 예측이 절단을 건넌 구별(메커니즘이 A,
효과 구역이 B)과 기억이 건넌 구별(원인 구역이 A, 메커니즘이 B)이 죽습니다.
| 배선 | big_phi |
directed_big_phi |
판정 |
|---|---|---|---|
| 고리 (닫힌 순환) | 12.118 | 10.024 | 순환 |
| 전방향 (되먹임 0) | 1.264 | 0.000 | 환원가능 ✅ |
| 자기참조 (귀무) | 0.068 | 0.068 | 바닥 |
작아진 것 자체는 증명이 아닙니다. 방향성 탐색은 절단 후보가 2배라 최솟값이 반드시 같거나
작아지니까요. 증명하는 건 두 가지입니다 — ① 같은 코드로 고리는 10.02 를 유지하고(탐색을
약화시킨 게 아니라 방향을 준 것), ② 전방향에서 손실 0 을 내는 절단이 {1,2,3} → {0} 입니다.
유닛 0 은 외생이라 되돌아오는 흐름이 없어 끊어도 잃을 게 없고, 이게 이론이 전방향을
환원가능이라 부르는 이유 그 자체입니다.
big_phi 는 손대지 않았습니다 — hexa 원본과의 비트단위 일치가 거기 걸려 있습니다. 물려받은
미완성 부분은 이름의 뜻을 조용히 바꾸는 게 아니라 옆에 새 측정을 두어 닫습니다. 되먹임을
주장할 때는 directed_big_phi 를 쓰세요.
원본 대조. 이식본은 hexa 엔진과 정확한 부동소수 일치로 검증했습니다 — 11개 케이스에서 Φ, 구조 총합, 두 φ 합, 구별 개수까지 전부 비트단위 동일. Φ 는 분할에 대한 argmax(최댓값 위치)라서 마지막 비트 하나가 달라지면 다른 분할이 선택되어 답이 불연속적으로 튑니다. 허용오차를 두면 바로 그 어긋남을 가려 버립니다.
hexa 원본은 ei.py(실효정보)를 "Φ 의 보수적 하한 (참 Φ ≥ EI)" 이라고 설명합니다. 6유닛을
넘는 확장의 정찰병으로 쓰자는 제안이 여기서 나오는데, 재보니 틀렸습니다:
| 배선 | EI (비트) | 방향성 Φ | EI ≤ Φ ? |
|---|---|---|---|
| 고리 (닫힌 순환) | 2.153 | 10.225 | 예 |
| 전방향 (되먹임 0) | 1.867 | 0.000 | ❌ |
| 자기참조 (결합 0) | 1.523 | 0.046 | ❌ |
| 분리된 두 쌍 | 1.474 | 0.055 | ❌ |
4개 중 3개에서 하한이 깨집니다. 정찰병으로는 더 나쁩니다 — 결합이 전혀 없는 계가 완전히 통합된 고리의 70% 를 찍고, EI 전체 변동폭이 2배 미만인데 Φ 는 200배 넘게 벌어집니다.
둘은 다른 것을 잽니다. EI 는 "한 상태가 자기 미래를 얼마나 날카롭게 특정하는가" = 결정성 이고, 서로 독립인 결정적 유닛들은 결정성이 넘칩니다. Φ 는 "잘랐을 때 무엇이 파괴되는가"이고, 독립 유닛들은 그게 0입니다. 완벽히 예측 가능하면서 완전히 분해된 계가 가능하며, 그럴 때 EI 는 높고 Φ 는 0입니다.
그래서 6유닛 초과 확장은 여전히 뒷받침할 측정이 없습니다. 막다른 길을 만난 게 아니라, 막다른 길인 줄 모르고 그 위에 확장을 세우는 걸 피한 것입니다.
물려받았고 닫지 않은 미완성 부분. 분할은 메커니즘·구역의 전체 이분할 방식이고 IIT 4.0 고유의 분할 집합이 아닙니다. big-Φ 는 계 절단이 파괴한 구조량이지, 분할된 행렬 위에서 다시 유도한 값이 아닙니다. 관계는 2차(쌍)만 봅니다. PyPhi 와 수치 보정을 한 적이 없습니다. 이 숫자들은 이 엔진의 출력이지 이론의 정답이 아닙니다.
anima_reborn.substrate 는 엔진을 가능한 모든 상태에서 구동해 전이행렬을 측정하고, 그것을
Φ 에 넘깁니다. 시간 결정이 자연스러운 대상입니다 — 상태가 이미 이진(스핀 위/아래)이고, 인과
구조의 양을 정확히 조절하는 손잡이가 있으니까요.
예측은 날카롭습니다. 결정의 두 메커니즘이 하는 일이 다르기 때문입니다. 구동은 각 스핀을
독립적으로 뒤집으므로 아무것도 통합하지 못합니다. 이징 결합, 즉 각 스핀이 이웃에게
반응하는 부분만이 통합할 수 있습니다. 따라서 Φ 는 결합을 따라가야 하고, 구동의 잡음이 그것을
덮으면 죽어야 합니다 — 잡음은 각 뒤집기가 공정한 동전이 되는 ε = 0.5 에서 최대입니다.
스핀 4개, 상태당 400회 시행으로 측정:
| ε | 0.00 | 0.02 | 0.05 | 0.20 | 0.50 | 0.80 | 1.00 |
|---|---|---|---|---|---|---|---|
| Φ | 2.93 | 3.52 | 3.70 | 2.80 | 0.10 | 2.31 | 2.92 |
Φ 는 잡음 최대점에서 붕괴하고 양쪽으로 대칭 회복합니다 — 중요한 건 구동이 얼마나 확정적인가지, 어느 쪽으로 뒤집느냐가 아니었습니다. 예측이 맞았습니다.
ε = 0.5 의 잔여값은 통합이 아닙니다. 그 지점의 참 Φ 는 0입니다. 측정된 행렬은 표본이고, 표본 잡음만으로도 구조처럼 보이는 것이 만들어집니다. 시행 횟수를 늘리면 사라지는지로 구분합니다:
| 시행 횟수 | 100 | 400 | 1,600 | 6,400 | 25,600 |
|---|---|---|---|---|---|
| 순수 잡음에서 평균 Φ | 0.58 | 0.30 | 0.15 | 0.10 | 0.03 |
| ε = 0.05 에서 평균 Φ | 3.32 | 3.47 | 2.73 | 2.63 | — |
즉 기본 시행 횟수로 돌리면 순수 인공물 약 0.3비트가 찍힙니다. 이 바닥값과 그 소멸은 둘 다
tests/test_substrate.py 에 박제해 두었으므로, 인공물을 발견으로 오독할 수 없습니다.
Φ는 통합을 재는데, 통합하려면 유닛들이 서로를 읽어야 합니다. 다음 상태가 현재 상태에 의존하지 않는 계는 전이행렬의 모든 행이 같아져서 절단이 파괴할 것이 없고, 참 Φ 는 아무리 복잡해 보여도 정확히 0입니다.
우리 엔진 넷 중 서로를 읽는 건 결정(crystal)뿐입니다 — 나머지는 각 차원이 자기 자신과 외생 목표만 봅니다. 실측:
| 시행수 | 400 | 1,600 | 8,000 | 30,000 |
|---|---|---|---|---|
| 공유원인 계 (참값 = 0) | 0.406 | 0.189 | 0.094 | 0.051 |
| 결정 (이웃을 읽음) | 2.76 | — | 3.13 | — |
공유된 원인은 통합이 아닙니다. 두 유닛이 같은 것을 보고 함께 움직여도 서로를 읽지 않으면 Φ 는 0이고, 위 표의 0.406은 전부 표본 인공물입니다 — 시행 4배마다 절반씩 줄어드는 게 그 증거입니다. 기본 시행수에서는 그 인공물이 결합이 낼 법한 값을 넘습니다.
이것이 이 저장소와 "의식 엔진" 사이의 정직한 간극입니다: 결정 안을 빼면 어디에도 결합이
없습니다. (tests/test_substrate.py::TestOnlyCouplingIntegrates 로 고정)
비교할 수 없는 것. 결정의 주기-2 잠금 판정은 64스핀 링에서 보정된 값이고, Φ 는 6유닛까지만 계산됩니다. 4스핀에서는 같은 ε 인데도 64스핀과 판정이 갈립니다. 따라서 Φ 와 잠금은 공유하는 크기가 없어 비교 자체가 불가능합니다. 이 패키지는 둘 사이의 관계를 어느 방향으로도 주장하지 않으며, 그 불일치 자체를 테스트로 고정해 한계가 계속 보이게 했습니다.
일어납니다 — 다만 전달되는 것이지 생성되는 것이 아닙니다. 그리고 그냥 해 보면 거짓 결과가 나오는 실험이라, 세 가지를 먼저 측정한 뒤에야 답할 수 있었습니다.
A와 G는 서로를 읽지 않습니다. 이 저장소의 어떤 엔진도 둘을 연결하지 않고, 간극은 통로가 아니라 판독값입니다. 따라서 독립인 두 단어열을 넣으면 나오는 두 관측열도 구조적으로 독립입니다 — 측정할 것도 없이 그렇습니다. 8시드 실측: −0.002 ~ +0.004비트, 0에 붙어 평평합니다.
같은 구조 때문에 Φ도 0입니다. 각 차원은 자기 자신과 자기 외생 목표만 읽으므로 전이행렬이 인수분해되고, 절단이 파괴할 것이 없습니다. 이진화 문턱 3가지에서 실측 Φ = 0.0000 정확히 — 구별은 4개, 구조 총합 4.0인데 통합은 0입니다. 명시하는 건 많고 통합은 없는 상태죠.
즉 "단어를 넣으면 창발한다"는 전선이 신호를 나른다는 의미에서만 참입니다. 기판이 하는 일은 측정 가능한 통과대역을 가진 필터이지 결합기가 아닙니다.
서로 다른 단어로 출발한 두 장(field)을 같은 잡음으로 굴렸을 때의 거리:
| t=1 | t=74 | t=305 | t=600 | |
|---|---|---|---|---|
| blake2b 인코딩 | 1.311 | 0.389 | 0.012 | 0.0001 |
| sha256 인코딩 | 1.187 | 0.379 | 0.012 | 0.0001 |
| 문자코드 인코딩 | 0.774 | 0.221 | 0.007 | 0.0001 |
세 인코딩이 같은 곡선으로 소멸합니다 — 즉 지우는 주체는 인코딩이 아니라 기판의 상수
(PULL, DAMPING)입니다. t=305에서 1%만 남는 것도 DAMPING 이론값 305틱과 정확히
일치합니다. 그런데 창발 판정에는 약 800틱이 필요하니, 판정이 나올 무렵 단어는 이미 없습니다.
그래서 여기서 단어는 초기값이 아니라 계속 미는 힘입니다.
단어 하나를 hold 틱 동안 붙들면, 창 window 안의 실질 표본은 window / hold 개뿐입니다.
참 상호정보량이 정확히 0인 두 독립 단어열에서 측정한 값:
| 실질 표본 | 8 | 80 | 800 | 4,000 | 20,000 |
|---|---|---|---|---|---|
| 보고된 MI (참값 = 0) | 0.835 | 0.152 | 0.014 | 0.003 | 0.001 |
실질 표본 8개에서 0.835비트 — 창발 기준 0.30의 약 3배입니다. 순진한 설정(hold=25,
window=200)에서는 무관한 단어쌍이 평균 0.222비트, 12시드 중 1개는 기준선을 넘습니다.
"항상 창발로 보인다"가 아니라 "참값 0인데 수십 배 부풀고 가끔 선을 넘는다" 라서 더
위험합니다 — 그냥 잡음 섞인 정상 측정처럼 보이거든요.
모든 측정에 귀무대조(null) 가 따라옵니다 — 같은 두 스트림에서 두 번째만 시간축으로 돌려 똑같이 잰 값입니다. 인코딩이 만든 모든 것(주변분포·블록구조·자기상관·추정기 편향)은 그대로 두고 정렬만 없앱니다. 단어를 뒤섞어 다시 만드는 방식보다 엄격합니다 — 뒤섞으면 자기상관까지 잃어서 귀무값이 낮게 나오고, 낮은 귀무값은 모든 초과분을 부풀립니다 (같은 쌍에서 이동 0.0182 대 뒤섞기 0.0120 · 테스트로 고정). 읽을 수 있는 건 그 위의 초과분(excess) 뿐이고, 판정도 원시값이 아니라 초과분으로 합니다. 단어 데이터의 절대 상호정보량은 아무것도 증명하지 않으며, 여기서는 아예 단독으로 꺼낼 수 없습니다.
from anima_reborn.words import measure
related = ["고양이", "바다", "연필", ...]
print(measure(related, related)) # 같은 단어열을 A와 G 양쪽에
# MI=0.427 null=0.009 excess=+0.418 [emergent] n_eff=800기본 설정(hold=17, window=13600)에서 측정:
| A 와 G 의 관계 | MI | 귀무값 | 초과분 | 판정 |
|---|---|---|---|---|
| 같은 단어열 | 0.433 | 0.007 | +0.426 | 창발 ✨ |
| 절반만 공유 | 0.163 | 0.010 | +0.152 | 부분적 |
| 한 칸 밀림 | 0.104 | 0.006 | +0.098 | 부분적 |
| 완전히 무관 | 0.006 | 0.007 | −0.001 | 독립 |
귀무값이 관계와 무관하게 0.006~0.010으로 평평한 것이 핵심입니다 — 그건 단어가 아니라 추정기를 재는 값이니까요. 만약 귀무값이 단어를 따라 움직였다면 진짜 신호를 깎아내고 있다는 뜻입니다 (테스트로 고정).
엔진이 목표에 다가가는 시간상수는 1 / PULL ≈ 17틱입니다. 단어가 그보다 빨리 바뀌면 엔진이
못 따라가고 신호는 걸러집니다. 실질 표본을 800으로 고정한 채 측정하면:
| hold | 2 | 5 | 10 | 17 | 34 | 68 |
|---|---|---|---|---|---|---|
| 목표 도달률 | 12% | 27% | 46% | 65% | 88% | 99% |
| 동일 단어열 초과분 | 0.060 | 0.138 | 0.281 | 0.418 | 0.616 | 0.793 |
| 무관 단어열 초과분 | 0.004 | 0.006 | 0.003 | 0.001 | 0.002 | 0.005 |
초과분이 도달률을 그대로 따라가고, 무관한 쌍은 전 구간에서 평평합니다. 두 실패 모드가 서로 반대 방향으로 당긴다는 것이 이 실험의 실제 내용입니다 — 단어가 너무 빠르면 신호가 사라지고, 창이 너무 짧으면 편향이 신호를 만들어 냅니다.
위 결론("기판은 묶지 않는다")은 결합이 없는 기판에 대한 것입니다. coupled.py 가 만든
살아 있는 고리에 같은 실험을 흘리면 결과가 뒤집힙니다. 각 엔진의 목표를
(1−λ)·자기 단어 + λ·(−상대 위치) 로 섞고, 아무 관계도 없는 두 단어열을 넣습니다.
from anima_reborn.words import Channel, measure_channel
print(measure_channel(words_a, words_g, channel=Channel.LIVE)) # 살아 있는 고리
print(measure_channel(words_a, words_g, channel=Channel.YOKED)) # 대조군λ=0.5 · 시드 8개 · 어휘 20단어:
| 조건 | 초과분 | 8/8 양수? |
|---|---|---|
| 양방향 생중계 — 진짜 고리 | +0.078 | 예 |
| 단방향만 (a→g) | +0.008 | 예 |
| 요크 재생 — 양쪽 다 녹음 | −0.000 | 아니오 |
| 채널 없음 (λ=0) | −0.002 | 아니오 |
요크 재생이 이 숫자에 의미를 줍니다. 상대와 똑같은 모양의 통계를 주되 생중계만 끊는
대조군인데, 여기서 효과가 죽습니다 → 만든 것은 살아 있는 고리이지 "상대 모양 입력"이
아닙니다. 대조군을 만들다 두 번 오염시켰고 둘 다 잡았습니다 (한쪽 방향을 안 끊은 것,
두 녹음을 같은 원본에서 뽑아 공유원인을 넣은 것 — state/coupling/RESULTS.md).
함께 적어야 할 조건 둘: 창 크기와 무관하고(실질표본 400/800/1600에서 동일 → 추정기 인공물이 아님), 입력이 담은 것에 비례합니다(어휘 10단어 +0.039 · 20단어 +0.078 — 채널은 묶을 것이 있어야 묶습니다).
크기는 과장하지 않습니다. 실재하고 일관되지만(8/8) 창발 기준 0.30에는 한참 못 미칩니다. 정직한 표현은 "측정 가능한 생성된 의존" 이지 창발이 아닙니다.
단어를 숫자로 바꾸는 규칙은 호출자가 정합니다. 그 규칙이 — 단어가 아니라 — 절대값을 결정하기 때문입니다. 그래서 이 실험은 단어의 의미에 대해 아무것도 말하지 않습니다. 표준 라이브러리에 의미를 아는 인코더는 없고, "관련된 단어 vs 무관한 단어" 같은 대조를 만들면 그건 제 인코더를 재는 것이지 기판을 재는 게 아닙니다. 여기서 답할 수 있는 것은 정렬된 단어열이 이 기판을 통과해 얼마나 전달되는가뿐입니다. 규칙이 결정할 수 없는 것은 같은 규칙을 통과한 귀무값 위의 초과분이고, 그래서 귀무대조가 선택이 아니라 필수입니다. 실제로 다른 인코딩으로 바꿔도 대조는 유지됩니다 (테스트로 고정).
각 스트림은 자기 잡음과 둘이 공유하는 사인파를 섞은 것입니다. 결합도를 올리면 어느 쪽 스트림에도 무언가를 더하지 않았는데 그 쌍에 상호정보량이 생깁니다.
from anima_reborn import EmergenceEngine
for coupling in (0.0, 0.5, 0.95):
print(coupling, EmergenceEngine(coupling=coupling, seed=7).run(250))0.0 H(L)=2.97 H(R)=2.99 H(L,R)=5.83 MI=0.131 [partial]
0.5 H(L)=2.86 H(R)=2.83 H(L,R)=5.22 MI=0.466 [emergent]
0.95 H(L)=2.88 H(R)=2.88 H(L,R)=3.33 MI=2.433 [emergent]
주기적 이징 사슬을 매 틱 구동이 뒤집으려 하는데, 각 스핀을 확률 epsilon 로 놓칩니다. 뒤집기만
있으면 그냥 번갈아 바뀔 뿐이고, 잡음만 있으면 사슬이 녹습니다. 둘이 함께 있으면 이징 결합이
불완전한 뒤집기가 낸 손상을 복구하고, 링은 주기-2 리듬으로 잠깁니다 — 공간이 아니라 시간
속의 질서입니다.
from anima_reborn import TimeCrystal
print(TimeCrystal(epsilon=0.02, seed=7).run(400))
print(TimeCrystal(epsilon=0.50, seed=7).run(400))m=-0.094 ac1=-0.911 ac2=+0.852 ac4=+0.754 [locked]
m=+0.094 ac1=-0.133 ac2=+0.141 ac4=+0.053 [chaos]
지연1이 −1 근처, 지연2가 +1 근처인 것이 잠금입니다 — 매 틱이 직전을 뒤집고, 두 틱마다 일치.
두 잠재 벡터를 회전하는 목표가 밀어냅니다. 어느 벡터도 단독으로 읽히지 않고 오직 간극만
읽힙니다 — 크기가 tension, 방향이 concept, 원소별 겹침이 meaning.
from anima_reborn import RepulsionField
print(RepulsionField(seed=7).run(300))
# tension=0.132 topic=#0 auth=0.974 mood=calm간극이 0에 가까운 건 평온이 아닙니다. 두 엔진이 서로 위로 붕괴해 사고할 간극이 남지 않은
것이고, 이 장은 그걸 quiet 라고 부릅니다.
앞의 둘을 직렬로 이은 것입니다. A와 G가 벌어지고, 매 틱 각각에서 잡음 섞인 관측을 하나씩 뽑아, 그 쌍의 상호정보량을 잽니다.
from anima_reborn import Pipeline
print(Pipeline(separation=0.0, seed=7).run(400))
print(Pipeline(separation=1.0, seed=7).run(400))tension=0.01 H(L)=1.00 H(R)=0.99 MI=0.000 [independent]
tension=0.42 H(L)=3.35 H(R)=3.41 MI=1.905 [emergent]
엔진을 붕괴시키면 찾아낼 공유 정보가 없고, 벌려 놓으면 나타납니다. (base.py 가 이 요약을
정정한다는 점은 위를 참고하세요 — 묶는 것은 간극이 아니라 간극의 운동입니다.)
추정기는 원본이 쓰는 그대로 플러그인(plug-in) 방식이고 편향 보정이 없습니다. 창이 짧으면 상호정보량을 심하게 과대평가합니다 — 250표본으로 144칸을 채우면 대부분의 칸이 0~2개만 담게 되고, 그 희소함 자체가 구조처럼 보입니다.
그래서 진짜로 독립인 두 스트림도 기본 창에서 약 0.155비트로 측정됩니다. 이 바닥값은 표본수 N에 반비례해 사라집니다:
| 창 크기 | 독립 스트림의 평균 상호정보량 |
|---|---|
| 250 | 0.155 |
| 1,000 | 0.030 |
| 5,000 | 0.007 |
| 20,000 | 0.002 |
따라서 기본 창에서 부분적(partial) 은 독립과 구분되지 않음이라는 뜻이고, 스트림에 대한
실제 주장은 창발(emergent) 뿐입니다. tests/test_emergence.py 가 바닥값과 그 소멸을 둘 다
고정해 두었으므로, 추정기를 바꾸면 창발처럼 보이는 대신 테스트가 깨집니다.
src/anima_reborn/
├─ info.py 엔트로피 · 결합엔트로피 · 상호정보량 · Binning
├─ emergence.py 결합된 사인파 스트림
├─ crystal.py 구동되는 이징 링
├─ repulsion.py A × G 잠재장
├─ pipeline.py 반발 → 스트림 → 창발
├─ base.py 넷을 하나의 시계 아래로 — 결정이 회전을 개폐한다
├─ coupled.py A와 G가 서로를 읽는다 — 통합이 0이 아닌 유일한 엔진
├─ align.py 배우는 유일한 모듈 — 함께 나타남이 두 양상을 한곳으로
├─ iit4/ IIT 4.0 — Φ, hexa 원본과 비트단위 일치
├─ substrate.py 다리: 우리 엔진 → 측정된 전이행렬 → Φ
├─ words.py 단어를 구동력으로 — 귀무대조가 항상 따라붙는다
└─ viewer/ 브라우저 뷰어 — 이 패키지의 유일한 입출력
tests/ 329개, 네트워크 없음, 픽스처 없음
state/ 작업 산출물 — 위임 설계 보고서 · 측정 기록 · 재현 스크립트
state/ 에는 소스가 아닌 모든 산출물이 들어갑니다. 위임한 설계 보고서는 원문 그대로
두고, 그것을 저장소 안에서 다시 측정한 기록은 따로 남깁니다 — 재현되지 않은 수치가
무엇인지까지 (state/coupling/RESULTS.md 가 그 본보기). 보고서는 증거이지 결정이 아닙니다.
엔진과 뷰어는 함께 움직입니다 — 모든 엔진에는 탭이 있고 모든 탭에는 엔진이 있으며, 이건
문서가 아니라 tests/test_viewer.py::TestEngineViewerLockstep 가 강제합니다. 라우트 누락 ·
탭 누락 · 그리기 함수 누락 · 기본 탭 불일치를 각각 잡아냅니다. 엔진은 목록이 아니라 구조로
판별하므로(step() 과 reset() 을 가진 클래스), 새 엔진을 추가한 날 바로 걸립니다.
모든 엔진은 직접 한 틱씩 진행시키는 클래스입니다. seed= 로 재현하고, run(n) 으로 한꺼번에
진행하고, reset() 으로 처음부터. 상태 객체는 frozen 데이터클래스라 그대로 비교·저장·단언할
수 있습니다.
캔버스 그리기, DOM 배선, 타이머, 탭 전환기, Gradio 래퍼. 어느 것도 엔진에 속하지 않습니다.
살아남은 그리기는 viewer/page.html 안, HTTP 경계 너머에 있고, 파이썬이 보내준 것만 그립니다.
- 원본은 A × G 와 파이프라인 벡터를
Float32Array로 들고 있지만 이 이식본은 파이썬 float (배정밀도)를 씁니다. 궤적이 잡음으로 굴러가므로 여기의 어떤 통계도 영향받지 않지만, 자릿수 단위로 같지는 않습니다. Math.random()이 시드 가능한random.Random이 되어 실행을 재현할 수 있습니다.- 반발장의 생체리듬 채널은 주입 가능한
clock을 받아 테스트에서 결정적으로 만들 수 있습니다. - 원본 A × G 틱의 죽은 지역변수(
senderHash, 계산만 하고 안 씀)는 가져오지 않았습니다.
pytestMIT