Skip to content

Commit

Permalink
Optimize gate count of Subtract to n-1 Toffolis (#1057)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoureldinYosri authored Jul 19, 2024
1 parent 0968762 commit 1a0e3ab
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 63 deletions.
18 changes: 16 additions & 2 deletions qualtran/bloqs/arithmetic/addition.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,19 @@ def on_classical_vals(
) -> Dict[str, 'ClassicalValT']:
unsigned = isinstance(self.a_dtype, (QUInt, QMontgomeryUInt))
b_bitsize = self.b_dtype.bitsize
N = 2**b_bitsize if unsigned else 2 ** (b_bitsize - 1)
return {'a': a, 'b': int(math.fmod(a + b, N))}
N = 2**b_bitsize
if unsigned:
return {'a': a, 'b': int((a + b) % N)}

# Addition of signed integers can result in overflow. In most classical programming languages (e.g. C++)
# what happens when an overflow happens is left as an implementation detail for compiler designers.
# However for quantum subtraction the operation should be unitary and that means that the unitary of
# the bloq should be a permutation matrix.
# If we hold `a` constant then the valid range of values of `b` [-N/2, N/2) gets shifted forward or backwards
# by `a`. to keep the operation unitary overflowing values wrap around. this is the same as moving the range [0, N)
# by the same amount modulu $N$. that is add N/2 before addition and then remove it.
half_n = N >> 1
return {'a': a, 'b': int(a + b + half_n) % N - half_n}

def _circuit_diagram_info_(self, _) -> cirq.CircuitDiagramInfo:
wire_symbols = ["In(x)"] * int(self.a_dtype.bitsize)
Expand Down Expand Up @@ -188,6 +199,9 @@ def decompose_from_registers(
# reverse the order of qubits for big endian-ness.
input_bits = quregs['a'][::-1]
output_bits = quregs['b'][::-1]
if self.b_dtype.bitsize == 1:
yield CNOT().on(input_bits[0], output_bits[0])
return
ancillas = context.qubit_manager.qalloc(self.b_dtype.bitsize - 1)[::-1]
# Start off the addition by anding into the ancilla
yield And().on(input_bits[0], output_bits[0], ancillas[0])
Expand Down
7 changes: 7 additions & 0 deletions qualtran/bloqs/arithmetic/addition_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@ def test_classical_add_k_unsigned(bitsize, k, x, cvs, ctrls, result):
assert bloq_classical[-1] == result


@pytest.mark.parametrize('bitsize', range(2, 5))
def test_classical_add_signed_overflow(bitsize):
bloq = Add(QInt(bitsize))
mx = 2 ** (bitsize - 1) - 1
assert bloq.call_classically(a=mx, b=mx) == (mx, -2)


# TODO: write tests for signed integer addition (subtraction)
# https://github.com/quantumlib/Qualtran/issues/606
@pytest.mark.parametrize('bitsize,k,x,cvs,ctrls,result', [(5, 2, 0, (1, 0), (1, 0), 2)])
Expand Down
74 changes: 46 additions & 28 deletions qualtran/bloqs/arithmetic/subtraction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "5e1ac363",
"id": "3f62c768",
"metadata": {
"cq.autogen": "title_cell"
},
Expand All @@ -13,7 +13,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3a2de513",
"id": "1e50eeb1",
"metadata": {
"cq.autogen": "top_imports"
},
Expand All @@ -30,31 +30,36 @@
},
{
"cell_type": "markdown",
"id": "b91ab938",
"id": "9c974476",
"metadata": {
"cq.autogen": "Subtract.bloq_doc.md"
},
"source": [
"## `Subtract`\n",
"An n-bit subtraction gate.\n",
"\n",
"Implements $U|a\\rangle|b\\rangle \\rightarrow |a\\rangle|a-b\\rangle$ using $4n - 4 T$ gates.\n",
"Implements $U|a\\rangle|b\\rangle \\rightarrow |a\\rangle|a-b\\rangle$ using $4n - 4$ T gates.\n",
"\n",
"This first negates $b$ (assuming a two's complement representation), and then adds $a$ into it.\n",
"This construction uses the relation `a - b = ~(~a + b)` to turn the operation into addition. This relation is used in\n",
"[Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization](https://arxiv.org/pdf/2007.07391)\n",
"to turn addition into subtraction conditioned on a qubit.\n",
"\n",
"#### Parameters\n",
" - `a_dtype`: Quantum datatype used to represent the integer a.\n",
" - `b_dtype`: Quantum datatype used to represent the integer b. Must be large enough to hold the result in the output register of a - b, or else it simply drops the most significant bits. If not specified, b_dtype is set to a_dtype. \n",
"\n",
"#### Registers\n",
" - `a`: A a_dtype.bitsize-sized input register (register a above).\n",
" - `b`: A b_dtype.bitsize-sized input/output register (register b above).\n"
" - `b`: A b_dtype.bitsize-sized input/output register (register b above). \n",
"\n",
"#### References\n",
" - [Compilation of Fault-Tolerant Quantum Heuristics for Combinatorial Optimization, page 9](https://arxiv.org/pdf/2007.07391). \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "64dd238f",
"id": "0af034cf",
"metadata": {
"cq.autogen": "Subtract.bloq_doc.py"
},
Expand All @@ -65,7 +70,7 @@
},
{
"cell_type": "markdown",
"id": "4f77ad26",
"id": "9156f4e7",
"metadata": {
"cq.autogen": "Subtract.example_instances.md"
},
Expand All @@ -76,7 +81,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "2cd50aa6",
"id": "135553b1",
"metadata": {
"cq.autogen": "Subtract.sub_symb"
},
Expand All @@ -89,7 +94,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e2862687",
"id": "84c7207a",
"metadata": {
"cq.autogen": "Subtract.sub_small"
},
Expand All @@ -101,7 +106,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8b6584fc",
"id": "e98b3206",
"metadata": {
"cq.autogen": "Subtract.sub_large"
},
Expand All @@ -113,7 +118,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "27312995",
"id": "e003f5ad",
"metadata": {
"cq.autogen": "Subtract.sub_diff_size_regs"
},
Expand All @@ -122,9 +127,22 @@
"sub_diff_size_regs = Subtract(QInt(bitsize=4), QInt(bitsize=16))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4aabfa98",
"metadata": {
"cq.autogen": "Subtract.sub_symp_decomposition"
},
"outputs": [],
"source": [
"n = sympy.Symbol('n')\n",
"sub_symp_decomposition = Subtract(QInt(bitsize=n)).decompose_bloq()"
]
},
{
"cell_type": "markdown",
"id": "82c580af",
"id": "9b580c5b",
"metadata": {
"cq.autogen": "Subtract.graphical_signature.md"
},
Expand All @@ -135,20 +153,20 @@
{
"cell_type": "code",
"execution_count": null,
"id": "4e508f72",
"id": "aa412d69",
"metadata": {
"cq.autogen": "Subtract.graphical_signature.py"
},
"outputs": [],
"source": [
"from qualtran.drawing import show_bloqs\n",
"show_bloqs([sub_symb, sub_small, sub_large, sub_diff_size_regs],\n",
" ['`sub_symb`', '`sub_small`', '`sub_large`', '`sub_diff_size_regs`'])"
"show_bloqs([sub_symb, sub_small, sub_large, sub_diff_size_regs, sub_symp_decomposition],\n",
" ['`sub_symb`', '`sub_small`', '`sub_large`', '`sub_diff_size_regs`', '`sub_symp_decomposition`'])"
]
},
{
"cell_type": "markdown",
"id": "a282a369",
"id": "6fad45f3",
"metadata": {
"cq.autogen": "Subtract.call_graph.md"
},
Expand All @@ -159,7 +177,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "43f1884d",
"id": "6a3d1dbc",
"metadata": {
"cq.autogen": "Subtract.call_graph.py"
},
Expand All @@ -173,7 +191,7 @@
},
{
"cell_type": "markdown",
"id": "12650fb1",
"id": "c4da55d6",
"metadata": {
"cq.autogen": "SubtractFrom.bloq_doc.md"
},
Expand All @@ -200,7 +218,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "746c25eb",
"id": "ccc21662",
"metadata": {
"cq.autogen": "SubtractFrom.bloq_doc.py"
},
Expand All @@ -211,7 +229,7 @@
},
{
"cell_type": "markdown",
"id": "75ea545f",
"id": "b8182c9f",
"metadata": {
"cq.autogen": "SubtractFrom.example_instances.md"
},
Expand All @@ -222,7 +240,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e7ee57a8",
"id": "4b56cde9",
"metadata": {
"cq.autogen": "SubtractFrom.sub_from_symb"
},
Expand All @@ -235,7 +253,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8123a738",
"id": "cc45ae49",
"metadata": {
"cq.autogen": "SubtractFrom.sub_from_small"
},
Expand All @@ -247,7 +265,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e469b17a",
"id": "8e40b0fb",
"metadata": {
"cq.autogen": "SubtractFrom.sub_from_large"
},
Expand All @@ -258,7 +276,7 @@
},
{
"cell_type": "markdown",
"id": "a32af1e3",
"id": "6d965b01",
"metadata": {
"cq.autogen": "SubtractFrom.graphical_signature.md"
},
Expand All @@ -269,7 +287,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "0fd2dbd7",
"id": "093d2a0d",
"metadata": {
"cq.autogen": "SubtractFrom.graphical_signature.py"
},
Expand All @@ -282,7 +300,7 @@
},
{
"cell_type": "markdown",
"id": "47e50576",
"id": "420fd950",
"metadata": {
"cq.autogen": "SubtractFrom.call_graph.md"
},
Expand All @@ -293,7 +311,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "4564d561",
"id": "7d1b9b0e",
"metadata": {
"cq.autogen": "SubtractFrom.call_graph.py"
},
Expand Down
Loading

0 comments on commit 1a0e3ab

Please sign in to comment.