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

More explicit documentation of endianness #1500

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 17 additions & 7 deletions qualtran/_infra/data_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
We often wish to write algorithms which operate on quantum data. One can think
of quantum data types, similar to classical data types, where a collection of
qubits can be used to represent a specific quantum data type (eg: a quantum
integer of width 32 would comprise of 32 qubits, similar to a classical uint32
integer of width 32 would comprise 32 qubits, similar to a classical uint32
type). More generally, many current primitives and algorithms in qualtran
implicitly expect registers which represent signed or unsigned integers,
fixed-point (fp) numbers , or “classical registers” which store some classical
Expand All @@ -40,7 +40,7 @@
type assuming the bitsizes match. QInt(32) == QAny(32), QInt(32) !=
QFxp(32, 16). QInt(32) != QUInt(32).
5. We assume a big endian convention for addressing QBits in registers
throughout qualtran. Recall that in a big endian convention the most signficant
throughout qualtran. Recall that in a big endian convention the most significant
bit is at index 0. If you iterate through the bits in a register they will be
yielded from most significant to least significant.
6. Ones' complement integers are used extensively in quantum algorithms. We have
Expand Down Expand Up @@ -181,7 +181,7 @@ def __str__(self):

@attrs.frozen
class QAny(QDType):
"""Opaque bag-of-qbits type."""
"""Opaque bag-of-qubits type."""

bitsize: SymbolicInt

Expand Down Expand Up @@ -214,7 +214,10 @@ def assert_valid_classical_val_array(self, val_array, debug_str: str = 'val'):
class QInt(QDType):
"""Signed Integer of a given width bitsize.

A two's complement representation is assumed for negative integers.
A two's complement representation is used for negative integers.

Here (and throughout Qualtran), we use a big-endian bit convention. The most significant
bit is at index 0.

Attributes:
bitsize: The number of qubits used to represent the integer.
Expand Down Expand Up @@ -275,7 +278,11 @@ def __str__(self):
class QIntOnesComp(QDType):
"""Signed Integer of a given width bitsize.

A ones' complement representation is assumed for negative integers.
In contrast to `QInt`, this data type uses the ones' complement representation for negative
integers.

Here (and throughout Qualtran), we use a big-endian bit convention. The most significant
bit is at index 0.

Attributes:
bitsize: The number of qubits used to represent the integer.
Expand Down Expand Up @@ -323,8 +330,11 @@ def assert_valid_classical_val(self, val, debug_str: str = 'val'):
class QUInt(QDType):
"""Unsigned integer of a given width bitsize which wraps around upon overflow.

Similar to unsigned integer types in C. Any intended wrap around effect is
expected to be handled by the developer.
Any intended wrap around effect is expected to be handled by the developer, similar
to an unsigned integer type in C.

Here (and throughout Qualtran), we use a big-endian bit convention. The most significant
bit is at index 0.

Attributes:
bitsize: The number of qubits used to represent the integer.
Expand Down
7 changes: 7 additions & 0 deletions qualtran/_infra/data_types_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ def test_qint_to_and_from_bits():
assert qint4.from_bits(qint4.to_bits(x)) == x
assert list(qint4.to_bits(-2)) == [1, 1, 1, 0]
assert list(QInt(4).to_bits(2)) == [0, 0, 1, 0]
# MSB at lowest index -- big-endian
assert qint4.from_bits([0, 0, 0, 1]) == 1
assert qint4.from_bits([0, 0, 0, 1]) < qint4.from_bits([0, 1, 0, 0])
assert qint4.from_bits(qint4.to_bits(-2)) == -2
assert qint4.from_bits(qint4.to_bits(2)) == 2
with pytest.raises(ValueError):
Expand Down Expand Up @@ -325,6 +328,10 @@ def test_quint_to_and_from_bits():
assert [*quint4.get_classical_domain()] == [*range(0, 16)]
assert list(quint4.to_bits(10)) == [1, 0, 1, 0]
assert quint4.from_bits(quint4.to_bits(10)) == 10
# MSB at lowest index -- big-endian
assert quint4.from_bits([0, 0, 0, 1]) == 1
assert quint4.from_bits([0, 0, 0, 1]) < quint4.from_bits([1, 0, 0, 0])

for x in range(16):
assert quint4.from_bits(quint4.to_bits(x)) == x
with pytest.raises(ValueError):
Expand Down
Loading