Graph-tool MajorityVoterState

Sylen·2025년 1월 4일

아래 내용은 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)을 공유한다는 점을 수리적/수치적으로 보여주는 것입니다. 본문을 꼼꼼히 살피면서, 식(수식), 개념, 원리를 최대한 자세하고 친절하게 설명해보겠습니다.


1. 서론(Introduction)

논문에서는 “majority-vote 모델”이라고 불리는 확률적 동역학 스핀 모형을 다룹니다. 이 모델은 아래와 같은 특징이 있습니다.

  1. 2차원 정사각 격자(sqare lattice)의 각 격자점(site)에는 (\sigma_i = \pm 1) 값을 가지는 스핀 변수가 존재합니다.
  2. 이산 시간(discrete time)에서 무작위로 하나의 스핀을 선택하고, 그 스핀이 자기 주변(neighborhood)에 있는 스핀들의 ‘다수결(majority)’ 방향을 확률 (p) 로 따르고, ‘소수결(minority)’ 방향을 확률 (q = 1 - p) 로 따르는 규칙으로 전이(플립, flip)합니다.
  3. 2차원 정사각 격자에서, 각 스핀의 근방 이웃은 ‘4방향 최근접 이웃(상하좌우, nearest neighbors 4개)’입니다.
  4. 주변 이웃 스핀이 +1이 2개, -1이 2개처럼 동률인 경우에는 반반(50%) 확률로 플립 여부가 결정됩니다.
  5. 격자 전체의 동역학은 “자세한균형(detailed balance)”을 만족하지 않으므로, 계(system)가 평형 상태(에너지적 의미의 열역학적 평형)로 귀결되지 않습니다. 그럼에도 불구하고, 스핀의 업(+1)/다운(-1) 대칭(up-down symmetry)을 유지하고 있는 점이 중요합니다.

다음과 같은 물리학적 혹은 통계역학적 관심사가 있습니다:

  • Ising 모형의 임계 현상과 비교했을 때, 이런 비평형 모델이 어떤 “보편적 임계 거동(universal critical behavior)”을 보이는가?
  • majority-vote 모델이 특정 소음(noise) 수준 (q) 이하에서는 자발 대칭 깨짐(spontaneous symmetry breaking), 즉 +1 혹은 -1 상태가 장기적으로 결정되는 상전이(phase transition)를 일으키는가?

이 논문에서는 몬테카를로 시뮬레이션 및 유한크기 스케일링 이론을 적용해서, 비평형 모델이지만 고전적인 2차원 등방성 Ising 모형((d=2))과 동일한 임계 지표(critical exponent)를 가진다는 점을 수치적으로 보여줍니다.


2. Majority-Vote 모델의 정의

2.1 동역학적 규칙

  • 격자상의 각 위치 (i)에 (\sigma_i = \pm 1) 스핀이 존재.
  • 매 시간(step)마다 다음 절차로 스핀을 업데이트:
    1. 격자에서 임의로 하나의 스핀 (\sigma_i)을 선택한다.
    2. 선택된 스핀 (\sigma_i)의 주변 이웃(4개의 최근접 이웃)을 조사한다.
      • 주변 스핀의 합 (\sum_{j \in \text{nbr}(i)} \sigma_j)의 부호가 +라면 다수결이 +1, -라면 -1이다.
      • 만약 주변 스핀 합이 0이면 다수결이 성립되지 않으므로 50% 확률(동률)로 결정.
    3. 다수결 쪽 스핀 방향을 확률 (p) 로, 소수결 쪽 스핀 방향을 확률 (q=1-p) 로 따른다(플립 여부 결정).

논문에서는 (\displaystyle q)를 “노이즈 파라미터(noise parameter)”라고 부르며, (q)가 크면 스핀이 다수결을 잘 따르지 않고(소수결을 따를 확률 높음), (q=0)이면 무소음(완전히 다수결만 따름)이 됩니다.

2.2 Spin-flip 확률의 수식 표현

이 규칙을 확률적 전이율(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}))입니다.

2.3 비평형적 특징

이 모델은 세부상세균형(detailed balance)을 만족하지 않으므로, 전통적 의미의 열역학적 평형이 존재하지 않습니다. 이는, 예를 들어 어떤 스핀들의 특정 연쇄 뒤집기 패스와 그 역방향 패스의 확률이 일반적으로 같지 않다는 사실에서 드러납니다. 그럼에도 불구하고, 전체적으로 업/다운 대칭((\sigma_i \mapsto -\sigma_i))이 있기 때문에, 모델이 상전이를 일으키는가(즉, 자기발생적(spontaneous) 자발 자기화(magnetization)는 있는가), 임계점 근처에서 임계 거동이 어떠한가 하는 문제가 중요하게 다뤄집니다.


