Skip to content

Develop/pauli+ - #1

Merged
jlib245 merged 65 commits into
mainfrom
develop/Pauli+
Jun 29, 2026
Merged

Develop/pauli+#1
jlib245 merged 65 commits into
mainfrom
develop/Pauli+

Conversation

@jlib245

@jlib245 jlib245 commented Jun 29, 2026

Copy link
Copy Markdown
Owner

No description provided.

jlib245 added 30 commits May 5, 2026 16:22
  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).
  Catches yamls missed in 4e5c413. All these predate the simulation.backend
  field, which previously defaulted to 'stim' via a silent .get() fallback
  that was removed in ae2f069.
  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.
jlib245 added 29 commits May 6, 2026 16:32
…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
…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).
@jlib245
jlib245 merged commit 0d4503b into main Jun 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant