Skip to content

Commit

Permalink
Classical control (#4631)
Browse files Browse the repository at this point in the history
Sits on top of #4627

Creates `ConditionalOperation` class and executes operations conditionally upon the classical bits. Most of this is done in commit 06883d5.

Reimplements quantum teleportation example based off this class.

Parts 8, 9, 10 of https://tinyurl.com/cirq-feedforward.
  • Loading branch information
daxfohl authored Dec 8, 2021
1 parent 45fd829 commit e97768c
Show file tree
Hide file tree
Showing 18 changed files with 741 additions and 221 deletions.
2 changes: 2 additions & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
CCZPowGate,
CCNOT,
CCNotPowGate,
ClassicallyControlledOperation,
CNOT,
CNotPowGate,
ControlledGate,
Expand Down Expand Up @@ -538,6 +539,7 @@
measurement_key_obj,
measurement_key_names,
measurement_key_objs,
measurement_keys_touched,
mixture,
mul,
num_qubits,
Expand Down
4 changes: 1 addition & 3 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2422,9 +2422,7 @@ def _draw_moment_in_diagram(
max_x = x0
for op in non_global_ops:
qubits = tuple(op.qubits)
cbits = tuple(
(protocols.measurement_key_objs(op) | protocols.control_keys(op)) & label_map.keys()
)
cbits = tuple(protocols.measurement_keys_touched(op) & label_map.keys())
labels = qubits + cbits
indices = [label_map[label] for label in labels]
y1 = min(indices)
Expand Down
205 changes: 6 additions & 199 deletions cirq-core/cirq/circuits/circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,28 +86,6 @@ def validate_moment(self, moment):
moment_and_op_type_validating_device = _MomentAndOpTypeValidatingDeviceType()


class ControlOp(cirq.Operation):
def __init__(self, keys, qubits=None):
self._keys = [cirq.MeasurementKey(k) if isinstance(k, str) else k for k in keys]
self._qubits = qubits or []

def with_qubits(self, *new_qids):
pass # coverage: ignore

@property
def qubits(self):
return self._qubits

def _control_keys_(self):
return self._keys

def _circuit_diagram_info_(
self, args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
symbols = ['X'] * len(self._qubits) + ['^'] * len(self._keys)
return cirq.CircuitDiagramInfo(symbols)


def test_alignment():
assert repr(cirq.Alignment.LEFT) == 'cirq.Alignment.LEFT'
assert repr(cirq.Alignment.RIGHT) == 'cirq.Alignment.RIGHT'
Expand Down Expand Up @@ -250,190 +228,19 @@ def test_append_single():


def test_append_control_key():
q = cirq.LineQubit(0)
q0, q1, q2 = cirq.LineQubit.range(3)
c = cirq.Circuit()
c.append(cirq.measure(q, key='a'))
c.append(ControlOp(['a']))
c.append(cirq.measure(q0, key='a'))
c.append(cirq.X(q1).with_classical_controls('a'))
assert len(c) == 2

c = cirq.Circuit()
c.append(cirq.measure(q, key='a'))
c.append(ControlOp(['b']))
c.append(ControlOp(['b']))
c.append(cirq.measure(q0, key='a'))
c.append(cirq.X(q1).with_classical_controls('b'))
c.append(cirq.X(q2).with_classical_controls('b'))
assert len(c) == 1


def test_control_key_diagram():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.measure(q0, key='a'), ControlOp(qubits=[q1], keys=['a']))

cirq.testing.assert_has_diagram(
c,
"""
0: ───M───────
1: ───╫───X───
║ ║
a: ═══@═══^═══
""",
use_unicode_characters=True,
)


def test_control_key_diagram_pauli():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.measure_single_paulistring(cirq.X(q0), key='a'), ControlOp(qubits=[q1], keys=['a'])
)

cirq.testing.assert_has_diagram(
c,
"""
0: ───M(X)───────
1: ───╫──────X───
║ ║
a: ═══@══════^═══
""",
use_unicode_characters=True,
)


def test_control_key_diagram_extra_measurements():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.measure(q0, key='a'), cirq.measure(q0, key='b'), ControlOp(qubits=[q1], keys=['a'])
)