3. Majority-Vote 모델과 Ising 모형의 연결성

논문에서 흥미로운 점은, 이 majority-vote 모델을 어떤 오픈(open) Ising 모형(두 개의 다른 열저장고(heat bath)에 연결된 계)으로 해석할 수 있다는 것입니다. 특정 극한 조건(한 저장고는 매우 낮은 온도, 다른 저장고는 매우 높은 온도)에서 이 majority-vote 동역학이 나타난다는 것을 보이는데, 그로부터도 이 모델이 비평형적(열역학적 detailed balance 없음)임을 알 수 있습니다.

실제로, 저자들은 역학적 전이율 (w_i) 가 Ising 모형에서 Glauber 동역학(전통적인 열역학적 몬테카를로 업데이트 규칙)과 0K(절대 영도)에서의 완전 결정적 동역학(“spin randomization”와 반대되는 개념)의 혼합 꼴로도 해석할 수 있음을 지적합니다.


4. 2차원 정사각 격자에서의 상전이 존재성

이 논문에서는 (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) (정렬되지 않은 상태)임을 보입니다.


5. 유한크기 스케일링(Finite-Size Scaling) 이론

임계 현상을 분석할 때, 유한 크기 (L \times L) 격자 모형을 다룰 경우, 전형적으로 유한크기 스케일링 가설(finite-size scaling hypothesis) 을 사용합니다. 이 이론의 핵심적인 아이디어는 다음과 같습니다.

  1. 무한계((L \to \infty))에서 어떤 물리량 (Q(\varepsilon))가
    [
    Q(\varepsilon) \sim |\varepsilon|^{-\rho}
    ]
    ((\rho)는 어떤 임계 지수, (\varepsilon = q - q_c)는 임계점에서의 편차) 형태를 띤다고 하자.
  2. 유한크기 (L)에서는, “길이 스케일”이 (L)로 제한되므로,
    [
    Q_L(\varepsilon)
    = L^{-\rho\,/\,(\nu)} \, \mathcal{F}!\Bigl(L^{1/\nu}\,\varepsilon\Bigr),
    ]
    와 같은 형태를 기대할 수 있습니다.
    • (\nu)는 상관길이 (\xi\sim|\varepsilon|^{-\nu})와 관련된 임계 지수.
    • (\mathcal{F}(x))는 매끄러운 스케일링 함수(스케일링 변수 (x=L^{1/\nu}\varepsilon)를 인자로 함).

스핀 모형에서 보통 다음 물리량들을 많이 봅니다:

  • 자발자화(magnetization)
    [
    ML = \langle |m| \rangle,
    \quad
    m = \frac{1}{N} \sum
    {i=1}^N \sigma_i,
    \quad
    N = L^2.
    ]
  • 감수율(susceptibility)
    [
    \chi_L
    = N \bigl(\langle m^2\rangle - \langle |m|\rangle^2\bigr).
    ]
  • 4차 결합률(fourth-order cumulant)
    [
    U_L
    = 1 - \frac{\langle m^4\rangle}{3\,\langle m^2\rangle^2}.
    ]
    (논문에서는 (U_L)를 (\displaystyle 1 - \frac{\langle m^4\rangle}{3\langle m^2\rangle^2}) 대신 (\frac{\langle m^4\rangle}{\langle m^2\rangle^2})로부터의 형태로 쓸 수도 있는데, 보통 Binder cumulant와 유사한 형식입니다.)

이들은 다음과 같은 유한크기 스케일링 형태를 만족한다고 알려져 있습니다:

  1. [
    M_L(\varepsilon)
    = L^{-\beta/\nu}\,\mathcal{F}_M \bigl(L^{1/\nu}\varepsilon\bigr),
    ]
  2. [
    \chiL(\varepsilon)
    = L^{\gamma/\nu}\,\mathcal{F}
    \chi \bigl(L^{1/\nu}\varepsilon\bigr),
    ]
  3. [
    U_L(\varepsilon)
    = \mathcal{F}_U\bigl(L^{1/\nu}\varepsilon\bigr).
    ]

여기서 (\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) 등을 추정합니다.


6. 몬테카를로 시뮬레이션과 결과

