qupython.qubit

  1from __future__ import annotations
  2
  3
  4from contextvars import ContextVar
  5from typing import Self
  6
  7import qiskit.circuit.library as clib
  8from .err_msg import ERR_MSG
  9
 10__active_controls__ = ContextVar("qupython_active_controls", default=())
 11
 12class Bit:
 13    """
 14    Base class for `Qubit` and `BitPromise` objects; you shouldn't create an
 15    instance of this class yourself.
 16    """
 17    operations: list
 18    """History of operations on the bit"""
 19
 20    def _get_linked_bits(self, already_found=None):
 21        """
 22        Find all Bit objects that interact with this Bit through quantum
 23        circuit operations.
 24        """
 25        # TODO: unit test
 26        searched_bits = set()
 27        all_known_bits = { self }
 28        while len(searched_bits) < len(all_known_bits):
 29            for bit in (all_known_bits - searched_bits):
 30                for op in bit.operations:
 31                    all_known_bits |= set(op.qubits + op.promises)
 32                searched_bits.add(bit)
 33        return all_known_bits
 34
 35    def as_control(self) -> _ControlBitContextManager:
 36        """
 37        Return a context manager to condition quantum gates. Use this to
 38        control quantum gates using the `with` statement.
 39
 40        ```python
 41        with my_bit.as_control():
 42            qubit.x()  # conditioned on my_bit
 43        ```
 44
 45        You can condition on both `Qubit` and `BitPromise` objects.
 46        """
 47        return _ControlBitContextManager(self)
 48
 49class Qubit(Bit):
 50    """
 51    This is the main quPython object you'll interact with and the only object
 52    you should instantiate directly. Qubits start in state |0〉.
 53
 54    `Qubit` objects support the following single-qubit gate operations as
 55    methods.
 56
 57    | Name  | Qiskit object                                                                           |
 58    |-------|-----------------------------------------------------------------------------------------|
 59    | `x`   | [`XGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.XGate)         |
 60    | `y`   | [`YGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.YGate)         |
 61    | `z`   | [`ZGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.ZGate)         |
 62    | `h`   | [`HGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.HGate)         |
 63    | `s`   | [`SGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.SGate)         |
 64    | `sdg` | [`SdgGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.SdgGate)     |
 65    | `t`   | [`TGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TGate)         |
 66    | `tdg` | [`TdgGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TdgGate)     |
 67    | `p`   | [`PhaseGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PhaseGate) |
 68    | `rx`  | [`RXGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RXGate)       |
 69    | `ry`  | [`RYGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RYGate)       |
 70    | `rz`  | [`RZGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RZGate)       |
 71    | `u`   | [`UGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UGate)         |
 72
 73    These gate methods mutate the `Qubit` object and return it so you can stack them.
 74
 75    ```python
 76    qubit = Qubit().x().h()  # Qubit in state |->
 77    qubit.h()  # mutate to state |1>
 78    ```
 79
 80    The `p`, `rx`, `ry`, `rz`, and `u` gates require angles. See their
 81    corresponding Qiskit objects for a description of the gates and their
 82    angles.
 83
 84    ```python
 85    Qubit().rx(0.2)
 86    ```
 87
 88    For a controlled gate, use the `as_control` method and the `with`
 89    statement. This will control all gates inside the context by that qubit.
 90
 91    ```python
 92    qubit, another_qubit = Qubit(), Qubit()
 93    with qubit.as_control():
 94        another_qubit.x()
 95    ```
 96
 97    You can also control gates by passing a list of `Qubit`, `BitPromise`, and
 98    `bool` objects to the `conditions` argument. All conditions must be true to
 99    for the gate to apply.
100
101    ```python
102    qubit.x(conditions=[another_qubit, measurement_result, True])
103    ```
104
105    Use the `measure` method to return a `BitPromise`. These promises can
106    control quantum gates too using the `as_control` method.
107    """
108    def __init__(self):
109        self.operations = []
110        self._create_1q_gate_methods()
111
112    def __bool__(self):
113        raise ValueError(
114            "Can't cast Qubit to bool; use `.measure()` to measure"
115            " the qubit instead."
116        )
117
118    def _separate_conditions(self, conditions):
119        qubits = [c for c in conditions if isinstance(c, Qubit)]
120        promises = [c for c in conditions if isinstance(c, BitPromise)]
121        build_time_conditions = [c for c in conditions if not isinstance(c, (Qubit, BitPromise))]
122        return qubits, promises, build_time_conditions
123
124    def _create_1q_gate_methods(self):
125        """
126        Generate methods such as self.h, self.p, etc.
127        This method runs on object initialization.
128        """
129
130        # TODO: unit test
131        # TODO: neaten up
132        def _create_method(gate):
133            def add_gate(*args, **kwargs):
134                conditions = kwargs.pop("conditions", [])
135                conditions += __active_controls__.get()
136                qubits, promises, build_time_conditions = self._separate_conditions(conditions)
137                if not all(build_time_conditions):
138                    return
139                qiskit_inst = gate(*args, **kwargs)
140                if qubits:
141                    qiskit_inst = qiskit_inst.control(len(qubits))
142                inst = _quPythonInstruction(
143                    qiskit_instruction=qiskit_inst,
144                    qubits=qubits + [self],
145                    promises=promises
146                )
147                for qubit in qubits + [self]:
148                    qubit.operations.append(inst)
149                for promise in promises:
150                    promise.operations.append(inst)
151                return self
152
153            return add_gate
154
155        for gate, name in [
156            (clib.XGate, "x"),
157            (clib.YGate, "y"),
158            (clib.ZGate, "z"),
159            (clib.HGate, "h"),
160            (clib.SGate, "s"),
161            (clib.SdgGate, "sdg"),
162            (clib.TGate, "t"),
163            (clib.TdgGate, "tdg"),
164            (clib.PhaseGate, "p"),
165            (clib.RXGate, "rx"),
166            (clib.RYGate, "ry"),
167            (clib.RZGate, "rz"),
168            (clib.UGate, "u"),
169        ]:
170            setattr(self, name, _create_method(gate))
171            attr = getattr(self, name)
172            attr.__doc__ = f"Apply '{name}' gate to qubit"
173            attr.__annotations__["return"] = Self
174
175    def measure(self, conditions=None) -> BitPromise:
176        """
177        Add measure instruction to Qubit and return BitPromise
178        """
179        conditions = conditions or []
180        conditions += __active_controls__.get()
181        qubits, promises, build_time_conditions = self._separate_conditions(conditions)
182        if qubits or promises:
183            raise ConditionMeasurementAtRuntimeError(ERR_MSG["ConditionMeasurementAtRuntimeError"])
184        if not all(build_time_conditions):
185            return
186        inst = _quPythonMeasurement(self)
187        self.operations.append(inst)
188        return inst.promises[0]
189
190class BitPromise(Bit):
191    """
192    Placeholder for qubit measurement results. You should never instantiate
193    this class directly, it should only be output from `Qubit.measure`.
194
195    When you return a `BitPromise` from an `@quantum` function, quPython finds
196    the `Qubit` that created this `BitPromise` and compiles the quantum program
197    needed to calculate it.
198
199    Invert a `BitPromise` using the `~` operator. This returns a new
200    `BitPromise` with an inverted value.
201
202    ```python
203    with (~bit_promise).as_control():
204        # Apply gates if `bit_promise` is `False`
205    ```
206
207    <details>
208    <summary>Gotchas</summary>
209
210    After its value is determined, a `BitPromise` tries to behave as much like
211    a `bool` as possible. Unfortunately, there are some quirks because
212    `BitPromise` objects need to have unique hashes before circuit compilation,
213    but `bool`s all have the same hash (0 or 1) and you can't change an
214    object's hash without breaking basic Python functionality. The following
215    code snippet shows an example.
216
217    ```python
218    # Create fulfilled qubit promise
219    promise = BitPromise(None)
220    promise.value = True
221
222    # Show unexpected behavior
223    promise == True  # True
224    promise in (True, False)  # False
225    ```
226
227    For best results, cast to `bool` as soon as possible after the function
228    completes or use the `value` attribute.
229
230    If you have better ideas on how to handle this, let me know in an
231    [issue](https://github.com/frankharkins/qupython/issues/new?title=[Suggestion]:%20BitPromise%20values).
232    </details>
233    """
234
235    def __init__(self, measurement_instruction, inverse=False):
236        self.operations = [measurement_instruction]
237        self._inverse = inverse
238        self.value = None
239        """
240        Value of the `BitPromise`. This is `None` until the promise is
241        resolved.
242        """
243
244    def __bool__(self):
245        if self.value is None:
246            raise BitPromiseNotResolvedError(ERR_MSG["BitPromiseNotResolved"])
247        if self._inverse:
248            return not self.value
249        return self.value
250
251    def __int__(self):
252        return int(bool(self))
253
254    def __eq__(self, other):
255        if self.value is None:
256            return id(self) == id(other)
257        return bool(self) == other
258
259    def __hash__(self):
260        return id(self)
261
262    def __repr__(self):
263        if self.value is None:
264            return f"BitPromise({self.operations})"
265        return repr(bool(self))
266
267    def __invert__(self) -> BitPromise:
268        # TODO: This is a bit inefficient as it adds a new measurement each
269        # time we invert the promise
270        measurement_instruction = self.operations[0]
271        new_promise = BitPromise(
272            measurement_instruction,
273            inverse= not self._inverse
274        )
275        measurement_instruction.promises.append(new_promise)
276        return new_promise
277
278
279class _quPythonInstruction:
280    def __init__(self, qiskit_instruction, qubits, promises=[]):
281        self.qiskit_instruction = qiskit_instruction
282        self.qubits = qubits
283        self.promises = promises
284
285    def __repr__(self):
286        return f"_quPythonInstruction({self.qiskit_instruction.name}, {self.qubits})"
287
288
289class _quPythonMeasurement:
290    def __init__(self, qubit):
291        self.promises = [BitPromise(self)]
292        self.qubits = [qubit]
293
294
295class BitPromiseNotResolvedError(Exception):
296    """
297    For if you try to cast to `bool` before the promise has been resolved.
298    """
299    pass
300
301class ConditionMeasurementAtRuntimeError(Exception):
302    """
303    We can't currently condition measurement operations on `Bit` objects so
304    raise an exception if someone tries it.
305    """
306    pass
307
308class _ControlBitContextManager:
309    def __init__(self, control):
310        self.control = control
311    def __enter__(self):
312        existing_controls = __active_controls__.get()
313        self.reset_token = __active_controls__.set(existing_controls + (self.control,))
314    def __exit__(self, _exc_type, _exc_value, _traceback):
315        __active_controls__.reset(self.reset_token)
class Bit:
13class Bit:
14    """
15    Base class for `Qubit` and `BitPromise` objects; you shouldn't create an
16    instance of this class yourself.
17    """
18    operations: list
19    """History of operations on the bit"""
20
21    def _get_linked_bits(self, already_found=None):
22        """
23        Find all Bit objects that interact with this Bit through quantum
24        circuit operations.
25        """
26        # TODO: unit test
27        searched_bits = set()
28        all_known_bits = { self }
29        while len(searched_bits) < len(all_known_bits):
30            for bit in (all_known_bits - searched_bits):
31                for op in bit.operations:
32                    all_known_bits |= set(op.qubits + op.promises)
33                searched_bits.add(bit)
34        return all_known_bits
35
36    def as_control(self) -> _ControlBitContextManager:
37        """
38        Return a context manager to condition quantum gates. Use this to
39        control quantum gates using the `with` statement.
40
41        ```python
42        with my_bit.as_control():
43            qubit.x()  # conditioned on my_bit
44        ```
45
46        You can condition on both `Qubit` and `BitPromise` objects.
47        """
48        return _ControlBitContextManager(self)

