Skip to content

Commit 7f2b156

Browse files
authored
[open-systems] More classical data types (#1717)
Some ideas on how to handle the fact that for a given _encoding_ we usually want both a quantum data type and classical data type. For example, QInt and CInt both encode mathematical integers into bits or qubits in the same way but in a circuit we want to distinguish between a quantum integer and a classical integer. - Introduce `BitEncoding` abstract base class - Each data type "has a" `BitEncoding` - Shimming for backwards compatibility - Pretty easy to make data types based purely on composition I think it's important to have distinct objects for common quantum and classical data types e.g. QBit CBit QInt CInt. Maybe it's annoying to always have to define two classes for each BitEncoding (the quantum and classical one). You could use something like the `ClassicalVersion` meta-data-type in this PR. I'm worried that: right now data type compatibility is `dt1 == dt2` but if we have `ClassicalVersion(QInt)` representing the same thing as `CInt` it gets complicated. Generally, I'd prioritize making the end user (quantum programmer)'s life easier with top-level quantum and classical versions of everything at the expense of the "developer" (introducer of new quantum data types) having to do some boilerplate.
1 parent f67634f commit 7f2b156

File tree

8 files changed

+1265
-461
lines changed

8 files changed

+1265
-461
lines changed

qualtran/DataTypes.ipynb

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -283,49 +283,105 @@
283283
"source": [
284284
"## `QDType`, `CDType`, and `QCDType`\n",
285285
"\n",
286-
"Quantum variables are essential when authoring quantum programs, but we live in a classical world. Measuring a qubit yields a classical bit, and a program can do classical branching (choosing which quantum operations to execute based on a classical bit). Each data type we've seen so far is a quantum data type and inherits from `QDType`. "
286+
"Quantum variables are essential when authoring quantum programs, but we live in a classical world. Measuring a qubit yields a classical bit, and a program can do classical branching (choosing which quantum operations to execute based on a classical bit). Each data type we've seen so far is a quantum data type and inherits from `QDType`. There is a more general base class: `QCDType` that includes both quantum and classical data types. Classical data types inherit from `CDType`"
287287
]
288288
},
289289
{
290290
"cell_type": "code",
291291
"execution_count": null,
292-
"id": "06c5c158-555b-48cd-b612-e00d03b82f70",
292+
"id": "25191c43-18d1-49fd-9a76-363cc3b7548c",
293293
"metadata": {},
294294
"outputs": [],
295295
"source": [
296-
"from qualtran import QDType\n",
297-
"\n",
298-
"print(\"QBit() is QDType:\", isinstance(QBit(), QDType), \"; num_qubits =\", QBit().num_qubits)\n",
299-
"print(\"QUInt(4) is QDType:\", isinstance(QUInt(4), QDType), \"; num_qubits =\", QUInt(4).num_qubits)"
296+
"# Make a list of all the built-in data types\n",
297+
"import qualtran.dtype as qdt\n",
298+
"\n",
299+
"dtypes = [\n",
300+
" qdt.QBit(), qdt.CBit(),\n",
301+
" qdt.QAny(8),\n",
302+
" qdt.QInt(8), qdt.CInt(8),\n",
303+
" qdt.QIntOnesComp(8), qdt.CIntOnesComp(8),\n",
304+
" qdt.QUInt(8), qdt.CUInt(8),\n",
305+
" qdt.BQUInt(8, 230), qdt.BCUInt(8, 230),\n",
306+
" qdt.QFxp(8, 2), qdt.CFxp(8, 2),\n",
307+
" qdt.QMontgomeryUInt(8, 227), qdt.CMontgomeryUInt(8, 227),\n",
308+
" qdt.QGF(3, 4), qdt.CGF(3, 4),\n",
309+
" qdt.QGFPoly(4, qdt.QGF(2,2)), qdt.CGFPoly(4, qdt.CGF(2,2)),\n",
310+
"]"
300311
]
301312
},
302313
{
303314
"cell_type": "markdown",
304-
"id": "c434a68e-b691-4a35-bc81-49b7159728f8",
315+
"id": "701ebb85-8e44-4456-95da-ef2ead979c5f",
305316
"metadata": {},
306317
"source": [
307-
"There is a more general base class: `QCDType` that includes both quantum and classical data types. Classical data types inherit from `CDType`"
318+
"### Table of data type properties"
308319
]
309320
},
310321
{
311322
"cell_type": "code",
312323
"execution_count": null,
313-
"id": "c983e4cb-ac84-497b-883f-3a71abf6878f",
324+
"id": "7c38eb9b-60d5-4d0c-b282-19f2638bc483",
314325
"metadata": {},
315326
"outputs": [],
316327
"source": [
317-
"from qualtran import QCDType, QDType, CDType, CBit\n",
318-
"\n",
319-
"dtypes = [QBit(), QUInt(4), CBit()]\n",
320-
"\n",
321-
"print(f\"{'dtype':10} {'QCDType?':9s} {'QDType?':9s} {'CDType?':9s}\"\n",
322-
" f\"{'bits':>6s} {'qubits':>6s} {'cbits':>6s}\"\n",
323-
" )\n",
328+
"print(f\"{'dtype':23} {'QCDType?':9s} \"\n",
329+
" f\"{'QDType?':9s} {'CDType?':9s} \"\n",
330+
" f\"{'bits':>6s} {'qubits':>6s} {'cbits':>6s}\")\n",
331+
"print(\"-\"*80)\n",
332+
"for dtype in dtypes:\n",
333+
" print(f\"{dtype!s:23} {isinstance(dtype, qdt.QCDType)!s:9} \"\n",
334+
" f\"{isinstance(dtype, qdt.QDType)!s:9} {isinstance(dtype, qdt.CDType)!s:9}\"\n",
335+
" f\"{dtype.num_bits:6d} {dtype.num_qubits:6d} {dtype.num_cbits:6d}\")"
336+
]
337+
},
338+
{
339+
"cell_type": "markdown",
340+
"id": "c2089b62-36ce-4c90-b794-4e5e8338de47",
341+
"metadata": {},
342+
"source": [
343+
"### Table of data type encoding examples"
344+
]
345+
},
346+
{
347+
"cell_type": "code",
348+
"execution_count": null,
349+
"id": "dd0c1fd9-94d3-45fd-8766-c18615fc5123",
350+
"metadata": {},
351+
"outputs": [],
352+
"source": [
353+
"print(f\"{'dtype':23} {'val':>6s} \"\n",
354+
" f\"{'encoding':s}\")\n",
324355
"print(\"-\"*60)\n",
325356
"for dtype in dtypes:\n",
326-
" print(f\"{dtype!s:10} {isinstance(dtype, QCDType)!s:9} {isinstance(dtype, QDType)!s:9} {isinstance(dtype, CDType)!s:9}\"\n",
327-
" f\"{dtype.num_bits:6d} {dtype.num_qubits:6d} {dtype.num_cbits:6d}\"\n",
328-
" )"
357+
" if isinstance(dtype, QAny):\n",
358+
" print(f\"{dtype!s:23} {'':6s} [opaque encoding]\")\n",
359+
" continue\n",
360+
" vals = list(dtype.get_classical_domain())\n",
361+
"\n",
362+
" val = vals[0] # take the minimum\n",
363+
" val_bits = dtype.to_bits(val)\n",
364+
" val_bits_str = ','.join(str(bit) for bit in val_bits)\n",
365+
" print(f\"{dtype!s:23} {val!s:>6} {val_bits_str}\")\n",
366+
"\n",
367+
" val = vals[-1] # take the maximum\n",
368+
" val_bits = dtype.to_bits(val)\n",
369+
" val_bits_str = ','.join(str(bit) for bit in val_bits)\n",
370+
" print(f\"{'':23} {val!s:>6} {val_bits_str}\")"
371+
]
372+
},
373+
{
374+
"cell_type": "markdown",
375+
"id": "a40933a4-1656-4a5a-b352-a09daf626518",
376+
"metadata": {},
377+
"source": [
378+
"## Adding a new type\n",
379+
"\n",
380+
"You can add custom types by implementing the `_BitEncoding` interface and defining a light-weight `QDType` and/or `CDType`.\n",
381+
"\n",
382+
" 1. Define a class that implements the `BitEncoding[T]` abstract interface. the generic `T` should be the type of the analogous Python classical type. For example, `_Int` inherits from `BitEncoding[int]` and defines methods `to_bits`, `from_bits`, etc.\n",
383+
" 2. Define a _quantum_ data type class that inherits from the `QDType[T]` base class. Override the abstract property `_bit_encoding` to return an instance of the bit encoding class you defined before. For example, `QInt` inherits from `QDType[int]` and its `_bit_encoding` property returns `_Int(self.bitsize)`. \n",
384+
" 3. Perform an analogous step to deinfe a _classical_ data type class that inherits from `CDType[T]` (but returns the same `BitEncoding` implementation). For example, `CInt` inherits from `CDType[int]` and its `_bit_encoding` property returns `_Int(self.bitsize)`. "
329385
]
330386
}
331387
],
@@ -345,7 +401,7 @@
345401
"name": "python",
346402
"nbconvert_exporter": "python",
347403
"pygments_lexer": "ipython3",
348-
"version": "3.11.8"
404+
"version": "3.13.9"
349405
}
350406
},
351407
"nbformat": 4,

qualtran/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,21 @@
5858
QBit,
5959
CBit,
6060
QInt,
61+
CInt,
6162
QIntOnesComp,
63+
CIntOnesComp,
6264
QUInt,
65+
CUInt,
6366
BQUInt,
67+
BCUInt,
6468
QFxp,
69+
CFxp,
6570
QMontgomeryUInt,
71+
CMontgomeryUInt,
6672
QGF,
73+
CGF,
6774
QGFPoly,
75+
CGFPoly,
6876
QDTypeCheckingSeverity,
6977
check_dtypes_consistent,
7078
)

0 commit comments

Comments
 (0)