trait 정렬 fix 가 후속의 base 라는 직관적 의존성은 실제로는 약함: 1D random walker 는 simul-core trait 만 사용하므로 simul-euclidean ForceInteraction 부정합과 독립이다. 반면 trait fix 의 *최종 모양* (Handle 을 simul-core 로 lift 할지, EuclideanInteraction sub-trait 으로 둘지) 은 두 데이터 포인트 (Euclidean MD + Lattice) 가 있어야 근거 있게 결정됨. 따라서 M0+ 단위 순서를 1↔2 swap + 결정 게이트 신규 §2 추가: (구) 1. Trait fix → 2. canary → 3-7. forces/wrappers (신) 1. canary → 2. 추상 재설계 결정 → 3. Trait fix → 4-8. PROGRESS.md §1 M0+ 표 행 재배치, §6 다음 한 단위 갱신. learning/M0+.md 섹션 순서 물리 재배치 + 새 §2 결정 게이트 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
M0+ 컨텍스트 패키지 — 즉시 시작 가능한 6 단위
마일스톤 정의: ROADMAP §8.1 의 M0+. 학습자가 지금 당장 시작 가능한 6 단위 + Tier-1 wrapper 14건. 추정 기간: 2–3주 (단위 6개) + 며칠 (wrapper 14건) = 3–4주. DoD: PROGRESS.md §1 의 M0+ DoD.
추천 순서
| 순 | 단위 | 시간 | 의존 |
|---|---|---|---|
| 1 | simul-lattice canary (1D random walker) | 1주 | — (simul-core 만 사용, trait fix 와 독립) |
| 2 | 추상 재설계 결정 | 한나절 | (1) — canary 결과로 Handle을 simul-core lift할지 결정 |
| 3 | Trait 정렬 fix | 한나절~1일 | (2) — 결정된 모양으로 실행 |
| 4 | RMSD / RG / OrientationRestraint | 4–5일 | (3) |
| 5 | AndersenThermostat → Thermostat trait |
1일 | (3) |
| 6 | Tier-1 wrapper 14건 | 며칠 | (3)~(5) |
(1)
(3) 은 아키텍처 작업이다 — 추상이 lattice + euclidean 두 데이터 포인트로 결정된 후 trait fix 실행. (4)(6) 은 구현 작업. canary 가 trait fix 에 의존하지 않는 이유: 1D random walker 는Dynamics<Lattice<1>>+StateSpace만 사용하며 simul-euclidean 의ForceInteraction과 무관 — 부정합과 독립.
1. simul-lattice canary (1D random walker)
배경
ROADMAP §11.6-(a). simul-core trait 가 MD-specific 으로 부풀어 오르는 것을 잡기 위한 살아있는 가드레일.
단계
- crate skeleton:
simul-lattice/ ├── Cargo.toml # depends on simul-core only └── src/ ├── lib.rs ├── space.rs # Lattice<const D: usize> : StateSpace ├── dynamics.rs # RandomWalk1D : Dynamics<Lattice<1>> └── prelude.rs simul/Cargo.tomlworkspacemembers에simul-lattice추가Lattice<D>: StateSpace구현:pub struct Lattice<const D: usize>; impl<const D: usize> StateSpace for Lattice<D> { const DIM: usize = D; type Point = SVector<i64, D>; type Momentum = (); fn is_continuous() -> bool { false } fn name() -> &'static str { "Lattice" } }RandomWalk1D: Dynamics<Lattice<1>>구현:impl Dynamics<Lattice<1>> for RandomWalk1D { fn step<R: Rng>(&self, state: &mut SystemState<Lattice<1>>, rng: &mut R) -> f64 { for p in state.positions.iter_mut() { p[0] += if rng.random_bool(0.5) { 1 } else { -1 }; } 1.0 // discrete step } fn time_type(&self) -> TimeType { TimeType::Discrete } fn is_deterministic(&self) -> bool { false } fn name(&self) -> &'static str { "RandomWalk1D" } }- example:
simul-lattice/examples/random_walk_1d.rs— 1000 step 후 평균/분산 출력 - canary 테스트:
simul-lattice/tests/canary.rs— N=1000 walker, t=1000 step 후<x²> ≈ t(diffusion 1)
Definition of Done
cargo test -p simul-lattice통과cargo run -p simul-lattice --example random_walk_1d출력이<x²> ≈ 1000 ± 100- PROGRESS.md §4 의 (a) Lattice canary
🔴 미설치→✅ - DESIGN.md §"Workspace Structure" 의 simul-lattice 항목 갱신 (canary 만 active)
함정
SystemState<Lattice<1>>가positions: Vec<SVector<i64, 1>>,momenta: Vec<()>,masses: Vec<f64>를 들고 있음. masses 는 lattice 에서 무의미 —Default로 zero. 향후SystemState에 lattice-incompatible 필드 추가 시 여기서 컴파일 깨짐. 이게 canary 의 작동 방식.simul-lattice/Cargo.toml의 dependency 는 simul-core 만 (simul-euclidean 의존 절대 금지 — 그러면 canary 가 의미 없음).
학습 가치
상. 추상화 검증의 살아있는 코드 기법. const generic + StateSpace + Dynamics 의 모든 trait 가 진짜 일반적인지 확인. 200 LOC 로 trait drift 영구 가드.
참고
- ROADMAP §11.6, §12
.team-output/T6-rust-architect.md§"DESIGN 에 없지만 OpenMM 에 있는 기능" — lattice 자체는 OpenMM 에 없지만 추상은 호환- DESIGN.md §"Lattice"
2. 추상 재설계 결정
배경
canary 구현이 끝난 시점의 결정 게이트. simul-core::Interaction 에 device handle 개념을 lift 할지, simul-euclidean 에만 sub-trait 으로 둘지를 두 데이터 포인트 (Euclidean MD + Lattice random walker) 기반으로 결정. 추상을 예측 이 아니라 발견 으로 정의.
결정 트리
- canary 가 host
Vec<i64>만으로 충분 (기대 시나리오) → Handle 을 simul-core 에 올리지 않음.EuclideanInteraction: Interaction<Euclidean<D>, Output = ForceOutput<D>>sub-trait 이 device path 를 보유. §3 Trait 정렬 fix 는 sub-trait 추가만 (한나절). - canary 가 어떤 형태든 buffer/storage 추상을 자연스럽게 요구 →
StateSpace::Bufferassociated type 을 simul-core 에 도입.SystemState리팩터 동반 → 한나절 스코프 초과 → 별도 마일스톤으로 격상. - 결정 보류 (1D random walker 가 너무 trivial 해서 데이터가 부족) → "현재로서 lift 할 근거 없음, M3 (lattice MC 본격) 시 재평가" 로 결정. 결정 자체도 결정.
Definition of Done
- 결정 문서
learning/decisions/abstraction-after-canary.md작성 (1페이지) - DESIGN.md "Core Abstractions" 의 trait 다이어그램에 결정 반영
- §3 Trait 정렬 fix 의 구체 시그니처 (이름:
EuclideanInteraction권장) 가 결정됨
함정
- 아키텍처를 예쁘게 만들려고 lattice 가 필요로 하지 않는 추상을 simul-core 에 끌어오지 말 것. YAGNI.
- 반대로, canary 에서 명백히 device-like 추상이 필요한데 simul-euclidean 에만 두면 다음 lattice 작업 (M3) 에서 또 trait drift 가 생긴다. 두 방향 모두 위험.
- 결정 문서를 만들지 않으면 "왜 이 모양이지?" 가 6개월 후 잊힘 → 반드시 짧게라도 기록.
학습 가치
상. 추상을 예측 이 아니라 발견 으로 정의하는 절차 자체. 결정 문서화 패턴 ADR (Architecture Decision Record).
참고
- 본 M0+.md §1 (canary 결과)
- ROADMAP §6 P1, §11.6
.team-output/T6-rust-architect.md§3.1 (현 trait 부정합 진단).team-output/T7-rust-architect.md§P1
3. Trait 정렬 fix
배경 (T6 §3.1)
현재:
// simul-core/src/interaction.rs
pub trait Interaction<S: StateSpace>: Send + Sync + Debug {
type Output;
fn compute(&self, state: &SystemState<S>) -> Self::Output;
fn name(&self) -> &'static str;
}
// simul-euclidean/src/interaction/mod.rs
pub trait ForceInteraction<const D: usize> {
fn compute_forces(
&self,
client: &Client,
positions: &Handle,
forces: &mut Handle,
n: usize,
) -> f64;
}
ForceInteraction 이 Interaction 을 구현하지 않는다. 두 trait 가 평행 — Interaction::Output associated type 이 lattice 까지 수용 가능한 추상인데 ForceInteraction 이 그 추상을 우회 한다.
권고 (T7 P1, §2 결정 결과 반영)
§2 의 결정에 따라 두 가지 모양 중 하나:
(a) sub-trait (Handle simul-core 에 lift 안 함):
// simul-euclidean
pub trait EuclideanInteraction<const D: usize>:
Interaction<Euclidean<D>, Output = (DeviceForces, f64)>
{
fn compute_forces(...); // 기존 시그니처 유지
}
(b) Buffer associated type (Handle 을 simul-core 로 lift):
// simul-core
pub trait StateSpace {
type Point;
type Momentum;
type Buffer; // Handle for Euclidean, Vec for Lattice
...
}
(b) 채택 시 SystemState 리팩터 동반 → 별도 마일스톤. (a) 가 한나절 스코프 기본값.
또는 간단하게: Interaction::compute() 를 ForceInteraction::compute_forces 의 wrapper 로 자동 구현하는 default impl 추가 — (a) 의 변형.
Definition of Done
simul-core::Interaction이EuclideanInteraction(구ForceInteraction) 의 super-traitcargo test --workspace통과 (nonbonded_openmm.rs/harmonic_bond_openmm.rs무수정)_template_openmm.rs상단의 "trait 부정합 (T6 §3.1) 때문에 일시적" 주석 갱신 또는 제거- DESIGN.md §"Core Abstractions" 의 trait 다이어그램 갱신
함정
Interaction::Output와ForceInteraction::compute_forces의 반환 타입이 다르면 super-trait 관계가 안 됨 → associated type 으로 통합Send + Sync + Debug + 'staticbound 일관성
학습 가치
중. Rust trait 의 sub-trait + associated type 조합. where Self: SuperTrait<...> ergonomic.
참고 자료
- ROADMAP §6 P1
.team-output/T6-rust-architect.md§3.1.team-output/T7-rust-architect.md§P1
4. RMSDForce → RmsdInteraction<D>
배경
OpenMM RMSDForce: 두 입자 set 사이의 root-mean-square deviation. 보통 reference 분자와 정렬 후 RMSD 계산. Theobald–Liu (quaternion-based) 알고리즘이 표준.
simul 측
- 위치:
simul-euclidean/src/interaction/restraint.rs신설 - 타입:
pub struct RmsdInteraction<const D: usize> { reference: Vec<SVector<f64, D>>, k: f64 } ForceInteraction<D>구현 (trait fix 후)
Definition of Done
- 5 원자 시스템에서 reference 와 동일한 좌표 → RMSD = 0, force = 0
- 균일 회전 → RMSD = 0 (rotation-invariant)
- 균일 이동 → RMSD = 0 (translation-invariant)
- Tier-1 테스트
simul-euclidean/tests/rmsd_openmm.rs작성 (_template_openmm.rs복사 후 채움) - reference Python 스크립트
tests/reference/openmm_rmsd.py
함정
- D=3 만 의미 있음 (회전이 정의되는 차원). const generic 으로 두지만
D=3만 구현, 다른 D 에서는unimplemented!()또는compile_error!(). - Quaternion 의 부호 모호성 (q ↔ −q 는 같은 회전).
- 미분 가능 — Theobald 알고리즘은
dRMSD/dr_i가 closed-form.
학습 가치
중. 회전 불변, 최소제곱 정렬, 사원수 — 분자 시뮬의 표준 도구.
참고
- ROADMAP §7.1 #3 (Top 10)
- T2 §2-A 코어 표준 Force #13
- T7 P1
- Theobald, D. L. (2005). Acta Cryst A. doi:10.1107/S0108767305015266
5. RGForce → RgInteraction<D>
배경
Radius of gyration: R_g² = Σᵢ |rᵢ − r_cm|² / N. 또는 mass-weighted: Σᵢ mᵢ |rᵢ − r_cm|² / Σᵢ mᵢ.
simul 측
- 위치:
simul-euclidean/src/interaction/restraint.rs(RMSD 옆) - 타입:
pub struct RgInteraction<const D: usize> { target_rg: f64, k: f64, mass_weighted: bool } - 단순: 미분도 명시적
Definition of Done
- 단일 입자 → R_g = 0
- 두 입자 거리 d → R_g = d/2
- Tier-1 테스트
simul-euclidean/tests/rg_openmm.rs - reference
tests/reference/openmm_rg.py
함정
mass_weighted = true면 simulSystemState::masses를 사용. 학습자는state.masses의 길이가state.positions와 일치하는지 unit test.
학습 가치
중-하. 단순한 수식이지만 RMSD 와 묶어서 "결합 가능한 관측량" 패턴.
참고
- ROADMAP §7.1 #3
- T2 §2-A #14
6. OrientationRestraintForce
배경
참조 분자에 대한 회전 제약. E = k · (1 − cos²(θ)) 또는 quaternion 기반.
simul 측
- 위치:
simul-euclidean/src/interaction/restraint.rs - 타입:
pub struct OrientationRestraintInteraction<const D: usize> { reference: ..., k: f64 } - D=3 만 구현
Definition of Done
- reference 와 동일 회전 → energy = 0
- 90° 회전 → energy = k
- Tier-1 테스트
orientation_restraint_openmm.rs - reference
tests/reference/openmm_orientation.py
함정
- 회전 행렬 vs quaternion 표현 선택. 미분 ergonomic 은 quaternion.
- 부호 모호성.
학습 가치
중. 강체 회전 + restraint. M8 의 VirtualSite 에서 재사용.
참고
- ROADMAP §7.1 #3
- T2 §2-A #15
- T7 P5 (VirtualSite — 회전 표현 공유)
7. AndersenThermostat → Thermostat trait
배경
일정 확률 ν · dt 로 입자의 속도를 Maxwell–Boltzmann 분포에서 새로 추첨. 캐노니컬 앙상블 (NVT) 의 단순 구현.
simul 측
- 위치:
simul-euclidean/src/dynamics/thermostat.rs신설 - 새 trait:
Thermostat: VelocityModifier(T7 P12 의 일부) - 기존
Dynamics와 합성 가능 (Compound 패턴, M3 에서 본격화) - 단발 구현:
AndersenThermostat { temperature, frequency, dt }가step마다 일부 입자 속도 재추첨
Definition of Done
Thermostattrait 정의 + 문서AndersenThermostat: Thermostat구현- Tier-1 단발 테스트: 단일 step 에서 reseed 된 입자의 속도 분포가 Maxwell–Boltzmann
- (선택) Tier-2 통계 테스트: 1000 step 평균 KE = 3/2 N k_B T (4σ 비교)
함정
frequency가 너무 크면 deterministic 동역학 손상.0.1 / ps가 표준.Thermostat은 Force 가 아님. T7 P12 의 권고대로 별도 trait.- 추후 NoseHoover (M7) 도 같은 trait 구현 → 다형성 검증.
학습 가치
중. NVT 앙상블, stochastic vs deterministic 의 구분, trait 다형성 (Thermostat).
참고
- ROADMAP §7.1 #5 (Top 10)
- T2 §2-C (Force-derived 보조 #1)
- T7 P12
8. Tier-1 wrapper 14건
배경
tests/UPSTREAM_TESTS.md §2 의 "현재 시점 ~14개" 게이팅. 새 force/integrator 구현 이 아니라 기존 simul 의 등가성 검증 을 추가.
리스트 (출처: T5 addendum §3)
harmonic_angle_openmm.rs(M1 후)periodic_torsion_openmm.rs(M1 후)rb_torsion_openmm.rs(M1 후)cmap_torsion_openmm.rs(M1 후)gbsaobc_openmm.rs(M0+ 가능)lcpo_openmm.rs(M0+ 가능)rmsd_openmm.rs(#3 와 함께)rg_openmm.rs(#4 와 함께)orientation_restraint_openmm.rs(#5 와 함께)gay_berne_openmm.rs(M2 후)andersen_openmm.rs(#6 와 함께)verlet_single_step_openmm.rs(단발)langevin_middle_single_step_openmm.rs(단발)brownian_single_step_openmm.rs(단발)
#1~#4, #10 은 M1/M2 후로 게이팅. M0+ 에서 즉시 작성 가능한 것은 #5~#9, #11~#14 = 9건.
작성 패턴 (모든 14건 동일)
_template_openmm.rs복사 →<name>_openmm.rs#![cfg(any())]첫 줄 삭제- FILL IN 12군데 채움
tests/reference/openmm_<name>.py작성 (PDB SHA =46376ea3...)- Python 출력 → 15 자리 reference 값 hard-code
cargo test --test <name>_openmm통과 확인
Definition of Done
- 9건 (M0+ 가능 분) 작성 완료
- PROGRESS.md §0 의 "현재 Tier-1" = 2 → 11 (existing 2 + new 9)
- 5건 (M1/M2 게이팅) 은 해당 마일스톤 종료 시 추가
함정
- 단위 테스트 (
#[test]) 가 너무 많아 cargo build 시간 증가.tests/common/mod.rs추출 권고 (T5 §3). - 5/8/11 은 simul 측 구현이 부분적이라 Tier-1 일부만 가능.
학습 가치
상. 14 회 반복으로 패턴 fluency. 매 작업 1–2시간 (작성 30분 + Python reference 30분 + 검증 30분).
참고
tests/UPSTREAM_TESTS.md§2 game 표- T5 본문 §3 작성 컨벤션
_template_openmm.rs자체
M0+ 종료 후 (M1 진입 시)
- PROGRESS.md §1 M0+ 모든 항목 ✅ 확인
learning/canary-check.md의 prompt 로 4중 방어선 게이트 점검- 통과 시
learning/todo-generator.md로 M1 컨텍스트 패키지 생성 →learning/M1.md저장 - ROADMAP §8.1 의 M0+ 누적 LOC / Tier-1 실측치를 기재 → 추정과 ±20% 이상 차이나면 M1+ 시간 추정 재조정