Base class for Qubit and BitPromise objects; you shouldn't create an instance of this class yourself.

operations: list

History of operations on the bit

def as_control(self) -> qupython.qubit._ControlBitContextManager:
36    def as_control(self) -> _ControlBitContextManager:
37        """
38        Return a context manager to condition quantum gates. Use this to
39        control quantum gates using the `with` statement.
40
41        ```python
42        with my_bit.as_control():
43            qubit.x()  # conditioned on my_bit
44        ```
45
46        You can condition on both `Qubit` and `BitPromise` objects.
47        """
48        return _ControlBitContextManager(self)

Return a context manager to condition quantum gates. Use this to control quantum gates using the with statement.

with my_bit.as_control():
    qubit.x()  # conditioned on my_bit

You can condition on both Qubit and BitPromise objects.

class Qubit(Bit):
 50class Qubit(Bit):
 51    """
 52    This is the main quPython object you'll interact with and the only object
 53    you should instantiate directly. Qubits start in state |0〉.
 54
 55    `Qubit` objects support the following single-qubit gate operations as
 56    methods.
 57
 58    | Name  | Qiskit object                                                                           |
 59    |-------|-----------------------------------------------------------------------------------------|
 60    | `x`   | [`XGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.XGate)         |
 61    | `y`   | [`YGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.YGate)         |
 62    | `z`   | [`ZGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.ZGate)         |
 63    | `h`   | [`HGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.HGate)         |
 64    | `s`   | [`SGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.SGate)         |
 65    | `sdg` | [`SdgGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.SdgGate)     |
 66    | `t`   | [`TGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TGate)         |
 67    | `tdg` | [`TdgGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TdgGate)     |
 68    | `p`   | [`PhaseGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PhaseGate) |
 69    | `rx`  | [`RXGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RXGate)       |
 70    | `ry`  | [`RYGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RYGate)       |
 71    | `rz`  | [`RZGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.RZGate)       |
 72    | `u`   | [`UGate`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.UGate)         |
 73
 74    These gate methods mutate the `Qubit` object and return it so you can stack them.
 75
 76    ```python
 77    qubit = Qubit().x().h()  # Qubit in state |->
 78    qubit.h()  # mutate to state |1>
 79    ```
 80
 81    The `p`, `rx`, `ry`, `rz`, and `u` gates require angles. See their
 82    corresponding Qiskit objects for a description of the gates and their
 83    angles.
 84
 85    ```python
 86    Qubit().rx(0.2)
 87    ```
 88
 89    For a controlled gate, use the `as_control` method and the `with`
 90    statement. This will control all gates inside the context by that qubit.
 91
 92    ```python
 93    qubit, another_qubit = Qubit(), Qubit()
 94    with qubit.as_control():
 95        another_qubit.x()
 96    ```
 97
 98    You can also control gates by passing a list of `Qubit`, `BitPromise`, and
 99    `bool` objects to the `conditions` argument. All conditions must be true to
100    for the gate to apply.
101
102    ```python
103    qubit.x(conditions=[another_qubit, measurement_result, True])
104    ```
105
106    Use the `measure` method to return a `BitPromise`. These promises can
107    control quantum gates too using the `as_control` method.
108    """
109    def __init__(self):
110        self.operations = []
111        self._create_1q_gate_methods()
112
113    def __bool__(self):
114        raise ValueError(
115            "Can't cast Qubit to bool; use `.measure()` to measure"
116            " the qubit instead."
117        )
118
119    def _separate_conditions(self, conditions):
120        qubits = [c for c in conditions if isinstance(c, Qubit)]
121        promises = [c for c in conditions if isinstance(c, BitPromise)]
122        build_time_conditions = [c for c in conditions if not isinstance(c, (Qubit, BitPromise))]
123        return qubits, promises, build_time_conditions
124
125    def _create_1q_gate_methods(self):
126        """
127        Generate methods such as self.h, self.p, etc.
128        This method runs on object initialization.
129        """
130
131        # TODO: unit test
132        # TODO: neaten up
133        def _create_method(gate):
134            def add_gate(*args, **kwargs):
135                conditions = kwargs.pop("conditions", [])
136                conditions += __active_controls__.get()
137                qubits, promises, build_time_conditions = self._separate_conditions(conditions)
138                if not all(build_time_conditions):
139                    return
140                qiskit_inst = gate(*args, **kwargs)
141                if qubits:
142                    qiskit_inst = qiskit_inst.control(len(qubits))
143                inst = _quPythonInstruction(
144                    qiskit_instruction=qiskit_inst,
145                    qubits=qubits + [self],
146                    promises=promises
147                )
148                for qubit in qubits + [self]:
149                    qubit.operations.append(inst)
150                for promise in promises:
151                    promise.operations.append(inst)
152                return self
153
154            return add_gate
155
156        for gate, name in [
157            (clib.XGate, "x"),
158            (clib.YGate, "y"),
159            (clib.ZGate, "z"),
160            (clib.HGate, "h"),
161            (clib.SGate, "s"),
162            (clib.SdgGate, "sdg"),
163            (clib.TGate, "t"),
164            (clib.TdgGate, "tdg"),
165            (clib.PhaseGate, "p"),
166            (clib.RXGate, "rx"),
167            (clib.RYGate, "ry"),
168            (clib.RZGate, "rz"),
169            (clib.UGate, "u"),
170        ]:
171            setattr(self, name, _create_method(gate))
172            attr = getattr(self, name)
173            attr.__doc__ = f"Apply '{name}' gate to qubit"
174            attr.__annotations__["return"] = Self
175
176    def measure(self, conditions=None) -> BitPromise:
177        """
178        Add measure instruction to Qubit and return BitPromise
179        """
180        conditions = conditions or []
181        conditions += __active_controls__.get()
182        qubits, promises, build_time_conditions = self._separate_conditions(conditions)
183        if qubits or promises:
184            raise ConditionMeasurementAtRuntimeError(ERR_MSG["ConditionMeasurementAtRuntimeError"])
185        if not all(build_time_conditions):
186            return
187        inst = _quPythonMeasurement(self)
188        self.operations.append(inst)
189        return inst.promises[0]

