Files
bjkim1 21ebb16fb7 SPipe 데모 v2 (2026-10-30) 서버 배포본
- SPipe_server_2026-10-30.zip 내용을 풀어서 등록 (압축 파일은 올리지 않음)
- README에 데모 v2 서버 업데이트 순서 추가, 저장소에 없는 문서 목차 정리
- 패치내역을 docs/로 이동, _worklog.md 추가
- 임시 파일(backend/temp.*)과 API 키가 든 파일은 제외
2026-10-30 11:00:46 +09:00

65 lines
2.0 KiB
Python

"""Step 5 — 블록 분할 (MVP: partition만. balance_check/refine은 범위 밖)."""
from __future__ import annotations
import networkx as nx
import numpy as np
from sklearn.cluster import SpectralClustering
from .config import Config
def partition(Gw: nx.Graph, k: int, cfg: Config) -> dict[str, int]:
if k < 1:
raise ValueError(f"k는 1 이상이어야 합니다: {k}")
if Gw.number_of_nodes() < k:
raise ValueError(f"노드 수({Gw.number_of_nodes()})가 k({k})보다 적습니다")
method = cfg.partition.method
if method == "spectral":
return _partition_spectral(Gw, k)
if method == "louvain":
return _partition_louvain(Gw, k)
raise ValueError(f"알 수 없는 분할 방법: {method}")
def _partition_spectral(Gw: nx.Graph, k: int) -> dict[str, int]:
nodes = list(Gw.nodes)
A = nx.to_numpy_array(Gw, nodelist=nodes, weight="w")
np.fill_diagonal(A, 0.0)
model = SpectralClustering(
n_clusters=k,
affinity="precomputed",
assign_labels="kmeans",
random_state=0,
)
labels = model.fit_predict(A)
return {n: int(label) for n, label in zip(nodes, labels)}
def _partition_louvain(Gw: nx.Graph, k: int, max_steps: int = 30) -> dict[str, int]:
lo, hi = 0.01, 10.0
best_communities: list[set] | None = None
best_diff = None
for _ in range(max_steps):
r = (lo + hi) / 2
communities = nx.community.louvain_communities(Gw, weight="w", resolution=r, seed=0)
n_found = len(communities)
diff = abs(n_found - k)
if best_diff is None or diff < best_diff:
best_diff, best_communities = diff, communities
if n_found == k:
break
if n_found < k:
lo = r
else:
hi = r
labels: dict[str, int] = {}
for i, community in enumerate(best_communities or []):
for n in community:
labels[n] = i
return labels