6.1 시뮬레이션 방법

  • 격자 크기 (L\in{5,10,20,40,80}).
  • 무작위 초기 구성에서 시작하여, 매 ‘몬테카를로 스텝(MCS)’마다 (L^2)번의 스핀 플립 시도 수행(그중 임의 스핀 하나씩 선택하여 flip 확률 계산).
  • 적당히 많은 MCS가 지난 뒤(열평형·초기 과도 구간 버림), 물리량((M_L), (\chi_L), (\langle m^2\rangle), (\langle m^4\rangle) 등)을 측정해서 평균.

이때 (q)를 변화시키며(예: (0.03 \le q \le 0.12) 범위), 각각의 (L)에 대해 스캔(sweep)하고, 여러 번 반복(run)하여 통계적 오차를 줄이는 방식으로 진행합니다.

6.2 시뮬레이션 결과 해석

(1) 자발자화 (M_L(q)) 곡선

  • (M_L(q))를 (q)에 대해 그리면, (L)이 클수록 임계점 인근에서 급격히 떨어지는 형태를 보임(그림 1, 그림 2 참조).
  • 임계점 (q_c)에서는 (L\to\infty) 극한에서 (M(q_c))가 0으로 가야 하는 지점이 존재.
  • (q < q_c)이면 (M(q) \neq 0) (대칭 깨짐), (q>q_c)이면 (M(q)=0).

(2) 감수율 (\chi_L(q)) 곡선

  • (\chi_L(q))는 일반적으로 임계 근처에서 큰 값(피크)을 가집니다.
  • (L)이 증가함에 따라 피크의 위치가 (q_c) 쪽으로 이동하고 피크 세기가 커집니다(그림 3).
  • 임계점에서 (\chi_L\sim L^{\gamma/\nu}) 스케일링이 관찰되므로, (\gamma/\nu)를 피팅할 수 있습니다.

(3) 4차 결합률(4th order cumulant) (U_L(q))

  • Binder 누적량(Binder cumulant)이라 불리는 이 양은 임계점에서 크기 (L)과 무관한 일정값 (U^*)로 수렴한다는 특징이 있습니다(그림 4).
  • 다른 (L)들에 대한 (U_L(q)) 곡선을 그려보면, 모든 곡선이 한 점에서 교차(crossing)하는 지점이 바로 임계점 (q_c)입니다.
  • 논문에서는 이 교차로부터 (q_c = 0.075 \pm 0.001)임을 결정하고, 교차점에서의 (U^* = 0.61 \pm 0.01)을 얻습니다.
  • 이는 2D Ising 모형에서의 (U^\approx 0.611)과 일치한다는 점이 Ising 보편성*과 밀접함을 시사합니다.