This is the main quPython object you'll interact with and the only object you should instantiate directly. Qubits start in state |0〉.

Qubit objects support the following single-qubit gate operations as methods.

Name Qiskit object
x XGate
y YGate
z ZGate
h HGate
s SGate
sdg SdgGate
t TGate
tdg TdgGate
p PhaseGate
rx RXGate
ry RYGate
rz RZGate
u UGate

These gate methods mutate the Qubit object and return it so you can stack them.

qubit = Qubit().x().h()  # Qubit in state |->
qubit.h()  # mutate to state |1>

The p, rx, ry, rz, and u gates require angles. See their corresponding Qiskit objects for a description of the gates and their angles.

Qubit().rx(0.2)

For a controlled gate, use the as_control method and the with statement. This will control all gates inside the context by that qubit.

qubit, another_qubit = Qubit(), Qubit()
with qubit.as_control():
    another_qubit.x()

You can also control gates by passing a list of Qubit, BitPromise, and bool objects to the conditions argument. All conditions must be true to for the gate to apply.

qubit.x(conditions=[another_qubit, measurement_result, True])

Use the measure method to return a BitPromise. These promises can control quantum gates too using the as_control method.

operations

History of operations on the bit

def measure(self, conditions=None) -> BitPromise:
176    def measure(self, conditions=None) -> BitPromise:
177        """
178        Add measure instruction to Qubit and return BitPromise
179        """
180        conditions = conditions or []
181        conditions += __active_controls__.get()
182        qubits, promises, build_time_conditions = self._separate_conditions(conditions)
183        if qubits or promises:
184            raise ConditionMeasurementAtRuntimeError(ERR_MSG["ConditionMeasurementAtRuntimeError"])
185        if not all(build_time_conditions):
186            return
187        inst = _quPythonMeasurement(self)
188        self.operations.append(inst)
189        return inst.promises[0]

