Develop/pauli+ - #1
Merged
Merged
Conversation
PauliPlusSimulator.rng가 메인 프로세스에서 한 번 만들어지고 fork된 워커들이 동일한 RNG 상태를 공유해, num_workers>0이면 모든 워커가 같은 shot을 뱉던 문제. _worker_init_fn은 global numpy random만 시드해서 self.rng는 안 건드렸음. OnlineQECDataset.__iter__에서 worker_info 진입 시 SeedSequence를 spawn해 각 simulator를 워커별로 reseed. BaseSimulator에 reseed() default no-op을 두고 PauliPlusSimulator만 override (Stim 백엔드는 global random 사용이라 worker_init_fn으로 충분).
apply_passive_decay에 numba batch 함수가 이미 있는데 _run에서 data qubit마다 Python 루프로 호출해 무력화됐던 부분. layout.data_arr를 한 번에 전달하도록 정리. d=5/rounds=5 기준 약 21% 가속 (47.4 → 37.3 ms/iter, leakage on). 정합성: noiseless 0% 유지, 실측 LER 분포 정상.
라운드마다 호출되는 _reset이 ancilla 8개를 Python 루프로 처리해 bitflip_batch_nb가 있는데도 단일 큐빗 bitflip()을 반복 호출. 초기 reset(all_qubits ~50개)도 마찬가지. numpy fancy indexing으로 frame을 한 번에 클리어하고 bitflip_batch_nb로 노이즈도 일괄 적용. layout.all_qubits_arr 미리 계산해 호출처에서 재사용. d=5/rounds=5 기준 약 31% 추가 가속 (48.0 → 32.9 ms/iter, leakage on). 정합성: noiseless 0%, LER 분포 정상.
- get_expanded_pauli_plus_configs(): list-valued 필드를 cartesian
product로 확장. _build_simulator_pool + eval_pipeline이 multi-config
처리. simulation.pauli_plus에 {p: [...]}처럼 list 주면 자동 sweep.
- t_cx_us, t_meas_us 필드 추가. heating(rate × time) 및 측정 중
T1 decay 계산이 디바이스 게이트 시간으로 캘리브레이션 가능.
기본값(0.05, 0.5)은 SI1000 표준 — 동작 보존.
- _no = 1 하드코딩 제거. layout.num_observables를 stim circuit에서
추출해 다른 코드/observable 추가 시 silent 손상 방지.
- iq_noise/leakage의 deprecated Python 단일-큐빗 버전 제거 (numba
batch 버전이 호출됨). leaked_mask 등 미사용 헬퍼도 정리.
-143 lines.
- preprocessor를 nn.Module 상속으로 바꾸고 인덱스 텐서를
register_buffer(persistent=False)로 등록 → wrapper.to(device) 시
자동 이동, state_dict 미포함, gpu_transform의 매 배치 디바이스
체크 제거.
- OnlineQECDataset이 chunk를 batch_size 단위로 슬라이스해
(batch_dict, batch_labels) 그대로 yield. DataLoader는
batch_size=None으로 collate 우회. sample-by-sample yield + collate
라운드트립 제거.
- torch.from_numpy(...).float()의 redundant .float() 제거 (trainer가
.to(device).float() 호출).
벤치 (d=5/r=5, chunk=1000, batch=256, full noise + crosstalk):
pipeline overhead 40.7 → 0.2 ms/chunk (사실상 제거)
정합성: state_dict는 core_model만, num_workers=0/2 학습 정상.
Output directory becomes <timestamp>_<basename> instead of <basename>_<timestamp> so plain ls sorts runs chronologically. Eval-only fallback now auto-creates the dir under training.output_dir when no model_path is given, instead of dumping eval CSV/log into the project root.
Pauli+ backend builds an SI1000-equivalent stim proxy circuit when MWPM or coset_mode needs a DEM/LUT (leakage/crosstalk are not DEM-expressible, so the matching graph sees Pauli noise only). NeuralDecoder.decode_batch now also forwards soft_measurements so soft_grid preprocessor models can be evaluated.
New builder registers as 'surface_code_si1000' (existing 'surface_code' aliased to 'surface_code_basic'). Uses all four stim noise channels (after_clifford / before_round_data_depolarization / before_measure / after_reset) at SI1000 ratios 1/2/5/2 × p_gate. p_meas is auto-derived as 5p (user value ignored).
Was missing required code.name/model/decoder/training fields and would error on load. Repurposed as Pauli+ backend MWPM eval matching pauli_plus_soft_iq.yaml's noise setup so the two configs share a comparable noise grid.
Adds: stim_si1000_d3{,_sweep,_mwpm,_coset,_coset_sweep} for the clean Stim
SI1000 CNN-vs-MWPM rematch; pauli_plus_soft_iq_quick + d5{,_sweep,_coset,
_coset_sweep} for the (now bug-flagged) Pauli+ comparison; experiment_mwpm_d{3,5}
plus _no_ct/_clean/_stim variants used during simulator diagnosis.
The flat _CX_RAW list is laid out with target as the outer index and control as the inner index, but callers index as _CX_C[fc, ft] = [control, target]. The reshape produced a transposed table — every CX gate gave wrong outputs (e.g. input X on control, I on target produced (I, X) instead of (X, X), so X errors disappeared from control instead of propagating to target). This caused per-round detection events to grow monotonically across rounds because errors propagated incorrectly through the CX layers, accumulating spurious syndrome events. Adds .T.copy() at construction so callers can continue indexing as [fc, ft].
…a builder PauliPlusSimulator was constructing syndromes by hand with separate logic for round 0 (Z-ancilla MR-order subset), round R>=1 (full MR-order XOR), and final (custom DETECTOR parser). Round 0 (and final) emission orders in stim's generated circuits do NOT match MR-order Z-ancilla subset — stim emits Z detectors in coord-sorted order, which differed by a permutation. The mismatch left MWPM matching graphs reading the right bits in the wrong slots, so MWPM's logical error rate on Pauli+ data ran 6–540× higher than on equivalent stim-sampled data. Replace the manual logic with stim.Circuit.compile_m2d_converter(): we concatenate ancilla_meas across rounds plus final data_meas in stim's measurement order and let stim compute detection events and observables exactly as its DETECTOR/OBSERVABLE annotations specify. This works for any stim-supported code (rotated_memory_z/x, color_code, externally loaded circuits) without per-code branches. Also stop hardcoding 'surface_code:rotated_memory_z' inside _SurfaceCodeLayout. The reference circuit is now produced by the registered builder for code.name (PauliPlusSimulator passes a zero-noise build through build_circuit, since structure extraction ignores noise instructions). Layout construction now requires a circuit kwarg — unregistered code.name raises KeyError from the builder registry instead of silently falling back to a default topology.
Removes z_ancilla_indices_in_order, _final_det_defs, compute_final_detectors, _get_final_detector_defs, _get_logical_z_indices, and the manual num_detectors arithmetic. Stim's circuit and m2d_converter already give us num_detectors and the logical observables directly, and the round-by-round syndrome assembly is gone, so none of this is reachable anymore.
Adds gen mode and DatasetGenerator support for Pauli+ (was Stim-only with an int8 cast bug that would corrupt soft IQ floats). The generator now preserves native dtypes per key, supports both backends via from_config classmethod, and shows tqdm progress per shot. Trainer.train_epoch wraps the batch loop in tqdm too. Factory always builds the simulator pool because the soft_grid preprocessor needs ancilla coordinates from a simulator even in offline mode. Removes silent default values across the codebase: function-signature numeric defaults (batch_size, hidden_dim, patience, shots), getattr fallbacks for schema-backed fields (coset_mode, REQUIRED_PREPROCESSOR), and dict.get fallbacks for required yaml keys (simulation.backend, simulation.pauli_plus, ZZ crosstalk weights). Missing values now raise KeyError/ValueError instead of silently using a hidden value. argparse --shots/--train-shots/--val-shots default to None and main.py validates per mode.
pauli_plus_soft_iq.yaml: bumped to 10M total samples (100k/epoch × 100 epochs) with patience=99999 to disable early stopping for the long run. pauli_plus_soft_iq_quick.yaml: reverted to single noise point for training. pauli_plus_soft_iq_quick_sweep.yaml: new sweep variant for eval over the 6-point noise grid. pauli_plus_soft_iq_d5_offline.yaml: new offline-mode d=5 config for /dev/shm dataset workflow.
Catches files missed in ae2f069. Same intent: replace function-signature defaults and dict.get/getattr fallbacks with explicit yaml/CLI inputs.
Caller (TrainingPipeline) already passes patience explicitly from training.early_stopping.patience in the yaml;
The <timestamp>_<basename> path construction was duplicated between TrainingPipeline._setup_workspace and EvaluationPipeline._resolve_output_dir. Move it to qec_sim/trainer/utils.py. Also rewrite the buffer-probe comment in DatasetGenerator.generate_and_save to record WHY (avoid the int8 cast bug; infer native dtype from the simulator directly) instead of WHAT (probe checks output keys).
The simulation: Dict[str, Any] field on ExperimentConfig allowed yaml typos (p_corsstalk, missing backend) to silently degrade or fall through to defaults. Replace it with a SimulationConfig dataclass with explicit backend / shots / pauli_plus fields, and validate pauli_plus keys against PauliPlusNoiseParams.fields at parse time (so unknown keys raise instead of being ignored). pauli_plus is kept as Dict[str, Any] internally because get_expanded_pauli_plus_configs needs the raw dict to detect list-valued fields and cartesian-expand them; the field validation gives most of the type-safety benefit anyway. Backend access becomes config.simulation.backend (was config.simulation['backend'] guarded by an explicit 'backend' not in dict check); same for pauli_plus. Seven legacy yamls that relied on the previously-removed silent 'stim' default now declare backend: "stim" explicitly.
The other four registries (model / decoder / preprocessor / circuit-builder) already share Registry[T] from qec_sim.core.registry; criterion was the only one still using a plain dict. Migrate it for pattern consistency. build_criterion's public API is unchanged.
DataLoader over IterableDataset has __len__ defined but raises TypeError on call. The tqdm total computation in train_epoch checked hasattr but not the actual call, so online-mode training crashed at the first epoch. Wrap the len() in try/except and fall back to indeterminate total (None).
The configs/ tree had grown to ~30 yamls — throwaway diagnostic variants, hardcoded /dev/shm paths, per-experiment sweep splits — none of which generalize across machines. The schema in qec_sim/config/schema.py is the source of truth; users construct yamls locally as needed. Files remain on disk untouched (--cached), just untracked. Old history still contains them.
…dation
Three high-value tests covering bugs hit in this session:
1. test_cx_table.py — verifies all 16 (control, target) Pauli combinations
transform correctly under CX. Catches the transpose bug fixed in df6771f.
2. test_pauli_plus_baseline.py — d=3 p=1e-3 Pauli+ LER must match Stim
self-consistent LER within 0.5%. Catches both the CX bug and the manual
detector parsing bug (was 17-543× off when broken). Plus a p=0 sanity check.
3. test_schema.py — SimulationConfig validation: missing backend raises,
unknown pauli_plus key (e.g. typo p_corsstalk) raises, stim backend
without pauli_plus block loads cleanly.
All run in <3s. Run with: pytest tests/ -v
Trim runtime dependencies to packages actually imported by qec_sim/main.py:
numpy, torch, stim, pymatching, numba, tqdm.
Move the rest:
- jupyter / notebook / pandas / graphviz / torchviz → optional 'analysis' extra
(install with pip install qec_sim[analysis] when running notebooks)
- pytest → [dependency-groups].dev (already there)
Drop intel-extension-for-pytorch (Intel-CPU-only, breaks installs on NVIDIA),
sinter and dotenv (not used anywhere).
GitHub Actions workflow uses uv to install runtime + dev dependencies, then runs the 9-test suite. CPU-only (no GPU usage in tests), so ubuntu-latest is fine.
…ia python-dotenv
- callbacks: RunLogger now a context manager — started at pipeline.run() entry,
tees stdout + stderr (tqdm + traceback 포함) via _ProgressAwareFile so
pipeline-level prints (Device, output dir, "데이터를 준비합니다…") land in run.log.
- pipeline: wrap run() body in `with RunLogger(...)`, drop from callbacks list.
- trainer: drop redundant periodic step print (tqdm covers it; competed for
the same `\r`-line and caused flicker).
- main: load_dotenv() before torch import so CUDA_VISIBLE_DEVICES from .env
is honored before CUDA context creation.
- pyproject: add python-dotenv dependency.
… drop unnecessary mps.copy in MPSBackend.normalize
…-faithful refactor)
…onCommon
Self-implements paper Algorithm 1 (BP message update + Bethe free entropy)
on top of quimb's BPC base for shared run loop / damping / convergence stats.
Two message modes via max_chi:
- max_chi=None: dense numpy array messages, validates bit-identical against
vanilla contract_l1bp (verified d=3..5, all syndromes / class_bits, rel_diff
1e-15..1e-14).
- max_chi=int: MPS messages with chi truncation. Outgoing message computed
as dense intermediate then compressed via from_dense(max_bond=chi).
At chi >= effective message bond dim (chi=2+ for d=3,4), result matches
dense mode to FP precision; chi=1 yields different BP fixed point as
expected.
Generic wrapper: any model with REQUIRED_PREPROCESSOR + run_dir → sinter.Decoder. Note: PyTorch eager small-batch overhead makes sinter inefficient for neural — use yaml eval pipeline for them, sinter for algorithmic decoders.
Adds qec_sim/circuit/noise_model.py with NoiseModel.SI1000(p) vendored
from honeycomb_threshold/src/noise.py (Apache-2.0, Craig Gidney). Adds
SurfaceCodeSI1000CanonicalBuilder + build_si1000_canonical helper that
apply this noise to stim's zero-noise rotated_memory_z circuit.
Modifications from upstream:
- noisy_gates += {CX, MR/MRX/MRY} for stim's CX-based generator
- any_clifford_2 = p set explicitly
- dropped honeycomb-specific MPP helpers + PC3/EM3 variants
LICENSES/Apache-2.0.txt holds a verbatim copy of the upstream Apache-2.0 text; NOTICE points to it. Satisfies Apache-2.0 §4(a) for the noise_model.py vendored from honeycomb_threshold (44f25fd).
…ched/GNN variants
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.