Skip to content

Add noisify API with structured NoiseModel (issue #729)#743

Open
yi-hsiu-yang wants to merge 4 commits into
QuantumSavory:masterfrom
yi-hsiu-yang:add-noisify-issue-729
Open

Add noisify API with structured NoiseModel (issue #729)#743
yi-hsiu-yang wants to merge 4 commits into
QuantumSavory:masterfrom
yi-hsiu-yang:add-noisify-issue-729

Conversation

@yi-hsiu-yang

Copy link
Copy Markdown

Closes #729

This PR adds the noisify API for inserting noise operations into a circuit.

What's implemented

  • Simple API: noisify(circuit, noise) applies the same noise to all gate / measurement / reset operations.
  • Structured API: noisify(circuit, NoiseModel(...)) allows different noise per operation type (single-qubit, two-qubit, measurement, reset, idle).
  • Idle noise: When nqubits is provided, noise is also inserted on qubits not touched by each operation. A clear error is raised when idle noise is requested without nqubits.
  • Fallback: Unknown operations (e.g. classical operations) pass through unchanged.

Tests

Added 9 tests in test/test_noisify.jl covering the simple API, structured NoiseModel, idle noise insertion, error handling, insertion order, and pass-through behavior.

Note

I'm a first-time contributor. All code was written by me; I used an AI assistant only to learn Julia syntax and discuss design choices conceptually, not to generate the implementation. Happy to adjust based on review feedback.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master 4e26930... master / 4e26930...
circuitsim/compactification/compact 7.37 ± 0.21 ms 7.08 ± 0.27 ms 1.04 ± 0.049
circuitsim/compactification/no_compact 7.13 ± 0.15 ms 6.97 ± 0.2 ms 1.02 ± 0.036
circuitsim/mctrajectories/q1001_r1 15.7 ± 0.39 ms 16.3 ± 0.5 ms 0.968 ± 0.038
circuitsim/mctrajectories/q101_r1 0.179 ± 0.013 ms 0.182 ± 0.015 ms 0.983 ± 0.11
circuitsim/mctrajectories_sumtype/q1001_r1 14 ± 0.51 ms 14.2 ± 1.4 ms 0.992 ± 0.11
circuitsim/mctrajectories_sumtype/q101_r1 0.123 ± 0.0043 ms 0.122 ± 0.0039 ms 1 ± 0.048
circuitsim/mctrajectories_union/q1001_r1 13.7 ± 0.63 ms 14.4 ± 1.1 ms 0.946 ± 0.087
circuitsim/mctrajectories_union/q101_r1 0.122 ± 0.0041 ms 0.121 ± 0.0053 ms 1.01 ± 0.055
circuitsim/pftrajectories/q1001_r1 0.0812 ± 0.037 ms 0.0853 ± 0.038 ms 0.951 ± 0.61
circuitsim/pftrajectories/q1001_r100 0.189 ± 0.013 ms 0.205 ± 0.016 ms 0.923 ± 0.097
circuitsim/pftrajectories/q1001_r10000 1.16 ± 0.027 ms 1.27 ± 0.046 ms 0.917 ± 0.04
circuitsim/pftrajectories/q101_r1 8.22 ± 3.2 μs 8.52 ± 3.4 μs 0.965 ± 0.54
circuitsim/pftrajectories_sumtype/q1001_r1 0.166 ± 0.0016 ms 0.139 ± 0.00033 ms 1.19 ± 0.012
circuitsim/pftrajectories_sumtype/q1001_r100 0.277 ± 0.01 ms 0.268 ± 0.011 ms 1.03 ± 0.056
circuitsim/pftrajectories_sumtype/q1001_r10000 1.24 ± 0.041 ms 1.36 ± 0.054 ms 0.915 ± 0.047
circuitsim/pftrajectories_sumtype/q1001_r10000_fastrow 6.13 ± 0.048 ms 6.21 ± 0.19 ms 0.986 ± 0.031
circuitsim/pftrajectories_sumtype/q101_r1 16.8 ± 0.051 μs 14 ± 0.11 μs 1.2 ± 0.01
circuitsim/pftrajectories_union/q1001_r1 23.3 ± 0.33 μs 23 ± 0.77 μs 1.01 ± 0.037
circuitsim/pftrajectories_union/q1001_r100 0.137 ± 0.0098 ms 0.141 ± 0.0055 ms 0.97 ± 0.079
circuitsim/pftrajectories_union/q1001_r10000 1.11 ± 0.055 ms 1.19 ± 0.027 ms 0.935 ± 0.051
circuitsim/pftrajectories_union/q101_r1 2.42 ± 0.049 μs 2.44 ± 0.07 μs 0.992 ± 0.035
clifford/dense/cnot250_on_dense500_destab 11.1 ± 0.037 ms 11.1 ± 0.16 ms 1 ± 0.015
clifford/dense/cnot250_on_dense500_stab 5.61 ± 0.021 ms 5.54 ± 0.037 ms 1.01 ± 0.0078
clifford/dense/cnot250_on_diag500_destab 0.978 ± 0.0031 ms 1.05 ± 0.14 ms 0.93 ± 0.13
clifford/dense/cnot250_on_diag500_stab 0.569 ± 0.011 ms 0.585 ± 0.017 ms 0.973 ± 0.034
clifford/dense/cnot_on_dense500_destab 0.0445 ± 0.00042 ms 0.0452 ± 0.0057 ms 0.985 ± 0.12
clifford/dense/cnot_on_dense500_stab 22.6 ± 0.23 μs 21.2 ± 0.19 μs 1.07 ± 0.014
clifford/dense/cnot_on_diag500_destab 26.7 ± 0.74 μs 0.0339 ± 0.0035 ms 0.788 ± 0.084
clifford/dense/cnot_on_diag500_stab 13.8 ± 0.44 μs 15.7 ± 1.7 μs 0.879 ± 0.099
clifford/dense/dense500_on_dense500_destab 11.1 ± 0.061 ms 11.1 ± 0.29 ms 0.996 ± 0.027
clifford/dense/dense500_on_dense500_stab 5.6 ± 0.027 ms 5.54 ± 0.094 ms 1.01 ± 0.018
clifford/dense/dense500_on_diag500_destab 1.13 ± 0.0055 ms 1.16 ± 0.06 ms 0.974 ± 0.05
clifford/dense/dense500_on_diag500_stab 0.569 ± 0.011 ms 0.578 ± 0.021 ms 0.986 ± 0.04
clifford/symbolic/cnot250_on_dense500_destab 1.51 ± 0.012 ms 1.55 ± 0.067 ms 0.972 ± 0.042
clifford/symbolic/cnot250_on_dense500_stab 0.755 ± 0.0092 ms 0.779 ± 0.021 ms 0.969 ± 0.029
clifford/symbolic/cnot250_on_diag500_destab 1.23 ± 0.025 ms 1.28 ± 0.056 ms 0.961 ± 0.046
clifford/symbolic/cnot250_on_diag500_stab 0.622 ± 0.011 ms 0.639 ± 0.031 ms 0.973 ± 0.05
clifford/symbolic/cnot_on_dense500_destab 4.97 ± 0.1 μs 5.49 ± 0.36 μs 0.905 ± 0.062
clifford/symbolic/cnot_on_dense500_stab 2.52 ± 0.08 μs 2.69 ± 0.28 μs 0.934 ± 0.1
clifford/symbolic/cnot_on_diag500_destab 4.94 ± 0.11 μs 5.13 ± 0.22 μs 0.963 ± 0.047
clifford/symbolic/cnot_on_diag500_stab 2.48 ± 0.031 μs 2.52 ± 0.09 μs 0.984 ± 0.037
ecc/evaluate_decoder/shor_bp_comm 2.24 ± 0.074 ms 2.14 ± 0.07 ms 1.04 ± 0.049
ecc/evaluate_decoder/shor_bp_naivesyn 4.91 ± 0.16 ms 4.69 ± 0.19 ms 1.05 ± 0.054
ecc/evaluate_decoder/shor_bp_shorsyn 5.34 ± 0.16 ms 5.09 ± 0.23 ms 1.05 ± 0.057
ecc/evaluate_decoder/shor_pybp_comm 22.1 ± 0.81 ms 21.9 ± 0.83 ms 1.01 ± 0.053
ecc/evaluate_decoder/shor_pybp_naivesyn 0.0448 ± 0.0011 s 0.0446 ± 0.0016 s 1 ± 0.044
ecc/evaluate_decoder/shor_pybp_shorsyn 0.0447 ± 0.0019 s 0.0452 ± 0.0016 s 0.989 ± 0.056
ecc/evaluate_decoder/shor_pybposd_comm 21.5 ± 1.1 ms 22.2 ± 0.88 ms 0.968 ± 0.061
ecc/evaluate_decoder/shor_pybposd_naivesyn 0.0443 ± 0.0019 s 0.0447 ± 0.0018 s 0.991 ± 0.059
ecc/evaluate_decoder/shor_pybposd_shorsyn 0.0442 ± 0.0022 s 0.0445 ± 0.0021 s 0.993 ± 0.069
ecc/evaluate_decoder/shor_table_comm 0.3 ± 0.046 ms 0.298 ± 0.043 ms 1 ± 0.21
ecc/evaluate_decoder/shor_table_naivesyn 0.983 ± 0.0078 ms 0.987 ± 0.014 ms 0.996 ± 0.016
ecc/evaluate_decoder/shor_table_shorsyn 1.37 ± 0.019 ms 1.39 ± 0.019 ms 0.992 ± 0.02
ecc/evaluate_decoder/toric8_bp_comm 0.762 ± 0.029 s 0.768 ± 0.039 s 0.993 ± 0.063
ecc/evaluate_decoder/toric8_bp_naivesyn 1.5 ± 0.059 s 1.51 ± 0.035 s 0.994 ± 0.045
ecc/evaluate_decoder/toric8_bp_shorsyn 1.52 ± 0.052 s 1.54 ± 0.073 s 0.987 ± 0.057
ecc/evaluate_decoder/toric8_pybp_comm 0.0632 ± 0.0025 s 0.0655 ± 0.0027 s 0.966 ± 0.055
ecc/evaluate_decoder/toric8_pybp_naivesyn 0.14 ± 0.005 s 0.137 ± 0.0044 s 1.03 ± 0.05
ecc/evaluate_decoder/toric8_pybp_shorsyn 0.143 ± 0.0043 s 0.145 ± 0.0047 s 0.987 ± 0.044
ecc/evaluate_decoder/toric8_pybposd_comm 0.0663 ± 0.0031 s 0.066 ± 0.0034 s 1 ± 0.07
ecc/evaluate_decoder/toric8_pybposd_naivesyn 0.138 ± 0.0049 s 0.139 ± 0.0049 s 0.992 ± 0.049
ecc/evaluate_decoder/toric8_pybposd_shorsyn 0.143 ± 0.0031 s 0.144 ± 0.0051 s 0.992 ± 0.041
ecc/evaluate_decoder/toric8_pymatch_comm 3.48 ± 0.048 ms 3.53 ± 0.069 ms 0.988 ± 0.024
ecc/evaluate_decoder/toric8_pymatch_naivesyn 13.8 ± 0.41 ms 14.1 ± 0.31 ms 0.978 ± 0.036
ecc/evaluate_decoder/toric8_pymatch_shorsyn 23 ± 1.1 ms 23.1 ± 1.1 ms 0.994 ± 0.067
ecc/evaluate_decoder/toric8_table_comm 3.73 ± 0.043 ms 3.78 ± 0.047 ms 0.988 ± 0.017
ecc/evaluate_decoder/toric8_table_naivesyn 13.6 ± 0.34 ms 14.1 ± 0.26 ms 0.966 ± 0.03
ecc/evaluate_decoder/toric8_table_shorsyn 22.3 ± 0.26 ms 22.1 ± 0.63 ms 1.01 ± 0.031
pauli/mul/100 0.04 ± 0 μs 0.04 ± 0 μs 1 ± 0
pauli/mul/1000 0.05 ± 0.01 μs 0.05 ± 0.001 μs 1 ± 0.2
pauli/mul/100000 0.832 ± 0.07 μs 0.981 ± 0.13 μs 0.848 ± 0.13
pauli/mul/20000000 0.161 ± 0.021 ms 0.21 ± 0.028 ms 0.768 ± 0.14
stabilizer/canon/cano500 3.15 ± 0.038 ms 3.18 ± 0.19 ms 0.992 ± 0.059
stabilizer/canon/diag_cano500 0.668 ± 0.039 ms 0.692 ± 0.096 ms 0.965 ± 0.15
stabilizer/canon/diag_gott500 2.54 ± 0.12 ms 2.63 ± 0.19 ms 0.963 ± 0.084
stabilizer/canon/diag_rref500 0.631 ± 0.058 ms 0.659 ± 0.087 ms 0.958 ± 0.15
stabilizer/canon/gott500 5.15 ± 0.22 ms 5.15 ± 0.24 ms 1 ± 0.064
stabilizer/canon/md_cano500 1.19 ± 0.017 ms 1.2 ± 0.046 ms 0.991 ± 0.041
stabilizer/canon/md_rref500 1.19 ± 0.015 ms 1.18 ± 0.036 ms 1.01 ± 0.033
stabilizer/canon/rref500 3.17 ± 0.027 ms 3.15 ± 0.074 ms 1.01 ± 0.025
stabilizer/project/destabilizer 18 ± 0.67 μs 16.7 ± 1.5 μs 1.08 ± 0.1
stabilizer/project/stabilizer 9.43 ± 0.18 μs 9.44 ± 1 μs 0.999 ± 0.11
stabilizer/tensor/diag_pow5_20 2.36 ± 0.62 ms 2.46 ± 0.92 ms 0.963 ± 0.44
stabilizer/tensor/pow5_20 3 ± 0.29 μs 3.14 ± 0.36 μs 0.955 ± 0.14
stabilizer/trace/destabilizer 21.8 ± 0.43 μs 21.9 ± 0.92 μs 0.999 ± 0.046
stabilizer/trace/stabilizer 25.1 ± 0.44 μs 24.8 ± 2.2 μs 1.01 ± 0.091
time_to_load 1.49 ± 0.025 s 1.52 ± 0.042 s 0.979 ± 0.032
Memory benchmarks
master 4e26930... master / 4e26930...
circuitsim/compactification/compact 0 allocs: 0 B 0 allocs: 0 B
circuitsim/compactification/no_compact 6 k allocs: 0.275 MB 6 k allocs: 0.275 MB 1
circuitsim/mctrajectories/q1001_r1 18 k allocs: 0.489 MB 18 k allocs: 0.489 MB 1
circuitsim/mctrajectories/q101_r1 1.82 k allocs: 0.0493 MB 1.82 k allocs: 0.0493 MB 1
circuitsim/mctrajectories_sumtype/q1001_r1 9 allocs: 0.484 kB 9 allocs: 0.484 kB 1
circuitsim/mctrajectories_sumtype/q101_r1 8 allocs: 0.25 kB 8 allocs: 0.25 kB 1
circuitsim/mctrajectories_union/q1001_r1 9 allocs: 0.484 kB 9 allocs: 0.484 kB 1
circuitsim/mctrajectories_union/q101_r1 8 allocs: 0.25 kB 8 allocs: 0.25 kB 1
circuitsim/pftrajectories/q1001_r1 2 k allocs: 0.0916 MB 2 k allocs: 0.0916 MB 1
circuitsim/pftrajectories/q1001_r100 2 k allocs: 0.0916 MB 2 k allocs: 0.0916 MB 1
circuitsim/pftrajectories/q1001_r10000 2 k allocs: 0.0916 MB 2 k allocs: 0.0916 MB 1
circuitsim/pftrajectories/q101_r1 0.201 k allocs: 9.42 kB 0.201 k allocs: 9.42 kB 1
circuitsim/pftrajectories_sumtype/q1001_r1 0 allocs: 0 B 0 allocs: 0 B
circuitsim/pftrajectories_sumtype/q1001_r100 0 allocs: 0 B 0 allocs: 0 B
circuitsim/pftrajectories_sumtype/q1001_r10000 0 allocs: 0 B 0 allocs: 0 B
circuitsim/pftrajectories_sumtype/q1001_r10000_fastrow 0 allocs: 0 B 0 allocs: 0 B
circuitsim/pftrajectories_sumtype/q101_r1 0 allocs: 0 B 0 allocs: 0 B
circuitsim/pftrajectories_union/q1001_r1 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
circuitsim/pftrajectories_union/q1001_r100 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
circuitsim/pftrajectories_union/q1001_r10000 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
circuitsim/pftrajectories_union/q101_r1 2 allocs: 0.0938 kB 2 allocs: 0.0938 kB 1
clifford/dense/cnot250_on_dense500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/cnot250_on_dense500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/cnot250_on_diag500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/cnot250_on_diag500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/cnot_on_dense500_destab 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
clifford/dense/cnot_on_dense500_stab 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
clifford/dense/cnot_on_diag500_destab 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
clifford/dense/cnot_on_diag500_stab 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
clifford/dense/dense500_on_dense500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/dense500_on_dense500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/dense500_on_diag500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/dense/dense500_on_diag500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot250_on_dense500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot250_on_dense500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot250_on_diag500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot250_on_diag500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot_on_dense500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot_on_dense500_stab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot_on_diag500_destab 0 allocs: 0 B 0 allocs: 0 B
clifford/symbolic/cnot_on_diag500_stab 0 allocs: 0 B 0 allocs: 0 B
ecc/evaluate_decoder/shor_bp_comm 0.0396 M allocs: 1.61 MB 0.0396 M allocs: 1.61 MB 1
ecc/evaluate_decoder/shor_bp_naivesyn 0.0749 M allocs: 3.15 MB 0.0747 M allocs: 3.14 MB 1
ecc/evaluate_decoder/shor_bp_shorsyn 0.0758 M allocs: 3.23 MB 0.0752 M allocs: 3.21 MB 1.01
ecc/evaluate_decoder/shor_pybp_comm 0.0935 M allocs: 3.29 MB 0.0935 M allocs: 3.29 MB 1
ecc/evaluate_decoder/shor_pybp_naivesyn 0.182 M allocs: 6.49 MB 0.182 M allocs: 6.49 MB 1
ecc/evaluate_decoder/shor_pybp_shorsyn 0.182 M allocs: 6.55 MB 0.182 M allocs: 6.55 MB 1
ecc/evaluate_decoder/shor_pybposd_comm 0.0935 M allocs: 3.29 MB 0.0935 M allocs: 3.29 MB 1
ecc/evaluate_decoder/shor_pybposd_naivesyn 0.182 M allocs: 6.49 MB 0.182 M allocs: 6.49 MB 1
ecc/evaluate_decoder/shor_pybposd_shorsyn 0.182 M allocs: 6.55 MB 0.182 M allocs: 6.55 MB 1
ecc/evaluate_decoder/shor_table_comm 3.98 k allocs: 0.17 MB 3.98 k allocs: 0.17 MB 1
ecc/evaluate_decoder/shor_table_naivesyn 2.8 k allocs: 0.185 MB 2.8 k allocs: 0.185 MB 1
ecc/evaluate_decoder/shor_table_shorsyn 3.28 k allocs: 0.247 MB 3.28 k allocs: 0.247 MB 1
ecc/evaluate_decoder/toric8_bp_comm 1.01 M allocs: 0.166 GB 1.02 M allocs: 0.167 GB 0.993
ecc/evaluate_decoder/toric8_bp_naivesyn 2.03 M allocs: 0.332 GB 2.09 M allocs: 0.34 GB 0.975
ecc/evaluate_decoder/toric8_bp_shorsyn 2.07 M allocs: 0.337 GB 2.08 M allocs: 0.339 GB 0.992
ecc/evaluate_decoder/toric8_pybp_comm 0.103 M allocs: 4.18 MB 0.103 M allocs: 4.18 MB 1
ecc/evaluate_decoder/toric8_pybp_naivesyn 0.218 M allocs: 9.04 MB 0.218 M allocs: 9.04 MB 1
ecc/evaluate_decoder/toric8_pybp_shorsyn 0.233 M allocs: 10.7 MB 0.233 M allocs: 10.7 MB 1
ecc/evaluate_decoder/toric8_pybposd_comm 0.103 M allocs: 4.18 MB 0.103 M allocs: 4.18 MB 1
ecc/evaluate_decoder/toric8_pybposd_naivesyn 0.218 M allocs: 9.04 MB 0.218 M allocs: 9.04 MB 1
ecc/evaluate_decoder/toric8_pybposd_shorsyn 0.233 M allocs: 10.7 MB 0.233 M allocs: 10.7 MB 1
ecc/evaluate_decoder/toric8_pymatch_comm 14 k allocs: 1.05 MB 14 k allocs: 1.05 MB 1
ecc/evaluate_decoder/toric8_pymatch_naivesyn 0.0389 M allocs: 2.71 MB 0.0389 M allocs: 2.71 MB 1
ecc/evaluate_decoder/toric8_pymatch_shorsyn 0.054 M allocs: 4.41 MB 0.054 M allocs: 4.41 MB 1
ecc/evaluate_decoder/toric8_table_comm 13.9 k allocs: 0.835 MB 13.9 k allocs: 0.835 MB 1
ecc/evaluate_decoder/toric8_table_naivesyn 0.0388 M allocs: 2.28 MB 0.0388 M allocs: 2.28 MB 1
ecc/evaluate_decoder/toric8_table_shorsyn 0.0538 M allocs: 3.98 MB 0.0538 M allocs: 3.98 MB 1
pauli/mul/100 0 allocs: 0 B 0 allocs: 0 B
pauli/mul/1000 0 allocs: 0 B 0 allocs: 0 B
pauli/mul/100000 0 allocs: 0 B 0 allocs: 0 B
pauli/mul/20000000 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/cano500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/diag_cano500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/diag_gott500 14.5 k allocs: 0.853 MB 14.5 k allocs: 0.853 MB 1
stabilizer/canon/diag_rref500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/gott500 14.5 k allocs: 0.854 MB 14.5 k allocs: 0.854 MB 1
stabilizer/canon/md_cano500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/md_rref500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/canon/rref500 0 allocs: 0 B 0 allocs: 0 B
stabilizer/project/destabilizer 5 allocs: 0.281 kB 5 allocs: 0.281 kB 1
stabilizer/project/stabilizer 2 allocs: 0.0781 kB 2 allocs: 0.0781 kB 1
stabilizer/tensor/diag_pow5_20 0.032 k allocs: 24 MB 0.032 k allocs: 24 MB 1
stabilizer/tensor/pow5_20 29 allocs: 5.48 kB 29 allocs: 5.48 kB 1
stabilizer/trace/destabilizer 2 allocs: 0.0781 kB 2 allocs: 0.0781 kB 1
stabilizer/trace/stabilizer 3 allocs: 0.109 kB 3 allocs: 0.109 kB 1
time_to_load 0.149 k allocs: 11.1 kB 0.149 k allocs: 11.1 kB 1

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.15385% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 74.13%. Comparing base (0729e6d) to head (4b621e8).

Files with missing lines Patch % Lines
src/noise.jl 96.15% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #743      +/-   ##
==========================================
+ Coverage   74.01%   74.13%   +0.11%     
==========================================
  Files         111      111              
  Lines        7778     7802      +24     
==========================================
+ Hits         5757     5784      +27     
+ Misses       2021     2018       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Krastanov Krastanov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not really seem to distinguish the single-qubit, two-qubit, idling, measurement, and reset noise -- all are just doing the same thing. A way to prove this works is to show a noisified circuit and where each different type of noise ended up in it.

Comment thread test/test_noisify.jl Outdated
end

@testset "errors when idle requested without nqubits" begin
@test_throws ErrorException noisify([sHadamard(1)], DefaultNoiseModel())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a test_throws should generally speaking not be this permissive -- you need to check it was actually the error you expected, not some completely unrelated error (thus hiding an actual bug, making the test actively harmful)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @Krastanov! I've pushed an update addressing both points, and would like to flag one design question that came up while working through the dispatch verification.

1. Tests now explicitly verify dispatch routing

I've added a new testset "noisify dispatches NoiseModel fields correctly" that exercises each dispatch path with visually distinguishable noise per field: different probabilities (0.01–0.05) and different Pauli biases (X-only / Y-only / Z-only / depolarizing), and asserts that each NoiseOp points back to the correct NoiseModel field, on the correct qubits.

Here's the noisified output for a 3-qubit, 5-operation circuit (5 ops → 15 ops):

 [1]  NoiseOp(PauliNoise(0.05, 0.0, 0.0), (2, 3))   ← idle on q2, q3
 [2]  NoiseOp(PauliNoise(0.01, 0.0, 0.0), (1,))     ← single_qubit on q1
 [3]  sHadamard(1)
 [4]  NoiseOp(PauliNoise(0.05, 0.0, 0.0), (3,))     ← idle on q3
 [5]  NoiseOp(PauliNoise(0.0, 0.02, 0.0), (1, 2))   ← two_qubit on q1, q2
 [6]  sCNOT(1, 2)
 [7]  NoiseOp(PauliNoise(0.05, 0.0, 0.0), (1, 3))   ← idle on q1, q3
 [8]  NoiseOp(PauliNoise(0.01, 0.0, 0.0), (2,))     ← single_qubit on q2
 [9]  sX(2)
[10]  NoiseOp(PauliNoise(0.05, 0.0, 0.0), (2, 3))   ← idle on q2, q3
[11]  NoiseOp(PauliNoise(0.0, 0.0, 0.03), (1,))     ← measurement on q1
[12]  sMZ(1)
[13]  NoiseOp(PauliNoise(0.05, 0.0, 0.0), (1, 2))   ← idle on q1, q2
[14]  NoiseOp(PauliNoise(0.04, 0.04, 0.04), (3,))   ← reset on q3
[15]  sMRZ(3)

Every gate is preceded by (a) idle noise on the non-affected qubits and (b) the op-specific noise from the matching NoiseModel field. The testset asserts both noise and indices for each NoiseOp and that the original gate is preserved at the expected position. Because each field uses a distinct PauliNoise parameter, any misrouted dispatch would fail the assertion.

2. @test_throws now matches the specific error message

Changed @test_throws ErrorException ... to @test_throws "nqubits must be provided" ..., so the test only passes on the intended error path rather than on any unrelated ErrorException that might be thrown.

3. Design question: should noise_model.reset apply to plain Reset?

I noticed that Reset (without measurement) currently falls through to the no-op fallback, it picks up noise_model.idle on non-affected qubits but not noise_model.reset:

julia> noisify([Reset(S"Z", [1])], model; nqubits=3)
2-element Vector{Any}:
 NoiseOp(PauliNoise(0.05, 0.0, 0.0), (2, 3))   # idle on q2, q3
 Reset(...)                                     # no reset noise on q1

The reset dispatch is currently noisify(g::AbstractResetMeasurement, ...), which covers sMRZ and friends but not Reset itself (which is AbstractOperation but not a subtype of AbstractResetMeasurement).

Is the intended semantics that noise_model.reset applies only to measure-and-reset operations (current behavior), or should it also apply to plain Reset?

@yi-hsiu-yang yi-hsiu-yang requested a review from Krastanov June 11, 2026 17:06
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.

Add a dispatch-based API for turning noiseless circuits into noisy circuits

2 participants