Add measure instruction to Qubit and return BitPromise

Inherited Members
Bit
as_control
class BitPromise(Bit):
191class BitPromise(Bit):
192    """
193    Placeholder for qubit measurement results. You should never instantiate
194    this class directly, it should only be output from `Qubit.measure`.
195
196    When you return a `BitPromise` from an `@quantum` function, quPython finds
197    the `Qubit` that created this `BitPromise` and compiles the quantum program
198    needed to calculate it.
199
200    Invert a `BitPromise` using the `~` operator. This returns a new
201    `BitPromise` with an inverted value.
202
203    ```python
204    with (~bit_promise).as_control():
205        # Apply gates if `bit_promise` is `False`
206    ```
207
208    <details>
209    <summary>Gotchas</summary>
210
211    After its value is determined, a `BitPromise` tries to behave as much like
212    a `bool` as possible. Unfortunately, there are some quirks because
213    `BitPromise` objects need to have unique hashes before circuit compilation,
214    but `bool`s all have the same hash (0 or 1) and you can't change an
215    object's hash without breaking basic Python functionality. The following
216    code snippet shows an example.
217
218    ```python
219    # Create fulfilled qubit promise
220    promise = BitPromise(None)
221    promise.value = True
222
223    # Show unexpected behavior
224    promise == True  # True
225    promise in (True, False)  # False
226    ```
227
228    For best results, cast to `bool` as soon as possible after the function
229    completes or use the `value` attribute.
230
231    If you have better ideas on how to handle this, let me know in an
232    [issue](https://github.com/frankharkins/qupython/issues/new?title=[Suggestion]:%20BitPromise%20values).
233    </details>
234    """
235
236    def __init__(self, measurement_instruction, inverse=False):
237        self.operations = [measurement_instruction]
238        self._inverse = inverse
239        self.value = None
240        """
241        Value of the `BitPromise`. This is `None` until the promise is
242        resolved.
243        """
244
245    def __bool__(self):
246        if self.value is None:
247            raise BitPromiseNotResolvedError(ERR_MSG["BitPromiseNotResolved"])
248        if self._inverse:
249            return not self.value
250        return self.value
251
252    def __int__(self):
253        return int(bool(self))
254
255    def __eq__(self, other):
256        if self.value is None:
257            return id(self) == id(other)
258        return bool(self) == other
259
260    def __hash__(self):
261        return id(self)
262
263    def __repr__(self):
264        if self.value is None:
265            return f"BitPromise({self.operations})"
266        return repr(bool(self))
267
268    def __invert__(self) -> BitPromise:
269        # TODO: This is a bit inefficient as it adds a new measurement each
270        # time we invert the promise
271        measurement_instruction = self.operations[0]
272        new_promise = BitPromise(
273            measurement_instruction,
274            inverse= not self._inverse
275        )
276        measurement_instruction.promises.append(new_promise)
277        return new_promise

