qupython.function
1# Class for @quantum functions 2 3from typing import Callable 4 5import qiskit 6from qiskit_aer.primitives import Sampler 7from .construction import _get_promises, _construct_circuit 8from .qubit import BitPromise 9from .err_msg import ERR_MSG 10 11 12class quPythonFunction: 13 """ 14 A wrapper to compile a quantum circuit from a function's outputs, execute 15 it, and resolve any `BitPromise` objects. You'll probably create these 16 objects through through the `@quantum` decorator. 17 18 ```python 19 from qupython import Qubit, quantum 20 21 @quantum 22 def my_function(): 23 return Qubit().measure() 24 25 type(my_function) # qupython.function.quPythonFunction 26 ``` 27 28 Calling an instance of this class will (in the following order): 29 1. Run the function in the Python interpreter 30 2. Intercept the output of the function and search for any `BitPromise` objects. 31 3. Compile the Qiskit circuit needed to calculate the `BitPromise` objects. 32 4. Execute the circuit on a quantum backend. 33 5. Use the execution results to assign the `BitPromise.value` attributes, resolving the promises. 34 35 Split these steps up by calling methods on this object rather than 36 calling it. The `compile` method does steps 1-3, but does not execute the 37 circuit on a quantum backend. It stores the compiled 38 [`qiskit.QuantumCircuit`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.QuantumCircuit) 39 in the `circuit` attribute. 40 41 ```python 42 my_function.compile() 43 my_function.circuit.draw() 44 ``` 45 46 ```output 47 ┌─┐ 48 q: ┤M├ 49 └╥┘ 50 c: 1/═╩═ 51 0 52 ``` 53 54 The `run` method carries out steps 4-5 using the circuit from the `circuit` 55 attribute. If you replace the attribute with a different circuit (such as 56 an optimized circuit), it will use that. 57 58 ```python 59 my_function.run() # BitPromise with value False 60 ``` 61 62 The `interpret_result` method uses a `SamplerResult` of the compiled 63 circuit to resolve `BitPromises` and return the function output. Use this 64 if you want to control the circuit execution. 65 66 ```python 67 from qiskit_aer.primitives import Sampler 68 qiskit_result = Sampler().run(my_function.circuit).result() 69 my_function.interpret_result(qiskit_result) # BitPromise with value False 70 ``` 71 72 """ 73 def __init__(self, function: Callable): 74 self.function: Callable = function 75 """ 76 The function to be compiled. It should return `BitPromise` objects. 77 """ 78 self.circuit: qiskit.QuantumCircuit | None = None 79 """ 80 The compiled Qiskit circuit. This is `None` until the class is 81 called or `compile` is run. 82 """ 83 self.promises: list[BitPromise] = None 84 """The `BitPromise` objects to be resolved""" 85 86 def __call__(self, *args, **kwargs): 87 """ 88 Run the function, compile the circuit, execute the circuit, and return 89 the results. 90 """ 91 self.compile(*args, **kwargs) 92 return self.run() 93 94 def compile(self, *args, **kwargs): 95 """ 96 Run function (with args and keyword args), collect promises, and 97 compile circuit, but don't execute the circuit. Stores the compiled 98 circuit in the `circuit` attribute. 99 """ 100 self.output = self.function(*args, **kwargs) 101 self.promises = list(_get_promises(self.output)) 102 self.circuit = _construct_circuit(self.promises) 103 104 def run(self): 105 """ 106 Run the pre-compiled circuit, interpret the result, and return the 107 output object with fulfilled promises. 108 """ 109 if self.promises is None: 110 raise quPythonFunctionError(ERR_MSG["CompileFunctionBeforeRunning"]) 111 if self.promises == []: 112 return self.output 113 self.sampler_result = Sampler().run(self.circuit).result() 114 return self.interpret_result(self.sampler_result) 115 116 def interpret_result(self, result: qiskit.primitives.SamplerResult): 117 """ 118 Given a one-shot `SamplerResult`, parse the result and return the 119 function's output with resolved promises. 120 """ 121 if result is None and self.promises == []: 122 # Function is not quantum 123 return self.output 124 125 integer = [*result.quasi_dists[0]][0] # Get 0th key from dict 126 for promise in self.promises: 127 promise.value = bool((1 << promise.index) & integer) 128 return self.output 129 130class quPythonFunctionError(Exception): 131 pass
13class quPythonFunction: 14 """ 15 A wrapper to compile a quantum circuit from a function's outputs, execute 16 it, and resolve any `BitPromise` objects. You'll probably create these 17 objects through through the `@quantum` decorator. 18 19 ```python 20 from qupython import Qubit, quantum 21 22 @quantum 23 def my_function(): 24 return Qubit().measure() 25 26 type(my_function) # qupython.function.quPythonFunction 27 ``` 28 29 Calling an instance of this class will (in the following order): 30 1. Run the function in the Python interpreter 31 2. Intercept the output of the function and search for any `BitPromise` objects. 32 3. Compile the Qiskit circuit needed to calculate the `BitPromise` objects. 33 4. Execute the circuit on a quantum backend. 34 5. Use the execution results to assign the `BitPromise.value` attributes, resolving the promises. 35 36 Split these steps up by calling methods on this object rather than 37 calling it. The `compile` method does steps 1-3, but does not execute the 38 circuit on a quantum backend. It stores the compiled 39 [`qiskit.QuantumCircuit`](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.QuantumCircuit) 40 in the `circuit` attribute. 41 42 ```python 43 my_function.compile() 44 my_function.circuit.draw() 45 ``` 46 47 ```output 48 ┌─┐ 49 q: ┤M├ 50 └╥┘ 51 c: 1/═╩═ 52 0 53 ``` 54 55 The `run` method carries out steps 4-5 using the circuit from the `circuit` 56 attribute. If you replace the attribute with a different circuit (such as 57 an optimized circuit), it will use that. 58 59 ```python 60 my_function.run() # BitPromise with value False 61 ``` 62 63 The `interpret_result` method uses a `SamplerResult` of the compiled 64 circuit to resolve `BitPromises` and return the function output. Use this 65 if you want to control the circuit execution. 66 67 ```python 68 from qiskit_aer.primitives import Sampler 69 qiskit_result = Sampler().run(my_function.circuit).result() 70 my_function.interpret_result(qiskit_result) # BitPromise with value False 71 ``` 72 73 """ 74 def __init__(self, function: Callable): 75 self.function: Callable = function 76 """ 77 The function to be compiled. It should return `BitPromise` objects. 78 """ 79 self.circuit: qiskit.QuantumCircuit | None = None 80 """ 81 The compiled Qiskit circuit. This is `None` until the class is 82 called or `compile` is run. 83 """ 84 self.promises: list[BitPromise] = None 85 """The `BitPromise` objects to be resolved""" 86 87 def __call__(self, *args, **kwargs): 88 """ 89 Run the function, compile the circuit, execute the circuit, and return 90 the results. 91 """ 92 self.compile(*args, **kwargs) 93 return self.run() 94 95 def compile(self, *args, **kwargs): 96 """ 97 Run function (with args and keyword args), collect promises, and 98 compile circuit, but don't execute the circuit. Stores the compiled 99 circuit in the `circuit` attribute. 100 """ 101 self.output = self.function(*args, **kwargs) 102 self.promises = list(_get_promises(self.output)) 103 self.circuit = _construct_circuit(self.promises) 104 105 def run(self): 106 """ 107 Run the pre-compiled circuit, interpret the result, and return the 108 output object with fulfilled promises. 109 """ 110 if self.promises is None: 111 raise quPythonFunctionError(ERR_MSG["CompileFunctionBeforeRunning"]) 112 if self.promises == []: 113 return self.output 114 self.sampler_result = Sampler().run(self.circuit).result() 115 return self.interpret_result(self.sampler_result) 116 117 def interpret_result(self, result: qiskit.primitives.SamplerResult): 118 """ 119 Given a one-shot `SamplerResult`, parse the result and return the 120 function's output with resolved promises. 121 """ 122 if result is None and self.promises == []: 123 # Function is not quantum 124 return self.output 125 126 integer = [*result.quasi_dists[0]][0] # Get 0th key from dict 127 for promise in self.promises: 128 promise.value = bool((1 << promise.index) & integer) 129 return self.output
A wrapper to compile a quantum circuit from a function's outputs, execute
it, and resolve any BitPromise objects. You'll probably create these
objects through through the @quantum decorator.
from qupython import Qubit, quantum
@quantum
def my_function():
return Qubit().measure()
type(my_function) # qupython.function.quPythonFunction
Calling an instance of this class will (in the following order):
- Run the function in the Python interpreter
- Intercept the output of the function and search for any
BitPromiseobjects. - Compile the Qiskit circuit needed to calculate the
BitPromiseobjects. - Execute the circuit on a quantum backend.
- Use the execution results to assign the
BitPromise.valueattributes, resolving the promises.
Split these steps up by calling methods on this object rather than
calling it. The compile method does steps 1-3, but does not execute the
circuit on a quantum backend. It stores the compiled
qiskit.QuantumCircuit
in the circuit attribute.
my_function.compile()
my_function.circuit.draw()
┌─┐
q: ┤M├
└╥┘
c: 1/═╩═
0
The run method carries out steps 4-5 using the circuit from the circuit
attribute. If you replace the attribute with a different circuit (such as
an optimized circuit), it will use that.
my_function.run() # BitPromise with value False
The interpret_result method uses a SamplerResult of the compiled
circuit to resolve BitPromises and return the function output. Use this
if you want to control the circuit execution.
from qiskit_aer.primitives import Sampler
qiskit_result = Sampler().run(my_function.circuit).result()
my_function.interpret_result(qiskit_result) # BitPromise with value False
74 def __init__(self, function: Callable): 75 self.function: Callable = function 76 """ 77 The function to be compiled. It should return `BitPromise` objects. 78 """ 79 self.circuit: qiskit.QuantumCircuit | None = None 80 """ 81 The compiled Qiskit circuit. This is `None` until the class is 82 called or `compile` is run. 83 """ 84 self.promises: list[BitPromise] = None 85 """The `BitPromise` objects to be resolved"""
The compiled Qiskit circuit. This is None until the class is
called or compile is run.
95 def compile(self, *args, **kwargs): 96 """ 97 Run function (with args and keyword args), collect promises, and 98 compile circuit, but don't execute the circuit. Stores the compiled 99 circuit in the `circuit` attribute. 100 """ 101 self.output = self.function(*args, **kwargs) 102 self.promises = list(_get_promises(self.output)) 103 self.circuit = _construct_circuit(self.promises)
Run function (with args and keyword args), collect promises, and
compile circuit, but don't execute the circuit. Stores the compiled
circuit in the circuit attribute.
105 def run(self): 106 """ 107 Run the pre-compiled circuit, interpret the result, and return the 108 output object with fulfilled promises. 109 """ 110 if self.promises is None: 111 raise quPythonFunctionError(ERR_MSG["CompileFunctionBeforeRunning"]) 112 if self.promises == []: 113 return self.output 114 self.sampler_result = Sampler().run(self.circuit).result() 115 return self.interpret_result(self.sampler_result)
Run the pre-compiled circuit, interpret the result, and return the output object with fulfilled promises.
117 def interpret_result(self, result: qiskit.primitives.SamplerResult): 118 """ 119 Given a one-shot `SamplerResult`, parse the result and return the 120 function's output with resolved promises. 121 """ 122 if result is None and self.promises == []: 123 # Function is not quantum 124 return self.output 125 126 integer = [*result.quasi_dists[0]][0] # Get 0th key from dict 127 for promise in self.promises: 128 promise.value = bool((1 << promise.index) & integer) 129 return self.output
Given a one-shot SamplerResult, parse the result and return the
function's output with resolved promises.
Common base class for all non-exit exceptions.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args