Skip to main content

QCut is a quantum circuit knitting package built on top of qiskit for performing gate cuts and resetless wire cuts allowing simulation of larger quantum circuits on smaller quantum devices or simulators at the cost of a circuit overhead.

Project description

Current Release Coverage Status Python License

QCut

QCut is a quantum circuit knitting package built on top of qiskit for performing gate cuts and resetless wire cuts allowing simulation of larger quantum circuits on smaller quantum devices or simulators at the cost of a circuit overhead. QCut has been designed and tested to work with IQM's qpus, and the Finnish Quantum Computing Infrastructure (FiQCI).

QCut has been built at CSC - IT Center for Science (Finnish IT Center for Science).

Check out jooniv.github.io/QCut/ for documentation and more examples.

Installation

For installation a UNIX-like system is currently needed due to pymetis being used for automatic cut finding. On Windows use WSL

Pip:
Installation should be done via uv

uv pip install QCut
#or
uv add QCut

If using other than the default Qiskit version (newest) it is recommended to install Qiskit first before installing QCut.

Uv can be installed with

#Linux / mac
curl -LsSf https://astral.sh/uv/install.sh | sh

Note: for drawing circuits you might have to install pylatexenc. This can also be done with uv.

uv pip install pylatexenc
#or
uv add pylatexenc

Install from source
It is also possible to use QCut by cloning this repository and including it in your project folder.

cd QCut
uv pip install .
#or
uv sync --no-dev

#or with dev deps
uv sync

Usage

Manual Usage

1: Import needed packages

import QCut as ck
from QCut import cut, cutGate
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import CXGate
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import CXGate
from qiskit_aer import AerSimulator
from qiskit.primitives import StatevectorEstimator, BackendEstimatorV2 as BackendEstimator
from iqm.qiskit_iqm import IQMFakeAdonis

2: Start by defining a QuantumCircuit just like in Qiskit

circuit  =  QuantumCircuit(4)

mult = 1.635
circuit.r(mult*0.46262, mult*0.1446, 0)
circuit.cx(0,1)
circuit.cx(1,2)
circuit.cx(2,3)
   
circuit.draw("mpl")

3: Insert cut operations to the circuit to denote where we want to cut the circuit

Note that here we don't insert any measurements. Measurements will be automatically handled by QCut.

from qiskit.circuit.library import CXGate

cut_circuit = QuantumCircuit(4)

mult = 1.635
cut_circuit.r(mult*0.46262, mult*0.1446, 0)
cut_circuit.append(**cutGate(CXGate(), 0, 1)) 
cut_circuit.append(cut(), [1])
cut_circuit.cx(1,2)
cut_circuit.cx(2,3)

cut_circuit.decompose(gates_to_decompose=["CutGate"]).draw("mpl")

Note that currently QCut only supports cutting CZ, SWAP, and iSWAP gates so all two qubit gates get decomposed to them, hence some cuts resulting in extra gates.

4: Extract cut locations from cut_circuit and split it into independent subcircuit.

cut_circuit = ck.get_locations_and_subcircuits(cut_circuit)

Now we can draw our subcircuits.

cut_circuit.subcircuits[0].draw("mpl")

cut_circuit.subcircuits[1].draw("mpl")

cut_circuit.subcircuits[2].draw("mpl")

5 Define backend and transpile the cut circuit

fake = IQMFakeAdonis() #noisy
sim = AerSimulator() #ideal
transpiled = ck.transpile_subcircuits(cut_circuit, fake, optimization_level=3)

6: Generate experiment circuits

observables = SparsePauliOp(["IIIZ", "IIZI", "IZII", "IIZZ"])

cut_experiment = ck.get_experiment_circuits(transpiled, observables)

Note that both CutCircuit and CutExperiment classes implement the assing_parameters() function of Qiskit.QuantumCircuit

7: Run the experiment circuits

results = ck.run_experiments(cut_experiment, backend=fake)

8: Define observables and calculate expectation values

expectation_values = ck.estimate_expectation_values(results, cut_experiment.expv_data())

9: Finally calculate the exact and noisy expectation values of the original circuit and compare them to the results calculated with QCut

obs = [ob.to_label() for ob in observables.paulis]

estimator = Estimator()
exact_expvals = [e.data.evs for e in
    estimator.run([(x) for x in zip([circuit] * len(obs), obs)]).result()
]


tr = transpile(circuit, backend=fake)

tr_obs = observables.apply_layout(tr.layout)

tr_obs_separate = [
    SparsePauliOp(pauli.to_label()) for pauli in tr_obs.paulis
]

