Skip to content

Commit

Permalink
Convert to a class
Browse files Browse the repository at this point in the history
  • Loading branch information
eliottrosenberg committed Jul 11, 2024
1 parent 4078f86 commit 1119ad9
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 58 deletions.
123 changes: 71 additions & 52 deletions cirq-core/cirq/transformers/noise_adding.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,65 +30,84 @@ def _gate_in_moment(gate: ops.Gate, moment: circuits.Moment) -> bool:


@transformer_api.transformer
def add_depolarizing_noise_to_two_qubit_gates(
circuit: circuits.Circuit,
*,
p: float | Mapping[tuple[ops.Qid, ops.Qid], float],
target_gate: ops.Gate = ops.CZ,
rng: np.random.Generator | None = None,
context: transformer_api.TransformerContext | None = None,
) -> circuits.Circuit:
class DepolerizingNoiseTransformer:
"""Add local depolarizing noise after two-qubit gates in a specified circuit. More specifically,
with probability p, append a random non-identity two-qubit Pauli operator after each specified
two-qubit gate.
Args:
circuit: The circuit to add noise to.
Attrs:
p: The probability with which to add noise.
target_gate: Add depolarizing nose after this type of gate
rng: The pseudorandom number generator to use.
context: Not used; to satisfy transformer API.
"""

Returns:
The transformed circuit.
def __init__(
self,
p: float | Mapping[tuple[ops.Qid, ops.Qid], float],
target_gate: ops.Gate = ops.CZ,
rng: np.random.Generator | None = None,
):
if rng is None:
rng = np.random.default_rng()
self.p = p
self.target_gate = target_gate
self.rng = rng

Raises:
TypeError: If `p` is not either be a float or a mapping from sorted qubit pairs to floats.
"""
if rng is None:
rng = np.random.default_rng()
def __call__(
self,
circuit: circuits.Circuit,
*,
context: transformer_api.TransformerContext | None = None,
):
"""
Apply the transformer to the given circuit.
Args:
circuit: The circuit to add noise to.
context: Not used; to satisfy transformer API.
Returns:
The transformed circuit.
Raises:
TypeError: If `p` is not either be a float or a mapping from sorted qubit pairs to floats.
"""

p = self.p
rng = self.rng
target_gate = self.target_gate

# add random Pauli gates with probability p after each of the specified gate
assert target_gate.num_qubits() == 2, "`target_gate` must be a two-qubit gate."
paulis = [ops.I, ops.X, ops.Y, ops.Z]
new_moments = []
for moment in circuit:
new_moments.append(moment)
if _gate_in_moment(target_gate, moment):
# add a new moment with the Paulis
target_pairs = {
tuple(sorted(op.qubits)) for op in moment.operations if op.gate == target_gate
}
added_moment_ops = []
for pair in target_pairs:
if isinstance(p, float):
p_i = p
elif isinstance(p, Mapping):
pair_sorted_tuple = (pair[0], pair[1])
p_i = p[pair_sorted_tuple]
else:
raise TypeError(
"p must either be a float or a mapping from sorted qubit pairs to floats"
)
apply = rng.choice([True, False], p=[p_i, 1 - p_i])
if apply:
choices = [
(pauli_a(pair[0]), pauli_b(pair[1]))
for pauli_a in paulis
for pauli_b in paulis
][1:]
pauli_to_apply = rng.choice(np.array(choices, dtype=object))
added_moment_ops.append(pauli_to_apply)
if len(added_moment_ops) > 0:
new_moments.append(circuits.Moment(*added_moment_ops))
return circuits.Circuit.from_moments(*new_moments)
# add random Pauli gates with probability p after each of the specified gate
assert target_gate.num_qubits() == 2, "`target_gate` must be a two-qubit gate."
paulis = [ops.I, ops.X, ops.Y, ops.Z]
new_moments = []
for moment in circuit:
new_moments.append(moment)
if _gate_in_moment(target_gate, moment):
# add a new moment with the Paulis
target_pairs = {
tuple(sorted(op.qubits)) for op in moment.operations if op.gate == target_gate
}
added_moment_ops = []
for pair in target_pairs:
if isinstance(p, float):
p_i = p
elif isinstance(p, Mapping):
pair_sorted_tuple = (pair[0], pair[1])
p_i = p[pair_sorted_tuple]
else: # pragma: no cover
raise TypeError( # pragma: no cover
"p must either be a float or a mapping from sorted qubit pairs to floats" # pragma: no cover
) # pragma: no cover
apply = rng.choice([True, False], p=[p_i, 1 - p_i])
if apply:
choices = [
(pauli_a(pair[0]), pauli_b(pair[1]))
for pauli_a in paulis
for pauli_b in paulis
][1:]
pauli_to_apply = rng.choice(np.array(choices, dtype=object))
added_moment_ops.append(pauli_to_apply)
if len(added_moment_ops) > 0:
new_moments.append(circuits.Moment(*added_moment_ops))
return circuits.Circuit.from_moments(*new_moments)
12 changes: 6 additions & 6 deletions cirq-core/cirq/transformers/noise_adding_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
def test_noise_adding():
qubits = devices.LineQubit.range(4)
circuit = circuits.Circuit(ops.CZ(*qubits[:2]), ops.CZ(*qubits[2:])) * 10
transformed_circuit_p0 = na.add_depolarizing_noise_to_two_qubit_gates(circuit, p=0.0)
transformed_circuit_p0 = na.DepolerizingNoiseTransformer(0.0)(circuit)
assert transformed_circuit_p0 == circuit
transformed_circuit_p1 = na.add_depolarizing_noise_to_two_qubit_gates(circuit, p=1.0)
transformed_circuit_p1 = na.DepolerizingNoiseTransformer(1.0)(circuit)
assert len(transformed_circuit_p1) == 20
transformed_circuit_p0_03 = na.add_depolarizing_noise_to_two_qubit_gates(circuit, p=0.03)
transformed_circuit_p0_03 = na.DepolerizingNoiseTransformer(0.03)(circuit)
assert 10 <= len(transformed_circuit_p0_03) <= 20
transformed_circuit_p_dict = na.add_depolarizing_noise_to_two_qubit_gates(
circuit, p={tuple(qubits[:2]): 1.0, tuple(qubits[2:]): 0.0}
)
transformed_circuit_p_dict = na.DepolerizingNoiseTransformer(
{tuple(qubits[:2]): 1.0, tuple(qubits[2:]): 0.0}
)(circuit)
assert len(transformed_circuit_p_dict) == 20

0 comments on commit 1119ad9

Please sign in to comment.