Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize gate count of Subtract to n-1 Toffolis #1057

Merged
merged 25 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 shoudl 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
Loading