(4) 임계 지수 (\nu, \gamma, \beta) 측정

  • (\nu)는 상관길이 지수, (\gamma)는 감수율 지수, (\beta)는 자발자화 지수.
  • (\nu)는 예를 들어 (dU_L/dq) 등의 곡선을 이용, 혹은 (M_L(q_c))의 크기 의존성을 통해서 얻을 수 있습니다.
  • 구체적으로:
    • (U_L(q))에서 (q=q_c) 근방에서의 기울기 (U_L'(q_c) \sim L^{1/\nu}).
      • 그림 5에서 (\log U_L'(q_c)) 대 (\log L)을 그려 직선(스케일링)으로 피팅하면, 기울기가 (1/\nu)가 됨.
      • 논문에서는 (\nu = 0.99 \pm 0.05)로 측정하였고, 이는 2D Ising 모형의 (\nu=1)과 잘 부합합니다.
    • 감수율 (\chi_L)에서 (\chi_L(q_c) \sim L^{\gamma/\nu}).
      • 그림 6에서 (\log \chi_L(q_c)) 대 (\log L)을 그려서, 기울기가 (\gamma/\nu).
      • 논문 결과: (\gamma/\nu \approx 1.73 \pm 0.05).
      • 2D Ising에서는 (\gamma/\nu = \frac{7/4}{1} = 1.75). 수치적으로 거의 일치.
    • 자발자화 (M_L(q_c) \sim L^{-\beta/\nu}).
      • 그림 8에서 (\log M_L(q_c)) 대 (\log L)을 피팅해서, 기울기가 (-\beta/\nu).
      • 논문 결과: (\beta/\nu \approx 0.125 \pm 0.005).
      • 2D Ising에서 (\beta/\nu = \frac{1/8}{1} = 0.125). 정확히 동일.

결국,
[
\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 모형과 같은 보편적 임계 거동을 보인다는 결론에 이릅니다.


7. 결론(논문의 요점)

  1. 등방적 majority-vote 모델은 비평형 확률적 동역학이지만, 업/다운 대칭을 가진다는 점에서 2D Ising 모형과 동일한 보편성 등급(universality class)에 속한다고 볼 수 있다.
  2. 몬테카를로 시뮬레이션과 유한크기 스케일링을 통해, 임계지표 (\nu, \beta, \gamma)가 2D Ising 모형과 수치적으로 동일함을 보였다.
  3. 임계 소음 (q_c)은 약 0.075 정도이며, 이는 mean-field 근사(약 0.135)나 상계(0.25 등)보다 훨씬 작다.
  4. 비평형 계라도(자세한균형 불만족), 대칭성((+1) ↔ (-1))이 존재한다면 Ising과 같은 임계 특성을 보일 수 있다는 흥미로운 사례로 해석할 수 있다.

8. 철학적·논리적 고찰(요약)

  • 대칭성과 보편성
    물리 계가 평형/비평형인가는 국소적 전이 규칙(detailed balance 존재 여부)로 구분되지만, 더 큰 범주에서 임계 현상의 보편성은 국소 대칭(symmetry)과 차원(dimension), 근방 이웃의 상호작용 범위 등에 의해 지배되기 때문에, majority-vote라는 비평형 의사결정 규칙 역시 +1/−1의 자발 대칭 깨짐 현상을 Ising과 ‘같은 방식으로’ 보여준다는 사실은, 보편성(universality) 개념을 재확인해 주는 중요한 예시입니다.

  • 동역학적 해석
    “투표(majority vote)”라는 사회적 과정(모델링)도, 통계역학의 스핀 모델과 수학적으로 대응됨을 보여주며, 집단적 거시 현상의 행동(동질화, 상전이)이 단순한 지역 규칙(local rule)만으로도 재현되는 것을 시사합니다.

  • 네트워크(그래프) 시각
    격자(lattice)는 특정한 그래프 구조(노드가 격자점, 에지가 최근접 이웃)를 이룬 예시입니다. 네트워크 과학에서 majority-vote 형태의 동역학은 ‘동조(conformity) / 사회적 압력(social pressure)’ 모델링 등에 자주 쓰이는데, 이 연구는 정사각 격자 그래프에서 그런 과정을 엄밀히 살핀 결과이기도 합니다.

  • 비평형 vs. 평형
    계가 열역학적으로 에너지를 최적화(에너지를 낮추는 쪽으로 진화)하는 과정이 아님에도, ‘상전이’와 같은 임계 현상을 동일하게 겪으며, 그 임계 지표는 평형계와 동일. 이는 “평형”이라는 조건만이 보편성의 필수 요건이 아니라는 점을 강조합니다.


9. 요약 정리

  • 문제의식: 비평형 확률적 스핀 모델(majority-vote)이 Ising 보편성 클래스를 따르는가?
  • 연구 방법: 2D 정사각 격자에서 시뮬레이션 + 유한크기 스케일링 분석.
  • 주요 결과:
    • 임계점 (q_c \approx 0.075 \pm 0.001).
    • 임계 지수 (\nu, \beta, \gamma) 모두 2차원 Ising 모형과 동일 ((\nu = 1), (\beta=1/8), (\gamma=7/4)).
    • 4차 결합률 (U^*\approx 0.61), 역시 Ising 모형과 동일.
  • 결론: 비평형성에도 불구하고, 업/다운 대칭을 유지하는 majority-vote 모델은 2D Ising 모델과 동일한 보편성을 갖는다.

以上이 논문의 주요 수식과 개념, 원리에 집중하여 상세히 살핀 설명입니다. 결론적으로, “다수결”이라는 사회적 규칙으로 업데이트되는 스핀 계가, 비평형이지만 Ising과 같은 임계 지표(즉, 동차 함수형, 보편성)를 가진다는 사실이 핵심입니다. 이는 다양한 상호작용 네트워크에서의 동의 형성(consensus) 혹은 자발적 대칭 깨짐 문제를 이해하는 중요한 통찰을 제공합니다.

아래 튜토리얼은 graph-tool 라이브러리를 사용하여 MajorityVoterState 클래스를 구체적으로 어떻게 사용하는지 설명합니다. 초보자도 차근차근 따라 할 수 있도록 파이썬 코드 예제와 함께 상세한 한글 주석을 포함하였습니다. 또한, 모델의 개념부터 시뮬레이션, 결과 시각화까지 유기적으로 연결하여 작성했으니 참고하시기 바랍니다.


1. Majority Voter Model이란?

Majority Voter Model(다수결 모델)은 네트워크(혹은 그래프)에서 각 노드가 가지는 “의견(opinion)”이, 주변 이웃 노드들의 다수 의견을 따라가려는 경향을 지니는 확률적(확률적 전이율) 동역학입니다. graph-toolMajorityVoterState는 이러한 다수결 모델을 범용 그래프 위에서 쉽게 시뮬레이션할 수 있도록 구현한 클래스입니다.

  • 주요 파라미터

    • ( q ) (기본값=2): 가능한 의견(opinion)의 개수
    • ( r ) (기본값=0): 노드가 무작위 의견을 선택하는 비율(probability)
    • ( s ): 초기 상태를 지정해주는 VertexPropertyMap (없으면 랜덤 초기 상태)
  • 전이 규칙

    1. 확률 ( r )만큼은 임의로 ( q )개의 의견 중 하나를 골라서 노드의 상태로 설정.
    2. 확률 ( 1-r )만큼은 이웃 노드들의 다수 의견을 선택해 본인 의견을 그에 맞춤. 만약 다수의견이 여러 개라면 무작위로 그 중 하나를 골라서 설정.

이 규칙은 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).


2. 환경 설정(설치 & 준비)

2.1 graph-tool 설치

graph-tool은 C++로 구현되어 있어 일반 파이썬 패키지보다 설치가 조금 까다롭습니다.

  • 우분투(Ubuntu) 리눅스에서는 다음 명령으로 비교적 쉽게 설치할 수 있습니다:
    sudo apt update
    sudo apt install python3-graph-tool
  • 기타 OS에서는 graph-tool 공식 설치 가이드를 참조해 주세요.

2.2 기타 필요한 라이브러리

  • numpy, matplotlib 등이 필요합니다.
  • 설치 예시:
    pip install numpy matplotlib

3. 기본 예제: 그래프 생성 & MajorityVoterState 초기화

아래 예제 코드는 가상 그래프(즉, 예시용으로 만들어낸 그래프)에서 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()

코드 설명

  1. create_example_graph 함수: graph_tool.random_graph를 활용하여 무작위(에르되시-레니, Erdos-Renyi) 그래프를 만듭니다.
  2. MajorityVoterState(g, q=3, r=0.1):
    • g는 우리가 만든 그래프
    • q=3이면 가능한 의견(opinion)이 0, 1, 2 (총 세 가지)가 됨
    • r=0.1이면 노드가 업데이트될 때 10% 확률로 이웃 다수결이 아니라 “무작위 의견”을 채택
  3. get_state() 메서드를 통해 현재 노드들의 의견을 담은 VertexPropertyMap을 가져올 수 있음.

4. 모델 시뮬레이션: 비동기 / 동기 업데이트

MajorityVoterState에는 다음과 같은 업데이트 메서드가 존재합니다.

  1. iterate_async(niter=1): 비동기(asynchronous) 업데이트.

    • 한 번 호출 시 노드 1개를 임의로 골라 업데이트를 1회 수행.
    • niter번 반복하면, 무작위 노드를 niter번 골라 순차로 업데이트.
    • 각 호출은 “변화가 실제로 일어난 노드(즉, 의견이 바뀐 노드)의 수”를 반환.
  2. iterate_sync(niter=1): 동기(synchronous) 업데이트.

    • 그래프 내 모든 노드에 대해 동시에(병렬로) 업데이트를 1회 수행하는 것을 “1 sweep”이라 함.
    • niter번 반복하면, 총 niter번의 전체 스윕을 수행.
    • 이 역시 “의견이 바뀐 노드의 수”를 반환.

4.1 비동기 업데이트 예시

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 사용.

4.2 동기 업데이트 예시

동기식(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회 진행.
  • 실제 구현 내부적으로는 이전 시점의 상태를 기반으로 전체 노드의 새 상태를 결정한 뒤 일괄 반영.

5. 고급 기능: get_active(), set_active() 등

MajorityVoterState에는 “active nodes”를 관리하는 메서드들도 존재합니다.

  • get_active(): 현재 활성 노드 목록을 가져오기(해당 모델에서 활성 노드의 개념이 쓰이는 경우)
  • set_active(active): 활성 노드 목록을 직접 지정
  • reset_active(): 활성 노드 목록을 리셋

다수결 모델에 따라서는 활성 노드 개념을 사용하지 않을 수도 있지만, 필요하다면 선택적으로 쓸 수 있습니다. 예를 들어 특정 시점에서 상태가 바뀐 노드만 ‘활성화’ 시켜놓고 다음 업데이트에서 그 이웃만 다시 체크한다든지 하는 방식의 최적화/변형에 활용될 수 있습니다.


6. 병렬 처리(Parallel Implementation)

graph-tool은 OpenMP를 사용하여 내부 알고리즘을 병렬화할 수 있습니다. 설치 시 OpenMP 활성화 옵션을 사용했다면, MajorityVoterState에서의 동기/비동기 업데이트 역시 병렬로 수행될 수 있습니다.

  • 구체적인 스레드 수 조절, 병렬화 설정은 OMP_NUM_THREADS 같은 환경 변수를 통해 설정합니다.
  • 대규모 그래프에서 업데이트 속도를 높이고 싶다면, 설치 환경에서 OpenMP를 켜도록 권장됩니다.

7. 종합 예제: 전 과정 (그래프 로드 → 시뮬레이션 → 시각화)

아래는 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()

실행 순서 정리

  1. g = gt.collection.data["pgp-strong-2009"]: graph-tool이 제공하는 샘플 그래프 중 하나 로딩
  2. run_majority_voter(g, q=4, r=0.05, max_iter=2000): 다수결 모델 시뮬레이션 실행
    • iterate_async로 비동기 업데이트
    • 시뮬레이션 중 각 타임스텝마다 의견 분포를 기록
  3. plot_opinion_counts(...): 기록된 의견 분포를 matplotlib를 이용해 시각화.

위 코드를 실행하면, “Time (iteration)에 따른 의견별 노드 수 추이”가 그래프로 표시되어, 다수결 모델의 동적인 변화 양상을 손쉽게 확인할 수 있습니다.


8. 정리 및 확장 아이디어

  • 초기 상태 직접 설정
    특정 노드들의 초기 의견을 임의로 지정하고 싶다면, VertexPropertyMap을 만들어 state = gt.MajorityVoterState(g, q=3, r=0.1, s=custom_state_map) 식으로 전달할 수 있습니다.

  • 동기 vs. 비동기

    • 비동기는 “갱신 순서”가 랜덤하다는 특징 때문에 더 현실적인 시나리오를 반영할 때가 많습니다.
    • 동기는 이론적으로는 Monte Carlo 시뮬레이션에서 “한 스윕(one sweep)”의 개념에 더 가깝습니다.
  • 모델 변형

    • r 파라미터가 0이 되면, 이웃의 다수 의견만을 따라가므로 빠르게 자발 대칭 깨짐(한 의견으로 몰리는 현상) 또는 고착 상태를 보입니다.
    • q 파라미터가 커질수록, 의견 종류가 늘어나 복잡도가 증가합니다.
  • 병렬 성능

    • 큰 그래프에서 많은 횟수의 스텝을 돌릴 때는 OpenMP 병렬화 성능을 활용하면 계산 시간을 대폭 줄일 수 있습니다(단, 설치 시 주의).

맺음말

이로써 graph-tool 라이브러리에서 제공하는 MajorityVoterState의 기본적인 사용 방법과 예시 코드를 살펴보았습니다.

  • 다수결(majority) 기반의 네트워크 확산 모델은 사회적, 교통 흐름, 바이럴 마케팅 등의 시스템에서 “이웃의 영향력”을 모사하는 데 유용합니다.
  • 도시·교통 빅데이터 맥락에서는 “어떤 교차로(노드)가 주변 교차로 신호나 정책을 얼마나 빠르게 따라가는지”를 다수결 모델로 간단히 가정해볼 수도 있을 것입니다.
  • 기본 예시들을 바탕으로, 의견 개수(q)·무작위율(r)·업데이트 모드(동기/비동기)·그래프 크기 등을 조합하여 다양한 시나리오를 시뮬레이션해 보세요.

이 튜토리얼이 도움이 되길 바라며, 향후 더 복잡한 모델(예: 가중치가 있는 그래프, 방향성(directed) 그래프, 이웃의 영향 가중치 차등 적용 등)으로 확장해보는 것도 흥미로울 것입니다. 즐거운 분석 & 시뮬레이션 되세요!

profile
AI가 재밌는 걸

0개의 댓글