Skip to main content

Write quantum programs directly in Python

Project description

quPython

Quantum 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 as bool-like objects.

What can it do?

Initialize a quPython.Qubit object just like any other object and use it inside a @quantum function.

from qupython import Qubit, quantum

@quantum
def random_bit():
    qubit = Qubit()         # Allocate new qubit
    qubit.h()               # Mutate qubit
    return qubit.measure()  # Measure qubit to bool

When you run random_bit, quPython compiles your function to a quantum program, executes it, and returns results.

>>> random_bit()
True

quPython makes writing quantum programs feel like any other Python program.

Features and examples

Allocate qubits as you go, and return classical bits as bool-like objects in whatever form you like.

@quantum
def ghz(num_bits: int):
    """
    Create and measure a GHZ state
    """
    qubits = [ Qubit() for _ in range(num_bits) ]
    control, targets = qubits[0], qubits[1:]
    control.h()
    for target in targets:
        target.x(conditions=[control])
    return [ qubit.measure() for qubit in qubits ]
>>> 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.

class BellPair:
    def __init__(self):
        self.left = Qubit().h()
        self.right = Qubit().x(conditions=[self.left])

@quantum
def teleportation_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])
    return bell_pair.right.measure()

Generate Qiskit circuits

If you want, you can just use quPython to create Qiskit circuits with Pythonic syntax (rather than the assembly-like syntax of qc.cx(0, 1) in native Qiskit).

# Compile using quPython
teleportation_demo.compile()

# Draw compiled Qiskit circuit
teleportation_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.

from qiskit_aer.primitives import Sampler
qiskit_result = Sampler().run(teleportation_demo.circuit).result()
teleportation_demo.interpret_result(qiskit_result)  # returns `False`

How it works

For contributors (or the curious)

To see how quPython works, we'll use the Qubit object outside a @quantum function. Qubit objects store a list of operations that act on them.

>>> qubit = Qubit()
>>> qubit.h()
>>> qubit.operations
[quPythonInstruction(h, [<qupython.qubit.Qubit object at 0x7fddf68504d0>])]

The only way to get a classical data type from a quantum program is the Qubit.measure method. We give the appearance that this returns a bool, but it actually returns a QubitPromise object, which saves a link to the measure operation that created it.

>>> promise = qubit.measure()
>>> promise
QubitPromise(<qupython.qubit.quPythonMeasurement object at 0x7fddf0ddbbd0>)

When the user calls a @quantum function, quPython intercepts the output, finds any QubitPromise objects, then traces back the Qubits those promises came from. With this information, quPython can construct the QuantumCircuit needed to fulfil the promises. quPython then executes the circuit and fills in the QubitPromise values.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qupython-0.0.1.tar.gz (11.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qupython-0.0.1-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

Details for the file qupython-0.0.1.tar.gz.

File metadata

  • Download URL: qupython-0.0.1.tar.gz
  • Upload date:
  • Size: 11.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.6

File hashes

Hashes for qupython-0.0.1.tar.gz
Algorithm Hash digest
SHA256 4101c29572611efb4ed1a68b1e89a0107057c1e66f4ff41cd6db16e475d24002
MD5 19750f3d85714a7c65aad15bcb717592
BLAKE2b-256 adb3b0b366ce055382410c9c9b693e5dd86e9e087dc354060c565aaa8e38a73b

See more details on using hashes here.

File details

Details for the file qupython-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: qupython-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 10.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.6

File hashes

Hashes for qupython-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 994e367611f493d19a20eae8f7dd19797f2000b7de82ee8cd51c7910d276dd53
MD5 2f84fd8cccdc91eaf738e59824507eb3
BLAKE2b-256 32e106b1279380a0dceff5858e34df24f21f92583cb31908da9e9b3b7b1f49bf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page