Package: qupython
quPythonQuantum programs directly in Python[!WARNING]This project is currently a proof-of-concept. It will be buggy and unstable.quPython compiles Python functions into quantum programs, executes the
programs, and returns the results asbool-like objects.What can it do?Initialize aquPython.Qubitobject just like any other object and use it
inside a@quantumfunction.fromqupythonimportQubit,quantum@quantumdefrandom_bit():qubit=Qubit()# Allocate new qubitqubit.h()# Mutate qubitreturnqubit.measure()# Measure qubit to boolWhen you runrandom_bit, quPython compiles your function to a quantum
program, executes it, and returns results.>>>random_bit()TruequPython makes writing quantum programs feel like any other Python program.Features and examplesAllocate qubits as you go, and return classical bits asbool-like objects in
whatever form you like.@quantumdefghz(num_bits:int):"""Create and measure a GHZ state"""qubits=[Qubit()for_inrange(num_bits)]control,targets=qubits[0],qubits[1:]control.h()fortargetintargets:target.x(conditions=[control])return[qubit.measure()forqubitinqubits]>>> ghz(8)
[False, False, False, False, False, False, False, False]Create classes for quantum data just as you would conventional data, and
condition quantum gates on classical and quantum data in exactly the same way.classBellPair:def__init__(self):self.left=Qubit().h()self.right=Qubit().x(conditions=[self.left])@quantumdefteleportation_demo():message=Qubit()bell_pair=BellPair()do_x=bell_pair.left.x(conditions=[message]).measure()do_z=message.h().measure()bell_pair.right.x(conditions=[do_x]).z(conditions=[do_z])returnbell_pair.right.measure()Generate Qiskit circuitsIf you want, you can just use quPython to create Qiskit circuits with Pythonic
syntax (rather than the assembly-like syntax ofqc.cx(0, 1)in native
Qiskit).# Compile using quPythonteleportation_demo.compile()# Draw compiled Qiskit circuitteleportation_demo.circuit.draw()┌───┐┌─┐                           
q_0: ────────────■──┤ H ├┤M├───────────────────────────
          ┌───┐  │  └───┘└╥┘┌──────────┐┌──────────┐┌─┐
q_1: ─────┤ X ├──┼────────╫─┤0         ├┤0         ├┤M├
     ┌───┐└─┬─┘┌─┴─┐ ┌─┐  ║ │          ││          │└╥┘
q_2: ┤ H ├──■──┤ X ├─┤M├──╫─┤          ├┤          ├─╫─
     └───┘     └───┘ └╥┘  ║ │  If_else ││          │ ║ 
c_0: ═════════════════╬═══╬═╡          ╞╡  If_else ╞═╩═
                      ║   ║ │          ││          │   
c_1: ═════════════════╩═══╬═╡0         ╞╡          ╞═══
                          ║ └──────────┘│          │   
c_2: ═════════════════════╩═════════════╡0         ╞═══
                                        └──────────┘You can compile the function without executing it, optimize the cirucit,
execute it however you like, then use quPython to interpret the results.fromqiskit_aer.primitivesimportSamplerqiskit_result=Sampler().run(teleportation_demo.circuit).result()teleportation_demo.interpret_result(qiskit_result)# returns `False`How it worksFor contributors (or the curious)To see how quPython works, we'll use theQubitobject outside a@quantumfunction.Qubitobjects store a list of operations that act on them.>>>qubit=Qubit()>>>qubit.h()>>>qubit.operations[quPythonInstruction(h,[<qupython.qubit.Qubitobjectat0x7fddf68504d0>])]The only way to get a classical data type from a quantum program is theQubit.measuremethod. We give the appearance that this returns abool, but
it actually returns aQubitPromiseobject, which saves a link to the measure
operation that created it.>>>promise=qubit.measure()>>>promiseQubitPromise(<qupython.qubit.quPythonMeasurementobjectat0x7fddf0ddbbd0>)When the user calls a@quantumfunction, quPython intercepts the output,
finds anyQubitPromiseobjects, then traces back theQubitsthose promises
came from. With this information, quPython can construct theQuantumCircuitneeded to fulfil the promises. quPython then executes the circuit and fills in
theQubitPromisevalues.