fake_estimator = BackendEstimator(backend=fake)
exps = [e.data.evs for e in
    fake_estimator.run([(x) for x in zip([tr] * len(tr_obs_separate), tr_obs_separate)]).result()
]
import numpy as np

np.set_printoptions(formatter={"float": lambda x: f"{x:0.6f}"})

print(f"QCut expectation values:{np.array(expectation_values)}")
print(f"Noisy expectation values with fake backend:{np.array(exps)}")
print(f"Exact expectation values with ideal simulator :{np.array(exact_expvals)}")

QCut expectation values:[0.704039 0.615275 0.554269 0.808868]

Noisy expectation values with fake backend:[0.587891 0.669922 0.500000 0.777344]

Exact expectation values with ideal simulator :[0.727323 0.727323 0.727323 1.000000]

As we can see QCut is able to accurately reconstruct the expectation values and be more accurate that just using the fake backend as is. (Note that since this is a probabilistic method the results vary a bit each run)

Additionally we can execute QCut using the ideal Aer simulator and see that we get (practically) exact results:

QCut expectation values:[0.699436 0.713172 0.713172 0.979377]

Usage shorthand

For convenience, it is not necessary to go through each of the aforementioned steps individually. Instead, QCut provides a function run() that executes the whole wire-cutting sequence.

The same example can then be run like this:

sim = AerSimulator()
observables = SparsePauliOp(["IIIZ", "IIZI", "IZII", "IIZZ"])

estimated_expectation_values = ck.run(cut_circuit, observables, sim)

Automatic cuts

QCut comes with functionality for automatically finding good cut locations that can place both wire and gate cuts.

from QCut import find_cuts

cut_circuit = find_cuts(circuit , 3, cuts="both")

estimated_expectation_values = ck.run_cut_circuit(cut_circuit, observables, sim)

np.set_printoptions(formatter={"float": lambda x: f"{x:0.6f}"})

print(f"QCut expectation values:{np.array(estimated_expectation_values)}")
print(f"Exact expectation values with ideal simulator :{np.array(exact_expvals)}")

QCut expectation values:[0.699436 0.713172 0.713172 0.979377]

Exact expectation values with ideal simulator :[0.727323 0.727323 0.727323 1.000000]

Running on IQM fake backends

To use QCut with IQM's fake backends it is required to install Qiskit IQM for Qiskit <= 1.2 or IQM client for Qiskit > 1.2.

Installation can be doen via uv:

uv pip install qiskit-iqm==17.8

or

uv pip install iqm-client[qiskit]

After installation just import the backend you want to use:

from iqm.qiskit_iqm import IQMFakeAdonis
backend = IQMFakeAdonis()

Running on FiQCI

For running on real hardware using the Lumi supercomputer follow the instructions here. If you are used to using Qiskit on jupyter notebooks it is recommended to use the Lumi web interface.

Running on other hardware

Running on other providers such as IBM is untested at the moment but as long as the hardware can be accessed with Qiskit QCut should be compatible.

Documentation

Check out jooniv.github.io/QCut/ for documentation and more examples.

The docs are built with sphinx using the sphinx book theme. To build the docs:

uv sync --group docs
cd docs
uv run sphinx-build -v -b html . build/sphinx/html -W

HTML files can then be found under build/sphinx/html/

Acknowledgements

This project is built on top of Qiskit which is licensed under the Apache 2.0 license.

License

Apache 2.0 license

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

qcut-1.3.2.tar.gz (626.1 kB view details)

Uploaded Source

Built Distribution

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

qcut-1.3.2-py3-none-any.whl (520.9 kB view details)

Uploaded Python 3

File details

Details for the file qcut-1.3.2.tar.gz.

File metadata

  • Download URL: qcut-1.3.2.tar.gz
  • Upload date:
  • Size: 626.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for qcut-1.3.2.tar.gz
Algorithm Hash digest
SHA256 cb6fd152e857a97c9ecf1b53d7db8f98a507aeb91d6293a63fbecc5722e52637
MD5 0b6381af4b77d19830fedf5aff5e467a
BLAKE2b-256 eecb7307cd49ade6034942cd56389536895a626f458dd80bf4cece72912fdb21

See more details on using hashes here.

File details

Details for the file qcut-1.3.2-py3-none-any.whl.

File metadata

  • Download URL: qcut-1.3.2-py3-none-any.whl
  • Upload date:
  • Size: 520.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for qcut-1.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d5220474967fa21af28251a9ee5c03c57f1f3c4ba59a8a3fc2d05b3ca405c3c9
MD5 73d3d3755ae812d334941f3298c0dbb1
BLAKE2b-256 1279bd38e34eec02d43245ab6d371ff54df9ba8ddcb252aa43cf1f7c462a0ec4

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