Skip to content

Commit

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

Creates `ConditionalOperation` class and executes operations conditionally upon the classical bits. Most of this is done in commit quantumlib@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 01e20a2 commit 8842866
Show file tree
Hide file tree
Showing 17 changed files with 724 additions and 211 deletions.
2 changes: 2 additions & 0 deletions 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/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/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/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/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 8842866

Please sign in to comment.