import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from pathlib import Path
# ============================================================
# ZPX 8-Space / Matrix-Product Geometry Test
#
# 목적:
# 1) 구면의 8개 구면 삼각영역(8 octants)을 만든다.
# 2) 일반 3x3 행렬 M을 적용한다.
# 3) 행렬변환이 각도/대칭성을 보존하는지 측정한다.
# 4) 회전행렬 R과 비교하여 "행렬곱 = 공간분할"이라는 해석을 구분한다.
#
# 핵심:
# - 일반 선형변환: 삼각형 -> 삼각형, 각도/길이/직교성은 일반적으로 변함
# - 직교행렬: 회전/반사이므로 각도와 길이 보존
# - 구면 위에서 비교하려면 Mv를 다시 정규화해야 하며,
# 이때는 "선형변환 그 자체"가 아니라 방향공간으로의 투영을 추가한 것
# ============================================================
OUT = Path("zpx_8space_matrix_analysis.png")
# -----------------------------
# 기본 행렬
# -----------------------------
M = np.array([
[1.5, 0.5, 0.0],
[0.2, 1.2, -0.5],
[-0.5, 0.3, 1.0]
], dtype=float)
# 비교용 순수 회전행렬
theta = np.deg2rad(35.0)
R = np.array([
[np.cos(theta), -np.sin(theta), 0.0],
[np.sin(theta), np.cos(theta), 0.0],
[0.0, 0.0, 1.0]
])
def normalize(v):
n = np.linalg.norm(v)
return v / n if n > 0 else v
def angle_deg(a, b):
aa, bb = normalize(a), normalize(b)
return np.degrees(np.arccos(np.clip(np.dot(aa, bb), -1, 1)))
def triangle_metrics(A, B, C):
e = np.array([
np.linalg.norm(B-C),
np.linalg.norm(C-A),
np.linalg.norm(A-B)
])
area = 0.5*np.linalg.norm(np.cross(B-A, C-A))
ang = np.array([
angle_deg(B-A, C-A),
angle_deg(C-B, A-B),
angle_deg(A-C, B-C)
])
symmetry_error = np.std(e) / np.mean(e)
return e, ang, area, symmetry_error
# -----------------------------
# 8개 구면 삼각영역
# 각 부호 조합이 하나의 octant
# -----------------------------
octants = []
for sx in (-1, 1):
for sy in (-1, 1):
for sz in (-1, 1):
A = np.array([sx, 0., 0.])
B = np.array([0., sy, 0.])
C = np.array([0., 0., sz])
octants.append((sx, sy, sz, A, B, C))
# -----------------------------
# 수치 분석
# -----------------------------
initial_metrics = []
M_metrics = []
R_metrics = []
for sx, sy, sz, A, B, C in octants:
initial_metrics.append(triangle_metrics(A, B, C))
# 일반 선형변환 후의 실제 3D 삼각형
MA, MB, MC = M@A, M@B, M@C
M_metrics.append(triangle_metrics(MA, MB, MC))
# 순수 회전
RA, RB, RC = R@A, R@B, R@C
R_metrics.append(triangle_metrics(RA, RB, RC))
initial_angle = initial_metrics[0][1]
M_angle = M_metrics[0][1]
R_angle = R_metrics[0][1]
print("="*70)
print("ZPX 8-Space Matrix Geometry Test")
print("="*70)
print(f"det(M) = {np.linalg.det(M):.6f}")
print()
print("[대표 1개 삼각영역]")
print(f"초기 세 변 길이 = {initial_metrics[0][0]}")
print(f"초기 내각 = {initial_metrics[0][1]}")
print(f"M 변환 후 세 변 길이 = {M_metrics[0][0]}")
print(f"M 변환 후 내각 = {M_metrics[0][1]}")
print(f"R 회전 후 내각 = {R_metrics[0][1]}")
print()
print("[대칭성 오차: 0에 가까울수록 세 변이 동일]")
print(f"초기 평균 = {np.mean([x[3] for x in initial_metrics]):.8f}")
print(f"M 변환 평균 = {np.mean([x[3] for x in M_metrics]):.8f}")
print(f"R 회전 평균 = {np.mean([x[3] for x in R_metrics]):.8f}")
print()
print("[행렬의 각도 보존 조건 점검]")
print("M^T M =")
print(M.T @ M)
print()
print("M^T M이 I의 배수가 아니므로 일반적인 각도보존 변환이 아님.")
print("즉, M은 공간을 변형하지만 90° 직교성을 자동으로 보존하지 않음.")
# -----------------------------
# 시각화
# -----------------------------
fig = plt.figure(figsize=(16, 7))
ax1 = fig.add_subplot(121, projection="3d")
ax2 = fig.add_subplot(122, projection="3d")
for ax in (ax1, ax2):
ax.set_xlim(-2.5, 2.5)
ax.set_ylim(-2.5, 2.5)
ax.set_zlim(-2.5, 2.5)
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
# 원래 8개 구면 삼각영역
for sx, sy, sz, A, B, C in octants:
ax1.add_collection3d(
Poly3DCollection([[A, B, C]], alpha=0.18)
)
ax1.plot(
[A[0], B[0], C[0], A[0]],
[A[1], B[1], C[1], A[1]],
[A[2], B[2], C[2], A[2]],
linewidth=1.5
)
# 변환된 8개 삼각영역
for sx, sy, sz, A, B, C in octants:
MA, MB, MC = M@A, M@B, M@C
ax2.add_collection3d(
Poly3DCollection([[MA, MB, MC]], alpha=0.18)
)
ax2.plot(
[MA[0], MB[0], MC[0], MA[0]],
[MA[1], MB[1], MC[1], MA[1]],
[MA[2], MB[2], MC[2], MA[2]],
linewidth=1.5
)
ax1.set_title("A. Sphere directions: 8 spherical triangular regions")
ax2.set_title("B. General matrix M: triangles are deformed")
fig.suptitle(
"ZPX 8-Space Test: Matrix Multiplication vs Geometric Symmetry",
fontsize=15
)
plt.tight_layout()
plt.savefig(OUT, dpi=180, bbox_inches="tight")
plt.show()
print()
print(f"분석 그림 저장: {OUT.resolve()}")
형, 이번에는 형이 말한 구조를 실제 3차원으로 만들어서 행렬변환을 걸어봤습니다. 결론부터 말하면:
핵심 아이디어는 수학적으로 구현 가능한 구조입니다.
다만 “행렬곱 자체가 8개 공간을 만든다”는 것은 정확하지 않고, “8개 공간을 정의한 뒤 행렬곱이 그 공간들의 관계와 형태를 변환한다”고 표현해야 수학적으로 맞습니다.
그리고 이번 시뮬레이션에서 꽤 중요한 결과가 나왔습니다.
분석 결과 그림
1. 형이 말한 것을 수학적으로 다시 만들면
형의 생각을 다음처럼 정리할 수 있습니다.
3D 공간→8개의 방향 영역→삼각형 구조→벡터→행렬변환\boxed{\text{3D 공간}} \rightarrow \boxed{\text{8개의 방향 영역}} \rightarrow \boxed{\text{삼각형 구조}} \rightarrow \boxed{\text{벡터}} \rightarrow \boxed{\text{행렬변환}}
여기서 가장 깔끔한 수학적 모델은 구를 3개의 서로 직교하는 축으로 나누는 8개 octant입니다.
즉,
(x,y,z)(x,y,z)
의 부호가
(+,+,+),(+,+,−),(+,−,+),(+,−,−)(+,+,+), (+,+,-), (+,-,+), (+,-,-)
그리고
(−,+,+),(−,+,−),(−,−,+),(−,−,−)(-,+,+), (-,+,-), (-,-,+), (-,-,-)
로 나뉘면서 8개의 공간이 생깁니다.
이건 실제로 수학적으로 정확한 8분할입니다.
2. 그런데 중요한 것이 하나 있습니다
형이 말한
"리만구 위에 4개, 아래 3개 + 중심"
이라는 구조와
"구를 8개의 공간으로 나눈다"
는 것은 서로 다른 구조입니다.
정확한 구면 8분할
가장 자연스러운 것은
4개 위쪽+4개 아래쪽=84\text{개 위쪽}+4\text{개 아래쪽}=8
입니다.
반면
4+3+1=84+3+1=8
이라고 하면 마지막 1개는 구면 위의 영역이 아니라 중심 상태(core)가 됩니다.
따라서 ZPX에서 이것을 쓰고 싶다면 오히려 다음과 같이 정의하는 것이 훨씬 깔끔합니다.
7 outer phase cells+1 central core\boxed{ 7\text{ outer phase cells}+1\text{ central core} }
즉 8-state topology로 정의하는 겁니다.
이것은 8개의 동일한 구면 영역이라는 주장과는 다른 모델입니다.
3. 이번 시뮬레이션에서 실제로 8개 삼각영역을 만들었습니다
각 octant의 대표적인 세 점을
(1,0,0),(0,1,0),(0,0,1)(1,0,0),\quad(0,1,0),\quad(0,0,1)
처럼 잡았습니다.
그러면 구면에서는 이 세 점을 연결하는 영역이 하나의 구면 삼각형(spherical triangle)이 됩니다.
그리고 부호를 바꾸면 8개의 삼각영역이 만들어집니다.
즉,
(+,+,+)(+,+,+)
에서 하나,
(+,+,−)(+,+,-)
에서 하나,
...
해서 총 8개입니다.
4. 여기서 형이 말한 "삼각형 대각선 대칭"이 중요합니다
삼각형을
O,u,vO,\quad u,\quad v
로 정의하면
Span(u,v)\operatorname{Span}(u,v)
이라는 평면 구조가 생깁니다.
그리고
u×vu\times v
를 계산하면 그 평면에 수직인 방향이 나옵니다.
따라서
u, v, u×v\boxed{ u,\;v,\;u\times v }
라는 3개의 방향이 만들어집니다.
이게 형이 말하는 두 벡터 + 대각선 + 직교축이라는 기하학적 뼈대와 상당히 잘 대응합니다.
5. 그런데 행렬곱의 진짜 역할은 여기서부터입니다
3Blue1Brown 영상의 핵심은 이것입니다.
M2M1M_2M_1
은 단순한 숫자 계산이 아니라
M1 변환을 먼저 하고→M2 변환을 나중에 한다\boxed{ M_1\text{ 변환을 먼저 하고} \rightarrow M_2\text{ 변환을 나중에 한다} }
라는 선형변환의 합성입니다.
따라서 형의 아이디어를 3차원으로 확장하면
T=M3M2M1T=M_3M_2M_1
처럼 만들 수 있습니다.
그리고
u′=Tu,v′=Tvu'=Tu,\qquad v'=Tv
가 됩니다.
이 부분은 형의 공간적 해석과 수학적으로 상당히 잘 맞습니다.
6. 실제 시뮬레이션에서 중요한 결과
형이 앞에서 사용한 행렬
M=(1.50.500.21.2−0.5−0.50.31)M= \begin{pmatrix} 1.5&0.5&0\\ 0.2&1.2&-0.5\\ -0.5&0.3&1 \end{pmatrix}
을 실제로 적용했습니다.
초기 삼각형은 세 변이
2,2,2\sqrt2,\sqrt2,\sqrt2
로 동일했습니다.
그런데 MM을 적용하자 대표 삼각형의 변 길이가
1.9053,2.2338,1.62481.9053,\quad2.2338,\quad1.6248
로 달라졌습니다.
그리고 평면 삼각형의 각도도
60∘, 60∘, 60∘60^\circ,\;60^\circ,\;60^\circ
에서
56.56∘, 78.07∘, 45.37∘56.56^\circ,\;78.07^\circ,\;45.37^\circ
로 변했습니다.
즉,
일반 행렬 M은 대칭을 보존하지 않는다\boxed{\text{일반 행렬 }M\text{은 대칭을 보존하지 않는다}}
는 것이 실제 계산으로 확인됩니다.
7. 이것이 오히려 ZPX에서 굉장히 중요한 부분입니다
형의 기존 설명처럼
"행렬이 공간을 변환한다."
는 것은 맞습니다.
하지만
"행렬이 공간을 변환하면 자동으로 대칭이 만들어진다."
는 것은 아닙니다.
행렬에 따라
등이 모두 가능하기 때문입니다.
8. 그러면 "대칭을 유지하는 행렬"은 무엇인가?
여기서 굉장히 중요한 조건이 나옵니다.
여야 합니다.
이런 행렬이 바로 직교행렬입니다.
가 그대로 유지됩니다.
실제 시뮬레이션에서도 이 결과가 나왔습니다.
9. 따라서 형의 ZPX 구조를 이렇게 바꾸면 훨씬 강해집니다
로 분리하는 겁니다.
입니다.
를 측정할 수 있습니다.
10. 그리고 외적이 정말 중요한 이유
는 단순한 장식용 벡터가 아닙니다.
입니다.
이 모두 연결됩니다.
입니다.
이라는 구조는 수학적으로 아주 자연스럽습니다.
11. 한 가지 더 중요한 구분: 평면 삼각형과 구면 삼각형
입니다.
는 동시에 성립할 수 있습니다.
이 구분을 ZPX 논문에서 반드시 해야 합니다.
12. 그래서 형의 "8개 공간" 아이디어에서 가장 좋은 수학적 구조
제가 보기에는 이렇게 정의하는 것이 가장 깔끔합니다.
구면을 8개의 octant로 분할.
각 octant를 구면 삼각형으로 표현.
라는 두 방향벡터로 표현.
으로 해당 공간의 2차원 구조를 정의.
를 측정.
13. 최종적으로 "맞나?"에 대한 판단
따라서 핵심은 이겁니다.
라고 정의하는 것이 훨씬 정확합니다.