Summary
For two-qubit gates, leaked-qubit gate suppression does not happen. The single-qubit leakage helper suppresses the gate on leaked qubits correctly; its two-qubit counterpart builds the filtered gate and then discards it.
Detail
python/quantum-pecos/src/pecos/noise/noise_impl/noise_sq_depolarizing_leakage.py constructs the gate restricted to non-leaked qubits and always emits it -- alongside the sampled noise (line 87) or alone (line 91):
noisy_op = QOp(name=op.name, args=list(not_leaked), metadata=dict(op.metadata))
...
buffered_ops.extend((noisy_op, QOp(name=sym, args=args, metadata={})))
...
buffered_ops.append(noisy_op)
python/quantum-pecos/src/pecos/noise/noise_impl/noise_tq_depolarizing_leakage.py does the analogous filtering (around line 54, op = QOp(name=op.name, args=new_args, ...)) into a local, and its returned buffered_ops contains only the sampled Pauli and leak operations -- never the gate. Its caller GenericErrorModel.process (python/quantum-pecos/src/pecos/noise/generic_error_model.py around line 219) therefore appends the ORIGINAL, unfiltered op and puts the helper's noise after it.
So the comment in the two-qubit helper, "Don't apply a gate if an input qubit has already leaked", is not honoured: the gate is applied to the leaked qubit anyway. The filtering's only live effect is on which pairs are eligible for noise.
Expected
A decision, then one consistent contract. Either the two-qubit helper returns the filtered gate the way the single-qubit one does (which changes what the caller must emit, so the caller changes with it), or the filtering is removed from the noise-eligibility path and gate suppression is done where the caller assembles operations. One rule for both arities, in one place.
Provenance
Found while fixing #816. That issue's stated expectation ("the surviving op acts on [[2, 3]]") was wrong for exactly this reason and has been corrected; #816 is now scoped to the inverted membership test, which affects noise targeting only. A Codex implementation arm stopped rather than widen its packet to cover this, which was the right call.
Summary
For two-qubit gates, leaked-qubit gate suppression does not happen. The single-qubit leakage helper suppresses the gate on leaked qubits correctly; its two-qubit counterpart builds the filtered gate and then discards it.
Detail
python/quantum-pecos/src/pecos/noise/noise_impl/noise_sq_depolarizing_leakage.pyconstructs the gate restricted to non-leaked qubits and always emits it -- alongside the sampled noise (line 87) or alone (line 91):python/quantum-pecos/src/pecos/noise/noise_impl/noise_tq_depolarizing_leakage.pydoes the analogous filtering (around line 54,op = QOp(name=op.name, args=new_args, ...)) into a local, and its returnedbuffered_opscontains only the sampled Pauli and leak operations -- never the gate. Its callerGenericErrorModel.process(python/quantum-pecos/src/pecos/noise/generic_error_model.pyaround line 219) therefore appends the ORIGINAL, unfiltered op and puts the helper's noise after it.So the comment in the two-qubit helper, "Don't apply a gate if an input qubit has already leaked", is not honoured: the gate is applied to the leaked qubit anyway. The filtering's only live effect is on which pairs are eligible for noise.
Expected
A decision, then one consistent contract. Either the two-qubit helper returns the filtered gate the way the single-qubit one does (which changes what the caller must emit, so the caller changes with it), or the filtering is removed from the noise-eligibility path and gate suppression is done where the caller assembles operations. One rule for both arities, in one place.
Provenance
Found while fixing #816. That issue's stated expectation ("the surviving op acts on
[[2, 3]]") was wrong for exactly this reason and has been corrected; #816 is now scoped to the inverted membership test, which affects noise targeting only. A Codex implementation arm stopped rather than widen its packet to cover this, which was the right call.