Skip to content

Commit fd06033

Browse files
authored
Add Toffoli (#323)
1 parent a207fdf commit fd06033

5 files changed

Lines changed: 202 additions & 2 deletions

File tree

dev_tools/autogenerate-bloqs-notebooks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import qualtran.bloqs.basic_gates.rotation_test
5656
import qualtran.bloqs.basic_gates.swap_test
5757
import qualtran.bloqs.basic_gates.t_gate_test
58+
import qualtran.bloqs.basic_gates.toffoli_test
5859
import qualtran.bloqs.basic_gates.x_basis_test
5960
import qualtran.bloqs.basic_gates.z_basis_test
6061
import qualtran.bloqs.factoring.mod_exp
@@ -87,6 +88,7 @@
8788
BloqNbSpec(qualtran.bloqs.basic_gates.z_basis_test._make_zero_state),
8889
BloqNbSpec(qualtran.bloqs.basic_gates.t_gate_test._make_t_gate),
8990
BloqNbSpec(qualtran.bloqs.basic_gates.rotation_test._make_Rz),
91+
BloqNbSpec(qualtran.bloqs.basic_gates.toffoli_test._make_Toffoli),
9092
],
9193
directory=f'{SOURCE_DIR}/bloqs',
9294
),

qualtran/bloqs/basic_gates.ipynb

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
"\n",
1414
"The bloqs in this module encode gates you'd expect to find in any quantum computing\n",
1515
"framework. It includes single-qubit unitary gates like rotations, bit- and phase-flip;\n",
16-
"basic multi-qubit unitary gates; and states and effects in the Pauli basis."
16+
"basic multi-qubit unitary gates; states and effects in the Pauli basis; and non-Clifford\n",
17+
"gates `TGate` and `Toffoli` which are commonly counted to estimate algorithm resource\n",
18+
"requirements."
1719
]
1820
},
1921
{
@@ -373,6 +375,53 @@
373375
"psi = circuit.final_state_vector()\n",
374376
"psi * np.sqrt(2)"
375377
]
378+
},
379+
{
380+
"cell_type": "markdown",
381+
"id": "30a5458b",
382+
"metadata": {
383+
"cq.autogen": "_make_Toffoli.md"
384+
},
385+
"source": [
386+
"## `Toffoli`\n",
387+
"The Toffoli gate.\n",
388+
"\n",
389+
"This will flip the target bit if both controls are active. It can be thought of as\n",
390+
"a reversible AND gate.\n",
391+
"\n",
392+
"Like `TGate`, this is a common compilation target. The Clifford+Toffoli gateset is\n",
393+
"universal.\n",
394+
"\n",
395+
"#### References\n",
396+
"[Novel constructions for the fault-tolerant Toffoli gate](https://arxiv.org/abs/1212.5069). Cody Jones. 2012. Provides a decomposition into 4 `TGate`.\n"
397+
]
398+
},
399+
{
400+
"cell_type": "code",
401+
"execution_count": null,
402+
"id": "020ce731",
403+
"metadata": {
404+
"cq.autogen": "_make_Toffoli.py"
405+
},
406+
"outputs": [],
407+
"source": [
408+
"from qualtran.bloqs.basic_gates import Toffoli\n",
409+
"\n",
410+
"bloq = Toffoli()\n",
411+
"show_bloq(bloq)"
412+
]
413+
},
414+
{
415+
"cell_type": "code",
416+
"execution_count": null,
417+
"id": "0aca6c53",
418+
"metadata": {},
419+
"outputs": [],
420+
"source": [
421+
"from qualtran.resource_counting import get_bloq_counts_graph, GraphvizCounts\n",
422+
"g, sigma = get_bloq_counts_graph(bloq)\n",
423+
"GraphvizCounts(g).get_svg()"
424+
]
376425
}
377426
],
378427
"metadata": {

qualtran/bloqs/basic_gates/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
1717
The bloqs in this module encode gates you'd expect to find in any quantum computing
1818
framework. It includes single-qubit unitary gates like rotations, bit- and phase-flip;
19-
basic multi-qubit unitary gates; and states and effects in the Pauli basis.
19+
basic multi-qubit unitary gates; states and effects in the Pauli basis; and non-Clifford
20+
gates `TGate` and `Toffoli` which are commonly counted to estimate algorithm resource
21+
requirements.
2022
"""
2123

2224
from .cnot import CNOT
2325
from .rotation import Rx, Ry, Rz
2426
from .swap import CSwap, TwoBitCSwap, TwoBitSwap
2527
from .t_gate import TGate
28+
from .toffoli import Toffoli
2629
from .x_basis import MinusEffect, MinusState, PlusEffect, PlusState, XGate
2730
from .z_basis import IntEffect, IntState, OneEffect, OneState, ZeroEffect, ZeroState, ZGate
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from functools import cached_property
16+
from typing import Dict, Optional, Set, Tuple, TYPE_CHECKING, Union
17+
18+
from attrs import frozen
19+
20+
from qualtran import Bloq, Register, Signature
21+
from qualtran.bloqs.basic_gates import TGate
22+
23+
if TYPE_CHECKING:
24+
import cirq
25+
26+
from qualtran.cirq_interop import CirqQuregT
27+
from qualtran.resource_counting import BloqCountT, SympySymbolAllocator
28+
from qualtran.simulation.classical_sim import ClassicalValT
29+
30+
31+
@frozen
32+
class Toffoli(Bloq):
33+
"""The Toffoli gate.
34+
35+
This will flip the target bit if both controls are active. It can be thought of as
36+
a reversible AND gate.
37+
38+
Like `TGate`, this is a common compilation target. The Clifford+Toffoli gateset is
39+
universal.
40+
41+
References:
42+
[Novel constructions for the fault-tolerant Toffoli gate](https://arxiv.org/abs/1212.5069).
43+
Cody Jones. 2012. Provides a decomposition into 4 `TGate`.
44+
"""
45+
46+
@cached_property
47+
def signature(self) -> Signature:
48+
return Signature([Register('ctrl', 1, shape=(2,)), Register('target', 1)])
49+
50+
def bloq_counts(self, ssa: Optional['SympySymbolAllocator'] = None) -> Set['BloqCountT']:
51+
return {(4, TGate())}
52+
53+
def on_classical_vals(
54+
self, ctrl: 'ClassicalValT', target: 'ClassicalValT'
55+
) -> Dict[str, 'ClassicalValT']:
56+
assert target in [0, 1]
57+
if ctrl[0] == 1 and ctrl[1] == 1:
58+
target = (target + 1) % 2
59+
60+
return {'ctrl': ctrl, 'target': target}
61+
62+
def as_cirq_op(
63+
self, qubit_manager: 'cirq.QubitManager', ctrl: 'CirqQuregT', target: 'CirqQuregT'
64+
) -> Tuple[Union['cirq.Operation', None], Dict[str, 'CirqQuregT']]:
65+
import cirq
66+
67+
(trg,) = target
68+
return cirq.CCNOT(*ctrl[:, 0], trg), {'ctrl': ctrl, 'target': target}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import itertools
15+
16+
import cirq
17+
18+
from qualtran import BloqBuilder
19+
from qualtran.bloqs.basic_gates import TGate, Toffoli, ZeroState
20+
from qualtran.resource_counting import get_bloq_counts_graph
21+
22+
23+
def _make_Toffoli():
24+
from qualtran.bloqs.basic_gates import Toffoli
25+
26+
return Toffoli()
27+
28+
29+
def test_toffoli_t_count():
30+
counts = Toffoli().bloq_counts()
31+
assert counts == {(4, TGate())}
32+
33+
_, sigma = get_bloq_counts_graph(Toffoli())
34+
assert sigma == {TGate(): 4}
35+
36+
37+
def test_toffoli_cirq():
38+
bb = BloqBuilder()
39+
c0, c1, trg = [bb.add(ZeroState()) for _ in range(3)]
40+
ctrl, target = bb.add(Toffoli(), ctrl=[c0, c1], target=trg)
41+
ctrl, target = bb.add(Toffoli(), ctrl=ctrl, target=target)
42+
cbloq = bb.finalize(q0=ctrl[0], q1=ctrl[1], q2=target)
43+
44+
circuit, qubits = cbloq.to_cirq_circuit()
45+
cirq.testing.assert_has_diagram(
46+
circuit,
47+
"""\
48+
_c(0): ───@───@───
49+
│ │
50+
_c(1): ───@───@───
51+
│ │
52+
_c(2): ───X───X───""",
53+
)
54+
55+
56+
def test_classical_sim():
57+
tof = Toffoli()
58+
59+
for c0, c1 in itertools.product([0, 1], repeat=2):
60+
ctrl, target = tof.call_classically(ctrl=[c0, c1], target=0)
61+
assert ctrl.tolist() == [c0, c1]
62+
if c0 == 1 and c1 == 1:
63+
assert target == 1
64+
else:
65+
assert target == 0
66+
67+
68+
def test_classical_sim_2():
69+
bb = BloqBuilder()
70+
c0, c1, trg = [bb.add(ZeroState()) for _ in range(3)]
71+
ctrl, target = bb.add(Toffoli(), ctrl=[c0, c1], target=trg)
72+
ctrl, target = bb.add(Toffoli(), ctrl=ctrl, target=target)
73+
cbloq = bb.finalize(q0=ctrl[0], q1=ctrl[1], q2=target)
74+
75+
b0, b1, b2 = cbloq.call_classically()
76+
assert b0 == 0
77+
assert b1 == 0
78+
assert b2 == 0

0 commit comments

Comments
 (0)