Placeholder for qubit measurement results. You should never instantiate this class directly, it should only be output from Qubit.measure.

When you return a BitPromise from an @quantum function, quPython finds the Qubit that created this BitPromise and compiles the quantum program needed to calculate it.

Invert a BitPromise using the ~ operator. This returns a new BitPromise with an inverted value.

with (~bit_promise).as_control():
    # Apply gates if `bit_promise` is `False`

Gotchas

After its value is determined, a BitPromise tries to behave as much like a bool as possible. Unfortunately, there are some quirks because BitPromise objects need to have unique hashes before circuit compilation, but bools all have the same hash (0 or 1) and you can't change an object's hash without breaking basic Python functionality. The following code snippet shows an example.

# Create fulfilled qubit promise
promise = BitPromise(None)
promise.value = True

# Show unexpected behavior
promise == True  # True
promise in (True, False)  # False

For best results, cast to bool as soon as possible after the function completes or use the value attribute.

If you have better ideas on how to handle this, let me know in an

BitPromise(measurement_instruction, inverse=False)
236    def __init__(self, measurement_instruction, inverse=False):
237        self.operations = [measurement_instruction]
238        self._inverse = inverse
239        self.value = None
240        """
241        Value of the `BitPromise`. This is `None` until the promise is
242        resolved.
243        """
operations

History of operations on the bit

value

Value of the BitPromise. This is None until the promise is resolved.

Inherited Members
Bit
as_control
class BitPromiseNotResolvedError(builtins.Exception):
296class BitPromiseNotResolvedError(Exception):
297    """
298    For if you try to cast to `bool` before the promise has been resolved.
299    """
300    pass

For if you try to cast to bool before the promise has been resolved.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args
class ConditionMeasurementAtRuntimeError(builtins.Exception):
302class ConditionMeasurementAtRuntimeError(Exception):
303    """
304    We can't currently condition measurement operations on `Bit` objects so
305    raise an exception if someone tries it.
306    """
307    pass

We can't currently condition measurement operations on Bit objects so raise an exception if someone tries it.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args