아래 내용은 M. J. de Oliveira가 1992년 Journal of Statistical Physics 66권에 게재한 논문(“Isotropic Majority-Vote Model on a Square Lattice”) 중 일부를 발췌한 것으로서, 이른바 등방적(isotropic) majority vote 모델이 2차원 정사각 격자에서 임계 특성이 어떻게 나타나는지를 몬테카를로 시뮬레이션과 유한 크기 스케일링(finite-size scaling)을 통해 밝히는 연구입니다. 논문의 핵심은 평형 상태가 아닌(비(非)자세한균형, 즉 detailed balance 불만족) 확률적(spin-flip) 동역학이지만, Ising 모형과 같은 보편적 임계 거동(universal critical behavior)을 공유한다는 점을 수리적/수치적으로 보여주는 것입니다. 본문을 꼼꼼히 살피면서, 식(수식), 개념, 원리를 최대한 자세하고 친절하게 설명해보겠습니다.
논문에서는 “majority-vote 모델”이라고 불리는 확률적 동역학 스핀 모형을 다룹니다. 이 모델은 아래와 같은 특징이 있습니다.
다음과 같은 물리학적 혹은 통계역학적 관심사가 있습니다:
이 논문에서는 몬테카를로 시뮬레이션 및 유한크기 스케일링 이론을 적용해서, 비평형 모델이지만 고전적인 2차원 등방성 Ising 모형((d=2))과 동일한 임계 지표(critical exponent)를 가진다는 점을 수치적으로 보여줍니다.
논문에서는 (\displaystyle q)를 “노이즈 파라미터(noise parameter)”라고 부르며, (q)가 크면 스핀이 다수결을 잘 따르지 않고(소수결을 따를 확률 높음), (q=0)이면 무소음(완전히 다수결만 따름)이 됩니다.
이 규칙을 확률적 전이율(transition rate) (w_i(\sigma))로 표현하면:
[
wi(\sigma) \;=\; \frac{1}{2} \Biggl[\, 1 - S\Bigl(\sum{j \in \text{nbr}(i)} \sigmaj\Bigr) \, (2\sigma_i)\, \Biggr] \, q \;+\; \frac{1}{2} \Bigl[\, 1 + S\bigl(\sum{j \in \text{nbr}(i)} \sigma_j\bigr) \, (2\sigma_i)\,\Bigr] \, p \,,
]
혹은 간략히,
[
wi(\sigma)
= \frac{1}{2}\Bigl[1 - \sigma_i\, S!\bigl(\sum{j}\sigmaj\bigr)\Bigr]\,q
\;+\; \frac{1}{2}\Bigl[1 + \sigma_i\, S!\bigl(\sum{j}\sigma_j\bigr)\Bigr]\,p.
]
여기서
[
S(x) =
\begin{cases}
\text{sign}(x), & x \neq 0, \
0, & x = 0.
\end{cases}
]
즉, 주변의 다수결 방향((\text{sign}))이 현재 스핀 (\sigma_i)와 같으면(agree) 플립확률이 (q), 반대라면(flips) 플립확률이 (p).
만약 주변 합이 0((+)와 (-)가 똑같은 수)이면 (S(0)=0)로 두어서, 그 상황에서 플립은 50% 확률((\frac{1}{2}))입니다.
이 모델은 세부상세균형(detailed balance)을 만족하지 않으므로, 전통적 의미의 열역학적 평형이 존재하지 않습니다. 이는, 예를 들어 어떤 스핀들의 특정 연쇄 뒤집기 패스와 그 역방향 패스의 확률이 일반적으로 같지 않다는 사실에서 드러납니다. 그럼에도 불구하고, 전체적으로 업/다운 대칭((\sigma_i \mapsto -\sigma_i))이 있기 때문에, 모델이 상전이를 일으키는가(즉, 자기발생적(spontaneous) 자발 자기화(magnetization)는 있는가), 임계점 근처에서 임계 거동이 어떠한가 하는 문제가 중요하게 다뤄집니다.
논문에서 흥미로운 점은, 이 majority-vote 모델을 어떤 오픈(open) Ising 모형(두 개의 다른 열저장고(heat bath)에 연결된 계)으로 해석할 수 있다는 것입니다. 특정 극한 조건(한 저장고는 매우 낮은 온도, 다른 저장고는 매우 높은 온도)에서 이 majority-vote 동역학이 나타난다는 것을 보이는데, 그로부터도 이 모델이 비평형적(열역학적 detailed balance 없음)임을 알 수 있습니다.
실제로, 저자들은 역학적 전이율 (w_i) 가 Ising 모형에서 Glauber 동역학(전통적인 열역학적 몬테카를로 업데이트 규칙)과 0K(절대 영도)에서의 완전 결정적 동역학(“spin randomization”와 반대되는 개념)의 혼합 꼴로도 해석할 수 있음을 지적합니다.
이 논문에서는 (q)가 충분히 작으면(즉, “노이즈”가 작으면) 계가 +1 쪽 또는 -1 쪽으로 자발 자기화(spontaneous magnetization)될 것이라고 주장합니다. 그리고 실제로 수치실험을 통해 임계점을 (qc \approx 0.075)로 측정합니다. 이 (q_c) 이하에서는 (\lim{t\to\infty} m(t) \neq 0) (즉, 자발자화 상태가 됨), (qc) 이상에서는 (\lim{t\to\infty} m(t) = 0) (정렬되지 않은 상태)임을 보입니다.
임계 현상을 분석할 때, 유한 크기 (L \times L) 격자 모형을 다룰 경우, 전형적으로 유한크기 스케일링 가설(finite-size scaling hypothesis) 을 사용합니다. 이 이론의 핵심적인 아이디어는 다음과 같습니다.
스핀 모형에서 보통 다음 물리량들을 많이 봅니다:
이들은 다음과 같은 유한크기 스케일링 형태를 만족한다고 알려져 있습니다:
여기서 (\beta,\gamma,\nu)는 전형적인 2차원(2D) 임계 현상의 지표(예: 2D Ising 모형의 경우 (\nu = 1), (\beta = 1/8), (\gamma = 7/4), 등).
논문에서는 이 스케일링 가설을 구체적으로 적용하여, 실험적으로(monte carlo) 측정한 (M_L), (\chi_L), (U_L)의 데이터를 서로 다른 (L)에 대해 한데 모으고, 그 데이터가 잘 “붕괴(data collapse)”하는지, 그로부터 (\nu, \beta, \gamma) 등을 추정합니다.
이때 (q)를 변화시키며(예: (0.03 \le q \le 0.12) 범위), 각각의 (L)에 대해 스캔(sweep)하고, 여러 번 반복(run)하여 통계적 오차를 줄이는 방식으로 진행합니다.
결국,
[
\nu = 0.99 \pm 0.05,\quad
\frac{\gamma}{\nu} = 1.73 \pm 0.05,\quad
\frac{\beta}{\nu} = 0.125 \pm 0.005,
]
이는 2D Ising 모형의 이론값 (\nu=1,\, \gamma=7/4=1.75,\, \beta=1/8=0.125)와 대단히 근접합니다.
따라서 majority-vote 모델이 2차원 정사각 격자 위에서 Ising 모형과 같은 보편적 임계 거동을 보인다는 결론에 이릅니다.
대칭성과 보편성
물리 계가 평형/비평형인가는 국소적 전이 규칙(detailed balance 존재 여부)로 구분되지만, 더 큰 범주에서 임계 현상의 보편성은 국소 대칭(symmetry)과 차원(dimension), 근방 이웃의 상호작용 범위 등에 의해 지배되기 때문에, majority-vote라는 비평형 의사결정 규칙 역시 +1/−1의 자발 대칭 깨짐 현상을 Ising과 ‘같은 방식으로’ 보여준다는 사실은, 보편성(universality) 개념을 재확인해 주는 중요한 예시입니다.
동역학적 해석
“투표(majority vote)”라는 사회적 과정(모델링)도, 통계역학의 스핀 모델과 수학적으로 대응됨을 보여주며, 집단적 거시 현상의 행동(동질화, 상전이)이 단순한 지역 규칙(local rule)만으로도 재현되는 것을 시사합니다.
네트워크(그래프) 시각
격자(lattice)는 특정한 그래프 구조(노드가 격자점, 에지가 최근접 이웃)를 이룬 예시입니다. 네트워크 과학에서 majority-vote 형태의 동역학은 ‘동조(conformity) / 사회적 압력(social pressure)’ 모델링 등에 자주 쓰이는데, 이 연구는 정사각 격자 그래프에서 그런 과정을 엄밀히 살핀 결과이기도 합니다.
비평형 vs. 평형
계가 열역학적으로 에너지를 최적화(에너지를 낮추는 쪽으로 진화)하는 과정이 아님에도, ‘상전이’와 같은 임계 현상을 동일하게 겪으며, 그 임계 지표는 평형계와 동일. 이는 “평형”이라는 조건만이 보편성의 필수 요건이 아니라는 점을 강조합니다.
以上이 논문의 주요 수식과 개념, 원리에 집중하여 상세히 살핀 설명입니다. 결론적으로, “다수결”이라는 사회적 규칙으로 업데이트되는 스핀 계가, 비평형이지만 Ising과 같은 임계 지표(즉, 동차 함수형, 보편성)를 가진다는 사실이 핵심입니다. 이는 다양한 상호작용 네트워크에서의 동의 형성(consensus) 혹은 자발적 대칭 깨짐 문제를 이해하는 중요한 통찰을 제공합니다.
아래 튜토리얼은 graph-tool 라이브러리를 사용하여 MajorityVoterState 클래스를 구체적으로 어떻게 사용하는지 설명합니다. 초보자도 차근차근 따라 할 수 있도록 파이썬 코드 예제와 함께 상세한 한글 주석을 포함하였습니다. 또한, 모델의 개념부터 시뮬레이션, 결과 시각화까지 유기적으로 연결하여 작성했으니 참고하시기 바랍니다.
Majority Voter Model(다수결 모델)은 네트워크(혹은 그래프)에서 각 노드가 가지는 “의견(opinion)”이, 주변 이웃 노드들의 다수 의견을 따라가려는 경향을 지니는 확률적(확률적 전이율) 동역학입니다. graph-tool의 MajorityVoterState는 이러한 다수결 모델을 범용 그래프 위에서 쉽게 시뮬레이션할 수 있도록 구현한 클래스입니다.
주요 파라미터
VertexPropertyMap (없으면 랜덤 초기 상태)전이 규칙
이 규칙은 de Oliveira (1992) 논문[^oliveira1992]에서 제안된 2차원 격자 기반 majority-vote 모델을 그래프 전반으로 일반화한 것이라고 볼 수 있습니다.
[^oliveira1992]: M. J. de Oliveira, “Isotropic majority-vote model on a square lattice”, J Stat Phys 66:273 (1992).
graph-tool은 C++로 구현되어 있어 일반 파이썬 패키지보다 설치가 조금 까다롭습니다.
sudo apt update
sudo apt install python3-graph-toolnumpy, matplotlib 등이 필요합니다.pip install numpy matplotlib아래 예제 코드는 가상 그래프(즉, 예시용으로 만들어낸 그래프)에서 MajorityVoterState를 구축하고, 일부 속성을 확인하는 과정을 보여줍니다.
# majority_voter_tutorial.py
import graph_tool.all as gt
import numpy as np
import matplotlib.pyplot as plt
def create_example_graph(num_nodes=10, prob=0.3):
"""
무작위 Erdos-Renyi 그래프를 생성하는 간단한 함수.
num_nodes: 노드 수
prob: 간선이 생길 확률
"""
# graph_tool에서 제공하는 무작위 그래프 생성 함수
g = gt.random_graph(num_nodes, lambda: (np.random.poisson(prob*num_nodes),),
model="erdos")
return g
def main():
# 1) 그래프 생성
g = create_example_graph(num_nodes=20, prob=0.2)
# 2) MajorityVoterState 초기화
# q=3 -> 의견이 0,1,2 이렇게 3종류
# r=0.1 -> 10% 확률로 무작위 의견 선택
state = gt.MajorityVoterState(g, q=3, r=0.1)
# 3) 초기 상태 확인
init_state_map = state.get_state()
print("[초기 상태 확인]")
for v in g.vertices():
print(f"노드 {int(v)}의 의견:", init_state_map[v])
# 나머지 로직 ...
# 추후 iterate_async(), iterate_sync() 등을 이용해 시뮬레이션 진행 가능
if __name__ == "__main__":
main()
create_example_graph 함수: graph_tool.random_graph를 활용하여 무작위(에르되시-레니, Erdos-Renyi) 그래프를 만듭니다.MajorityVoterState(g, q=3, r=0.1): g는 우리가 만든 그래프 q=3이면 가능한 의견(opinion)이 0, 1, 2 (총 세 가지)가 됨 r=0.1이면 노드가 업데이트될 때 10% 확률로 이웃 다수결이 아니라 “무작위 의견”을 채택 get_state() 메서드를 통해 현재 노드들의 의견을 담은 VertexPropertyMap을 가져올 수 있음.MajorityVoterState에는 다음과 같은 업데이트 메서드가 존재합니다.
iterate_async(niter=1): 비동기(asynchronous) 업데이트.
niter번 반복하면, 무작위 노드를 niter번 골라 순차로 업데이트. iterate_sync(niter=1): 동기(synchronous) 업데이트.
niter번 반복하면, 총 niter번의 전체 스윕을 수행. import graph_tool.all as gt
import numpy as np
import matplotlib.pyplot as plt
def run_async_dynamics(g, q=2, r=0.0, max_iter=1000):
"""
MajorityVoterState를 비동기 업데이트 방식으로 시뮬레이션하는 예시 함수
"""
state = gt.MajorityVoterState(g, q=q, r=r)
opinion_counts = [[] for _ in range(q)] # 각 의견별 노드 수 기록용
# 시뮬레이션 진행
for t in range(max_iter):
# 한 번의 비동기 업데이트: niter = g.num_vertices()
# => 전체 노드 수만큼 랜덤하게 한 노드를 골라 업데이트
changes = state.iterate_async(niter=g.num_vertices())
# 현재 상태를 가져와서 의견 분포를 기록
s = state.get_state().fa # fast array로 꺼낼 수 있음
for opinion_id in range(q):
opinion_counts[opinion_id].append((s == opinion_id).sum())
# 예시로, 만약 더 이상 의견 변화가 없는 경우 중단
if changes == 0:
print(f"[비동기] t={t}에서 더 이상 의견이 안 변해 중단.")
break
return opinion_counts
def main():
# 예시 그래프 생성
g = gt.collection.data["pgp-strong-2009"] # graph-tool에 내장된 샘플 그래프
# 파라미터 설정
q = 4 # 의견 4가지
r = 0.05 # 5% 확률로 무작위로 의견 선택
max_iter = 2000
# 시뮬레이션 실행
opinion_counts = run_async_dynamics(g, q=q, r=r, max_iter=max_iter)
# 결과 시각화
plt.figure(figsize=(8, 5))
for opinion_id in range(q):
plt.plot(opinion_counts[opinion_id], label=f"Opinion {opinion_id}")
plt.xlabel("Time (iteration)")
plt.ylabel("Number of nodes")
plt.legend(loc="best")
plt.title("Majority Voter Model (Asynchronous)")
plt.tight_layout()
plt.savefig("majority_voter_async.png")
plt.show()
if __name__ == "__main__":
main()
iterate_async(niter=g.num_vertices()): 노드 수 만큼 반복해서 무작위 노드 하나씩 업데이트. 이는 마치 “임의 순서로 전체 노드를 대충 한 번씩 건드린다”는 효과가 있습니다.changes: 그 사이 의견이 바뀐 노드 수. state.get_state().fa: 각 노드별 의견을 NumPy 배열 형태로 가져옴. opinion_id(0 ~ q-1)에 대해 노드 수를 센 뒤, opinion_counts에 기록. matplotlib 사용.동기식(sync) 업데이트는 iterate_sync(niter=1)를 사용하여 모든 노드를 동시에 업데이트합니다. 예시는 아래와 같습니다.
def run_sync_dynamics(g, q=2, r=0.0, max_iter=100):
"""
MajorityVoterState를 동기 업데이트 방식으로 시뮬레이션하는 예시 함수
"""
state = gt.MajorityVoterState(g, q=q, r=r)
opinion_counts = [[] for _ in range(q)]
for t in range(max_iter):
# 한 번의 동기 업데이트(1 sweep)
changes = state.iterate_sync(niter=1)
s = state.get_state().fa
for opinion_id in range(q):
opinion_counts[opinion_id].append((s == opinion_id).sum())
if changes == 0:
print(f"[동기] t={t}에서 더 이상 의견이 안 변해 중단.")
break
return opinion_counts
iterate_sync(niter=1)을 호출하면 그래프 내 모든 노드가 “같은 시간”에 업데이트를 1회 진행. MajorityVoterState에는 “active nodes”를 관리하는 메서드들도 존재합니다.
다수결 모델에 따라서는 활성 노드 개념을 사용하지 않을 수도 있지만, 필요하다면 선택적으로 쓸 수 있습니다. 예를 들어 특정 시점에서 상태가 바뀐 노드만 ‘활성화’ 시켜놓고 다음 업데이트에서 그 이웃만 다시 체크한다든지 하는 방식의 최적화/변형에 활용될 수 있습니다.
graph-tool은 OpenMP를 사용하여 내부 알고리즘을 병렬화할 수 있습니다. 설치 시 OpenMP 활성화 옵션을 사용했다면, MajorityVoterState에서의 동기/비동기 업데이트 역시 병렬로 수행될 수 있습니다.
OMP_NUM_THREADS 같은 환경 변수를 통해 설정합니다. 아래는 pgp-strong-2009라는 샘플 그래프를 불러와서, 비동기로 다수결 모델을 돌린 뒤 그 결과를 시각화하는 간단한 종합 예제입니다.
# majority_voter_full_example.py
import graph_tool.all as gt
import numpy as np
import matplotlib.pyplot as plt
def run_majority_voter(g, q=4, r=0.05, max_iter=2000):
"""
1) MajorityVoterState 초기화
2) 비동기 업데이트(iterate_async) 실행
3) 매 단계별 각 의견별 노드 수 기록
4) 의견이 바뀐 노드가 없으면 조기 종료
"""
state = gt.MajorityVoterState(g, q=q, r=r)
opinion_counts = [[] for _ in range(q)]
for t in range(max_iter):
# 그래프 전체 노드를 비동기 방식으로 업데이트
changed = state.iterate_async(niter=g.num_vertices())
# 현재 상태 확인
s = state.get_state().fa
for opinion_id in range(q):
opinion_counts[opinion_id].append((s == opinion_id).sum())
if changed == 0:
print(f"Iteration {t}: 상태 변화가 없어서 조기 종료합니다.")
break
return opinion_counts
def plot_opinion_counts(opinion_counts, q=4):
"""
opinion_counts: 각 의견별 노드 수가 time-step별로 기록된 2차원 리스트
"""
plt.figure(figsize=(6, 4))
for opinion_id in range(q):
plt.plot(opinion_counts[opinion_id], label=f"Opinion {opinion_id}")
plt.xlabel("Time (iteration)")
plt.ylabel("Number of nodes")
plt.legend(loc="best")
plt.title("Majority Voter Model")
plt.tight_layout()
plt.show()
def main():
# 샘플 그래프 불러오기 (PGP Web of Trust, 2009년)
g = gt.collection.data["pgp-strong-2009"]
print("불러온 그래프 정보:")
print(f"- 노드 수: {g.num_vertices()}")
print(f"- 간선 수: {g.num_edges()}")
# 파라미터 설정
q = 4
r = 0.05
max_iter = 2000
# 시뮬레이션
opinion_counts = run_majority_voter(g, q=q, r=r, max_iter=max_iter)
# 결과 시각화
plot_opinion_counts(opinion_counts, q=q)
if __name__ == "__main__":
main()
g = gt.collection.data["pgp-strong-2009"]: graph-tool이 제공하는 샘플 그래프 중 하나 로딩run_majority_voter(g, q=4, r=0.05, max_iter=2000): 다수결 모델 시뮬레이션 실행iterate_async로 비동기 업데이트 plot_opinion_counts(...): 기록된 의견 분포를 matplotlib를 이용해 시각화.위 코드를 실행하면, “Time (iteration)에 따른 의견별 노드 수 추이”가 그래프로 표시되어, 다수결 모델의 동적인 변화 양상을 손쉽게 확인할 수 있습니다.
초기 상태 직접 설정
특정 노드들의 초기 의견을 임의로 지정하고 싶다면, VertexPropertyMap을 만들어 state = gt.MajorityVoterState(g, q=3, r=0.1, s=custom_state_map) 식으로 전달할 수 있습니다.
동기 vs. 비동기
모델 변형
r 파라미터가 0이 되면, 이웃의 다수 의견만을 따라가므로 빠르게 자발 대칭 깨짐(한 의견으로 몰리는 현상) 또는 고착 상태를 보입니다. q 파라미터가 커질수록, 의견 종류가 늘어나 복잡도가 증가합니다.병렬 성능
OpenMP 병렬화 성능을 활용하면 계산 시간을 대폭 줄일 수 있습니다(단, 설치 시 주의).이로써 graph-tool 라이브러리에서 제공하는 MajorityVoterState의 기본적인 사용 방법과 예시 코드를 살펴보았습니다.
이 튜토리얼이 도움이 되길 바라며, 향후 더 복잡한 모델(예: 가중치가 있는 그래프, 방향성(directed) 그래프, 이웃의 영향 가중치 차등 적용 등)으로 확장해보는 것도 흥미로울 것입니다. 즐거운 분석 & 시뮬레이션 되세요!