cirq.testing.assert_has_diagram(
c,
"""
0: ───M───M('b')───
1: ───╫───X────────
║ ║
a: ═══@═══^════════
""",
use_unicode_characters=True,
)


def test_control_key_diagram_extra_controlled_bits():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.measure(q0, key='a'), ControlOp(qubits=[q0, q1], keys=['a']))

cirq.testing.assert_has_diagram(
c,
"""
0: ───M───X───
║ ║
1: ───╫───X───
║ ║
a: ═══@═══^═══
""",
use_unicode_characters=True,
)


def test_control_key_diagram_extra_control_bits():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.measure(q0, key='a'),
cirq.measure(q0, key='b'),
ControlOp(qubits=[q1], keys=['a', 'b']),
)

cirq.testing.assert_has_diagram(
c,
"""
0: ───M───M───────
║ ║
1: ───╫───╫───X───
║ ║ ║
a: ═══@═══╬═══^═══
║ ║
b: ═══════@═══^═══
""",
use_unicode_characters=True,
)


def test_control_key_diagram_multiple_ops_single_moment():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.measure(q0, key='a'),
cirq.measure(q1, key='b'),
ControlOp(qubits=[q0], keys=['a']),
ControlOp(qubits=[q1], keys=['b']),
)

cirq.testing.assert_has_diagram(
c,
"""
┌──┐ ┌──┐
0: ────M──────X─────
║ ║
1: ────╫M─────╫X────
║║ ║║
a: ════@╬═════^╬════
║ ║
b: ═════@══════^════
└──┘ └──┘
""",
use_unicode_characters=True,
)


def test_control_key_diagram_subcircuit():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.CircuitOperation(
cirq.FrozenCircuit(cirq.measure(q0, key='a'), ControlOp(qubits=[q1], keys=['a']))
)
)

cirq.testing.assert_has_diagram(
c,
"""
[ 0: ───M─────── ]
[ ║ ]
0: ───[ 1: ───╫───X─── ]───
[ ║ ║ ]
[ a: ═══@═══^═══ ]
1: ───#2───────────────────
""",
use_unicode_characters=True,
)


def test_control_key_diagram_subcircuit_layered():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit(
cirq.measure(q0, key='a'),
cirq.CircuitOperation(
cirq.FrozenCircuit(cirq.measure(q0, key='a'), ControlOp(qubits=[q1], keys=['a'])),
),
ControlOp(qubits=[q1], keys=['a']),
)

cirq.testing.assert_has_diagram(
c,
"""
[ 0: ───M─────── ]
[ ║ ]
0: ───M───[ 1: ───╫───X─── ]───────
║ [ ║ ║ ]
║ [ a: ═══@═══^═══ ]
║ ║
1: ───╫───#2───────────────────X───
║ ║ ║
a: ═══@═══╩════════════════════^═══
""",
use_unicode_characters=True,
)


def test_append_multiple():
a = cirq.NamedQubit('a')
b = cirq.NamedQubit('b')
Expand Down
1 change: 1 addition & 0 deletions cirq-core/cirq/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def _parallel_gate_op(gate, qubits):
'CCXPowGate': cirq.CCXPowGate,
'CCZPowGate': cirq.CCZPowGate,
'CNotPowGate': cirq.CNotPowGate,
'ClassicallyControlledOperation': cirq.ClassicallyControlledOperation,
'ControlledGate': cirq.ControlledGate,
'ControlledOperation': cirq.ControlledOperation,
'CSwapGate': cirq.CSwapGate,
Expand Down
4 changes: 4 additions & 0 deletions cirq-core/cirq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
ParallelGateFamily,
)

from cirq.ops.classically_controlled_operation import (
ClassicallyControlledOperation,
)

from cirq.ops.controlled_gate import (
ControlledGate,
)
Expand Down
Loading

0 comments on commit e97768c

Please sign in to comment.