Skip to content

Commit 57d14d0

Browse files
authored
Support symbolic parameters in QROM bloq (#945)
* Move symbolic_counting_utils to a new module * Add is_symbolic to data types and registers * Fix mypy errors * Fix tests * Support symbolic parameters in QROM bloq * Clear output of notebook * Improve docstrings * Fix formatting in docstrings
1 parent 9db3b44 commit 57d14d0

7 files changed

Lines changed: 356 additions & 98 deletions

File tree

qualtran/bloqs/data_loading/qrom.ipynb

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,77 @@
4040
"## `QROM`\n",
4141
"Bloq to load `data[l]` in the target register when the selection stores an index `l`.\n",
4242
"\n",
43-
"In the case of multidimensional `data[p,q,r,...]` we use multiple named\n",
44-
"selection registers to index and load the data named selection0, selection1, ...\n",
43+
"## Overview\n",
44+
"The action of a QROM can be described as\n",
45+
"$$\n",
46+
" \\text{QROM}_{s_1, s_2, \\dots, s_K}^{d_1, d_2, \\dots, d_L}\n",
47+
" |s_1\\rangle |s_2\\rangle \\dots |s_K\\rangle\n",
48+
" |0\\rangle^{\\otimes b_1} |0\\rangle^{\\otimes b_2} \\dots |0\\rangle^{\\otimes b_L}\n",
49+
" \\rightarrow\n",
50+
" |s_1\\rangle |s_2\\rangle \\dots |s_K\\rangle\n",
51+
" |d_1[s_1, s_2, \\dots, s_k]\\rangle\n",
52+
" |d_2[s_1, s_2, \\dots, s_k]\\rangle \\dots\n",
53+
" |d_L[s_1, s_2, \\dots, s_k]\\rangle\n",
54+
"$$\n",
4555
"\n",
46-
"When the input data elements contain consecutive entries of identical data elements to\n",
56+
"There two high level parameters that control the behavior of a QROM are -\n",
57+
"\n",
58+
"1. Shape of the classical dataset to be loaded ($\\text{data.shape} = (S_1, S_2, ..., S_K)$).\n",
59+
"2. Number of distinct datasets to be loaded ($\\text{data.bitsizes} = (b_1, b_2, ..., b_L)$).\n",
60+
"\n",
61+
"Each of these have an effect on the cost of the QROM. The `data_or_shape` parameter stores\n",
62+
"either\n",
63+
"1. A numpy array of shape $(L, S_1, S_2, ..., S_K)$ when $L$ classical datasets, each of\n",
64+
" shape $(S_1, S_2, ..., S_K)$ and bitsizes $(b_1, b_2, ..., b_L)$ are to be loaded and\n",
65+
" the classical data is available to instantiate the QROM bloq. In this case, the helper\n",
66+
" builder `QROM.build_from_data(data_1, data_2, ..., data_L)` can be used to build the QROM.\n",
67+
"\n",
68+
"2. A `Shaped` object that stores a (potentially symbolic) tuple $(L, S_1, S_2, ..., S_K)$\n",
69+
" that represents the number of classical datasets `L=data_or_shape.shape[0]` and\n",
70+
" their shape `data_shape=data_or_shape.shape[1:]` to be loaded by this QROM. This is used\n",
71+
" to instantiate QROM bloqs for symbolic cost analysis where the exact data to be loaded\n",
72+
" is not known. In this case, the helper builder `QROM.build_from_bitsize` can be used\n",
73+
" to build the QROM.\n",
74+
"\n",
75+
"### Shape of the classical dataset to be loaded.\n",
76+
"QROM bloq supports loading multidimensional classical datasets. In order to load a data\n",
77+
"set of shape $\\mathrm{data.shape} == (P, Q, R, S)$ the QROM bloq needs four selection\n",
78+
"registers with bitsizes $(p, q, r, s)$ where\n",
79+
"$p,q,r,s=\\log_2{P}, \\log_2{Q}, \\log_2{R}, \\log_2{S}$.\n",
80+
"\n",
81+
"In general, to load K dimensional data, we use K named selection registers `(selection0,\n",
82+
"selection1, ..., selection{k})` to index and load the data.\n",
83+
"\n",
84+
"The T/Toffoli cost of the QROM scales linearly with the number of elements in the dataset\n",
85+
"(i.e. $\\mathcal{O}(\\mathrm{np.prod(data.shape)}$).\n",
86+
"\n",
87+
"### Number of distinct datasets to be loaded, and their corresponding target bitsize.\n",
88+
"To load a classical dataset into a target register of bitsize $b$, the clifford cost of a QROM\n",
89+
"scales as $\\mathcal{O}(b \\mathrm{np.prod}(\\mathrm{data.shape}))$. This is because we need\n",
90+
"$\\mathcal{O}(b)$ CNOT gates to load the ith data element in the target register when the\n",
91+
"selection register stores index $i$.\n",
92+
"\n",
93+
"If you have multiple classical datasets `(data_1, data_2, data_3, ..., data_L)` to be loaded\n",
94+
"and each of them has the same shape `(data_1.shape == data_2.shape == ... == data_L.shape)`\n",
95+
"and different target bitsizes `(b_1, b_2, ..., b_L)`, then one construct a single classical\n",
96+
"dataset `data = merge(data_1, data_2, ..., data_L)` where\n",
97+
"\n",
98+
"- `data.shape == data_1.shape == data_2.shape == ... == data_L` and\n",
99+
"- `data[idx] = f'{data_1[idx]!0{b_1}b}' + f'{data_2[idx]!0{b_2}b}' + ... + f'{data_L[idx]!0{b_L}b}'`\n",
100+
"\n",
101+
"Thus, the target bitsize of the merged dataset is $b = b_1 + b_2 + \\dots + b_L$ and clifford\n",
102+
"cost of loading merged dataset scales as\n",
103+
"$\\mathcal{O}((b_1 + b_2 + \\dots + b_L) \\mathrm{np.prod}(\\mathrm{data.shape}))$.\n",
104+
"\n",
105+
"## Variable spaced QROM\n",
106+
"When the input classical data contains consecutive entries of identical data elements to\n",
47107
"load, the QROM also implements the \"variable-spaced\" QROM optimization described in Ref [2].\n",
48108
"\n",
49109
"#### Parameters\n",
50-
" - `data`: List of numpy ndarrays specifying the data to load. If the length of this list is greater than one then we use the same selection indices to load each dataset (for example, to load alt and keep data for state preparation). Each data set is required to have the same shape and to be of integer type.\n",
51-
" - `selection_bitsizes`: The number of bits used to represent each selection register corresponding to the size of each dimension of the array. Should be the same length as the shape of each of the datasets.\n",
52-
" - `target_bitsizes`: The number of bits used to represent the data signature. This can be deduced from the maximum element of each of the datasets. Should be of length len(data), i.e. the number of datasets.\n",
53-
" - `num_controls`: The number of control signature. \n",
110+
" - `data_or_shape`: List of numpy ndarrays specifying the data to load. If the length of this list ($L$) is greater than one then we use the same selection indices to load each dataset. Each data set is required to have the same shape $(S_1, S_2, ..., S_K)$ and to be of integer type. For symbolic QROMs, pass a `Shaped` object instead with shape $(L, S_1, S_2, ..., S_K)$.\n",
111+
" - `selection_bitsizes`: The number of bits used to represent each selection register corresponding to the size of each dimension of the array $(S_1, S_2, ..., S_K)$. Should be the same length as the shape of each of the datasets.\n",
112+
" - `target_bitsizes`: The number of bits used to represent the data signature. This can be deduced from the maximum element of each of the datasets. Should be a tuple $(b_1, b_2, ..., b_L)$ of length `L = len(data)`, i.e. the number of datasets to be loaded.\n",
113+
" - `num_controls`: The number of controls. \n",
54114
"\n",
55115
"#### References\n",
56116
" - [Encoding Electronic Spectra in Quantum Circuits with Linear T Complexity](https://arxiv.org/abs/1805.03662). Babbush et. al. (2018). Figure 1.\n",
@@ -120,6 +180,18 @@
120180
"qrom_multi_dim = QROM([data1, data2], selection_bitsizes=(2, 2), target_bitsizes=(8, 8))"
121181
]
122182
},
183+
{
184+
"cell_type": "code",
185+
"execution_count": null,
186+
"id": "a084ca8c-0c89-4439-86d9-51cf91e972c4",
187+
"metadata": {},
188+
"outputs": [],
189+
"source": [
190+
"N, M, b1, b2, c = sympy.symbols('N M b1 b2 c')\n",
191+
"qrom_symb = QROM.build_from_bitsize((N, M), (b1, b2), num_controls=c)\n",
192+
"qrom_symb"
193+
]
194+
},
123195
{
124196
"cell_type": "markdown",
125197
"id": "b92d1c8e",
@@ -140,8 +212,8 @@
140212
"outputs": [],
141213
"source": [
142214
"from qualtran.drawing import show_bloqs\n",
143-
"show_bloqs([qrom_small, qrom_multi_data, qrom_multi_dim],\n",
144-
" ['`qrom_small`', '`qrom_multi_data`', '`qrom_multi_dim`'])"
215+
"show_bloqs([qrom_small, qrom_multi_data, qrom_multi_dim, qrom_symb],\n",
216+
" ['`qrom_small`', '`qrom_multi_data`', '`qrom_multi_dim`', '`qrom_symb`'])"
145217
]
146218
},
147219
{
@@ -168,6 +240,31 @@
168240
"show_call_graph(qrom_small_g)\n",
169241
"show_counts_sigma(qrom_small_sigma)"
170242
]
243+
},
244+
{
245+
"cell_type": "code",
246+
"execution_count": null,
247+
"id": "5392752e-276e-434c-9d60-fadcf6478077",
248+
"metadata": {},
249+
"outputs": [],
250+
"source": [
251+
"qrom_symb_g, qrom_symb_sigma = qrom_symb.call_graph(generalizer=ignore_split_join)\n",
252+
"show_call_graph(qrom_symb_g)\n",
253+
"show_counts_sigma(qrom_symb_sigma)"
254+
]
255+
},
256+
{
257+
"cell_type": "code",
258+
"execution_count": null,
259+
"id": "52a98d72",
260+
"metadata": {
261+
"cq.autogen": "QROM.qrom_symb"
262+
},
263+
"outputs": [],
264+
"source": [
265+
"N, M, b1, b2, c = sympy.symbols('N M b1 b2 c')\n",
266+
"qrom_symb = QROM.build_from_bitsize((N, M), (b1, b2), num_controls=c)"
267+
]
171268
}
172269
],
173270
"metadata": {
@@ -186,7 +283,7 @@
186283
"name": "python",
187284
"nbconvert_exporter": "python",
188285
"pygments_lexer": "ipython3",
189-
"version": "3.11.7"
286+
"version": "3.11.8"
190287
}
191288
},
192289
"nbformat": 4,

0 commit comments

Comments
 (0)