diff --git a/qualtran/_infra/data_types.py b/qualtran/_infra/data_types.py index ee918d506..82c7bcdde 100644 --- a/qualtran/_infra/data_types.py +++ b/qualtran/_infra/data_types.py @@ -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 @@ -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 @@ -181,7 +181,7 @@ def __str__(self): @attrs.frozen class QAny(QDType): - """Opaque bag-of-qbits type.""" + """Opaque bag-of-qubits type.""" bitsize: SymbolicInt @@ -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. @@ -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. @@ -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. diff --git a/qualtran/_infra/data_types_test.py b/qualtran/_infra/data_types_test.py index 65252c5cb..ff08b7df6 100644 --- a/qualtran/_infra/data_types_test.py +++ b/qualtran/_infra/data_types_test.py @@ -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): @@ -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): diff --git a/qualtran/bloqs/basic_gates/rotation.py b/qualtran/bloqs/basic_gates/rotation.py index 9adb439e1..ae297c974 100644 --- a/qualtran/bloqs/basic_gates/rotation.py +++ b/qualtran/bloqs/basic_gates/rotation.py @@ -108,7 +108,7 @@ def _z_pow() -> ZPowGate: @frozen class CZPowGate(CirqGateAsBloqBase): - exponent: float = 1.0 + exponent: SymbolicFloat = 1.0 global_shift: float = 0.0 eps: SymbolicFloat = 1e-11 diff --git a/qualtran/cirq_interop/_cirq_to_bloq.py b/qualtran/cirq_interop/_cirq_to_bloq.py index f71810df9..b74383534 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq.py +++ b/qualtran/cirq_interop/_cirq_to_bloq.py @@ -150,9 +150,11 @@ def cirq_gate(self) -> cirq.Gate: def my_static_costs(self, cost_key: 'CostKey'): if isinstance(cost_key, QECGatesCost): t_count = _from_directly_countable_cirq(self.cirq_gate) - if t_count is None: - raise ValueError(f"Cirq gate must be directly countable, not {self.cirq_gate}") - return GateCounts(t=t_count.t, rotation=t_count.rotations, clifford=t_count.clifford) + if t_count is not None: + return GateCounts( + t=t_count.t, rotation=t_count.rotations, clifford=t_count.clifford + ) + return NotImplemented def _cirq_wire_symbol_to_qualtran_wire_symbol(symbol: str, side: Side) -> 'WireSymbol': diff --git a/qualtran/cirq_interop/_cirq_to_bloq_test.py b/qualtran/cirq_interop/_cirq_to_bloq_test.py index a016d85a6..314c404a4 100644 --- a/qualtran/cirq_interop/_cirq_to_bloq_test.py +++ b/qualtran/cirq_interop/_cirq_to_bloq_test.py @@ -17,6 +17,7 @@ import cirq import numpy as np import pytest +import sympy from attrs import frozen import qualtran @@ -34,11 +35,12 @@ SoquetT, ) from qualtran._infra.gate_with_registers import get_named_qubits -from qualtran.bloqs.basic_gates import CNOT, GlobalPhase, OneState +from qualtran.bloqs.basic_gates import CNOT, CZPowGate, GlobalPhase, OneState, YPowGate from qualtran.bloqs.bookkeeping import Allocate, Free, Join, Split from qualtran.bloqs.mcmt.and_bloq import And from qualtran.cirq_interop import cirq_optree_to_cbloq, CirqGateAsBloq, CirqQuregT from qualtran.cirq_interop.t_complexity_protocol import TComplexity +from qualtran.resource_counting import GateCounts, get_cost_value, QECGatesCost @frozen @@ -228,3 +230,16 @@ def test_cirq_gate_as_bloq_decompose_raises(): def test_cirq_gate_as_bloq_diagram_info(): assert cirq.circuit_diagram_info(GlobalPhase(exponent=0.5)) is None + + +def test_cirq_gate_cost_via_decomp(): + theta = sympy.Symbol("theta", real=True) + cirq_swappow = cirq.SwapPowGate(exponent=theta) + + swappow_bloq = CirqGateAsBloq(cirq_swappow) + + _, sigma = swappow_bloq.call_graph() + assert sigma == {CNOT(): 2, YPowGate(0.5): 1, YPowGate(-0.5): 1, CZPowGate(theta): 1} + + gc_swappow = get_cost_value(swappow_bloq, QECGatesCost()) + assert gc_swappow == GateCounts(clifford=4, rotation=1)