QCut API reference#

Submodules#

QCut.backend_utility module#

Utility functions for running on real backends.

QCut.backend_utility.expectation_values(counts: dict, observables: list, shots: int) list[source]#

Calculate expectation values.

Parameters:
  • counts (dict) – Counts obtained from circuit run, where keys are measurement outcomes and values are the number of times each outcome was observed.

  • observables (list) – List of observables to calculate expectation values for. Each observable can be an integer (index of a single qubit) or a list of integers (indices of multiple qubits).

  • shots (int) – Number of shots (total number of measurements).

Returns:

A list of expectation values for each observable.

Return type:

list

QCut.backend_utility.run_and_expectation_value(circuit: QuantumCircuit, backend, observables: list, shots: int) tuple[dict, list][source]#

Run circuit and calculate expectation value.

Parameters:
  • circuit (QuantumCircuit) – A quantum circuit.

  • backend – Backend to run circuit on.

  • observables (list) – Observables to calculate expectation values for.

  • shots (int) – Number of shots.

Returns:

A tuple containing:
  • dict: Counts from the circuit run.

  • list: A list of expectation values.

Return type:

tuple

QCut.backend_utility.run_on_backend(circuit: QuantumCircuit, backend, shots: int) dict[source]#

Run a quantum circuit on a specified backend.

Parameters:
  • circuit (QuantumCircuit) – The quantum circuit to be executed.

  • backend (Backend) – The backend to use for executing the circuit.

  • shots (int) – The number of shots (repetitions) to run the circuit.

Returns:

A dictionary of counts from the circuit run.

Return type:

dict

QCut.backend_utility.transpile_experiments(experiment_circuits: list | CutCircuit, backend, transpile_options: dict = None) CutCircuit[source]#

Transpile experiment circuits. Transpiles all generated experiment circuits for a given backend. Most often one should use transpile_subcircuits instead, as that only subcircuits before experiment generation which is alot more efficient. This function is mainly provided for special cases where one needs/wants extra control over the tranpilation of experiment circuits.

Parameters:
  • experiment_circuits – (list): Experiment circuits to be transpiled.

  • backend (str) – Backend to transpile to.

  • transpile_options (dict) – Arguments passed to qiskit transpile function.

Returns:

Transpiled experiment circuits wrapped in CutCircuit class.

Return type:

CutCircuit

QCut.backend_utility.transpile_subcircuits(subcircuits: list[QuantumCircuit], cut_locations: list, backend, optimization_level: int = 0, transpile_options: dict = None) CutCircuit[source]#

Transpile subcircuits for a given backend. More efficient than transpiling experiment circuits as it only transpiles each subcircuit once instead of each experiment circuit. However, may lead to suboptimal transpilation results as the tranpiler cannot use the backend object directly due to need to retain some placeholder gates for cuts and observables. transpile_options can be used to pass additional options to the transpiler. For more control over transpilation of experiment circuits, use transpile_experiments or manually transpile them.

Parameters:
  • subcircuits (list[QuantumCircuit]) – List of subcircuits to be transpiled.

  • backend – Backend to transpile to.

  • optimization_level (int) – Optimization level for transpilation (0-3).

  • transpile_options (dict) – Arguments passed to qiskit transpile function.

Returns:

Transpiled subcircuits wrapped in CutCircuit class.

Return type:

CutCircuit

QCut.helper module#

Helper functions for circuit knitting.

QCut.helper.get_pauli_list(input_list: list, length: int) PauliList[source]#

Transform list of observable indices to Paulilist of Z observables.

Parameters:
  • input_list (list) – list of observables as qubit indices

  • length (int) – number of qubits in the circuit

Returns:

a PauliList of Z observables

Return type:

PauliList

QCut.helper.relative_error(actual: list, approx: list) list[source]#

Calculate the relative error between actual and approximate values. :param actual: The list of actual values. :type actual: list :param approx: The list of approximate values. :type approx: list

Returns:

The list of relative errors for each corresponding pair of actual and approximate values.

Return type:

list

Raises:

ValueError – If the lengths of actual and approx lists are not the same.

QCut.qpd_gates module#

Helper gates for circuit knitting.

QCut.qpd_gates.cutGate(gate: Gate, control: int, target: int) dict[source]#

Return a cutCZ circuit with the same parameters as the input gate.

QCut.identity_qpd module#

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

QCut.identity_qpd.append(object, /)#

Append object to the end of the list.

QCut.identity_qpd.clear()#

Remove all items from list.

QCut.identity_qpd.copy()#

Return a shallow copy of the list.

QCut.identity_qpd.count(value, /)#

Return number of occurrences of value.

QCut.identity_qpd.extend(iterable, /)#

Extend list by appending elements from the iterable.

QCut.identity_qpd.index(value, start=0, stop=9223372036854775807, /)#

Return first index of value.

Raises ValueError if the value is not present.

QCut.identity_qpd.insert(index, object, /)#

Insert object before index.

QCut.identity_qpd.pop(index=-1, /)#

Remove and return item at index (default last).

Raises IndexError if list is empty or index is out of range.

QCut.identity_qpd.remove(value, /)#

Remove first occurrence of value.

Raises ValueError if the value is not present.

QCut.identity_qpd.reverse()#

Reverse IN PLACE.

QCut.identity_qpd.sort(*, key=None, reverse=False)#

Sort the list in ascending order and return None.

The sort is in-place (i.e. the list itself is modified) and stable (i.e. the order of two equal elements is maintained).

If a key function is given, apply it once to each list item and sort them, ascending or descending, according to their function values.

The reverse flag can be set to sort in descending order.

QCut.wirecut module#

Circuit knitting wire cut functionality.

QCut.wirecut.estimate_expectation_values(results: list[TotalResult], coefficients: list[int], cut_locations: ndarray[CutLocation], observables: list[int | list[int]], map_qubits: dict[int, int] | None = None) list[float][source]#

Calculate the estimated expectation values.

Loop through processed results. For each result group generate all products of different measurements from different subcircuits of the group. For each result from qpd measurements calculate qpd coefficient and from counts calculate weight. Get results for qubits corresponding to the observables. If multiqubit observable multiply individual qubit eigenvalues and multiply by (-1)^(m+1) where m is number of qubits in the observable. Multiply by weight and add to sub expectation value. Once all results iterated over move to next circuit group. Lastly multiply by 4^(2*n), where n is the number of cuts, and divide by number of samples.

Parameters:
  • results (list[TotalResult]) – results from experiment circuits

  • coefficients (list[int]) – list of coefficients for each subcircuit group

  • cut_locations (np.ndarray[CutLocation]) – cut locations

  • observables (list[int | list[int]]) – observables to calculate expectation values for

Returns:

expectation values as a list of floats

Return type:

list[float]

QCut.wirecut.get_experiment_circuits(subcircuits: list[QuantumCircuit] | CutCircuit, cut_locations: ndarray[CutLocation]) tuple[CutCircuit, list[int], list[tuple[int, int, int]]][source]#

Generate experiment circuits by inserting QPD operations on measure/initialize nodes.

Loop through qpd combinations. Calculate coefficient for subcircuit group by taking the product of all coefficients in the current qpd row. Loop through subcircuits generated in 4. Make deepcopy of subcircuit and iterate over its circuit data. When hit either Meas_{ind} of Init_{ind} repace it with operation found in qpd[ind][“op_0”/”op_1”]. WHile generating experiment circuits also generate a list of locations that have an identity basis measurement. These measurement outcomes need to be added during post-processing. Locations added as [index of experiment circuit, index of subcircuit, index of classical bit corresponding to measurement]. Repeat untill looped through all qpd rows. sircuits reutrned as [circuit_group0, circuit_group1, …], where circuit_goup is [subciruit0, subcircuit1, …].

Parameters:
  • subcircuits (list[QuantumCircuit]) – subcircuits with measure/initialize nodes.

  • cut_locations (np.ndarray[CutLocation]) – cut locations.

Returns:

A tuple containing:
  • CutCircuit: A CutCircuit object containing the experiment circuits.

  • list[int]: A list of coefficients for each circuit.

  • list[tuple[int, int, int]]:

    A list of index pointers to results that need additional post-processing due to identity basis measurement.

Return type:

tuple

QCut.wirecut.get_placeholder_locations(subcircuits: list[QuantumCircuit]) list[source]#

Identify the locations of placeholder operations in a list of quantum subcircuits. This function scans through each quantum subcircuit provided in the input list and identifies the indices and operations where either measurement (“Meas”) or initialization (“Init”) operations occur. It returns a list of lists, where each sublist corresponds to a subcircuit and contains tuples of the form (index, operation).

Parameters:

subcircuits (list[QuantumCircuit]) – A list of QuantumCircuit objects to be analyzed.

Returns:

A list of lists, where each sublist contains tuples (index, operation) indicating the positions of measurement or initialization operations in the corresponding subcircuit.

Return type:

list

QCut.wirecut.get_qpd_combinations(cut_locations: np.ndarray[CutLocation]) Iterable[tuple[dict]][source]#

Get all possible combinations of the QPD operations so that each combination has len(cut_locations) elements.

For a single cut operations can be straightforwardly inserted from the identity qpd. If multiple cuts are made one need to take the cartesian product of the identity qpd with itself n times, where n is number of cuts. This will give a qpd with 8^n rows. Each row corresponds to a subcircuit group. These operations can then be inserted to generate the experiment circuits.

Parameters:

cut_locations (np.ndarray[CutLocation]) – cut locations

Returns:

Iterable of the possible QPD operations

Return type:

Iterable[tuple[dict]]

QCut.wirecut.insert_cz_cut_qpd(ind, op, subcircuit, offset, qpd_qubits, qpd, id_meas, num_id_meas, id_meas_experiment_index, id_meas_subcircuit_index, id_meas_bit, classical_bit_index, inserted_operations)[source]#
QCut.wirecut.insert_wire_cut_qpd(ind, op, subcircuit, offset, qpd_qubits, qpd, id_meas, num_id_meas, id_meas_experiment_index, id_meas_subcircuit_index, id_meas_bit, classical_bit_index, inserted_operations)[source]#
QCut.wirecut.run_experiments(experiment_circuits: CutCircuit, cut_locations: ndarray[CutLocation], id_meas: list[tuple[int, int, int]], shots: int = 4096, backend: None = None) list[TotalResult][source]#

Run experiment circuits.

Loop through experiment circuits and then loop through circuit group and run each circuit. Store results as [group0, group1, …] where group is [res0, res1, …]. where res is “xxx yy”: count xxx are the measurements from the end of circuit measurements on the meas classical register and yy are the qpd basis measurement results from the qpd_meas class register.

Parameters:
  • experiment_circuits (CutCircuit) – experiment circuits

  • cut_locations (np.ndarray[CutLocation]) – list of cut locations

  • id_meas (list[int, int, int]) – list of identity basis measurement locations

  • shots (int) – number of shots per circuit run (optional)

  • backend – backend used for running the circuits (optional)

Returns:

list of transformed results

Return type:

list[TotalResult]

Module contents#

Init circuit knitting.

QCut.cutGate(gate: Gate, control: int, target: int) dict[source]#

Return a cutCZ circuit with the same parameters as the input gate.

QCut.estimate_expectation_values(results: list[TotalResult], coefficients: list[int], cut_locations: ndarray[CutLocation], observables: list[int | list[int]], map_qubits: dict[int, int] | None = None) list[float][source]#

Calculate the estimated expectation values.

Loop through processed results. For each result group generate all products of different measurements from different subcircuits of the group. For each result from qpd measurements calculate qpd coefficient and from counts calculate weight. Get results for qubits corresponding to the observables. If multiqubit observable multiply individual qubit eigenvalues and multiply by (-1)^(m+1) where m is number of qubits in the observable. Multiply by weight and add to sub expectation value. Once all results iterated over move to next circuit group. Lastly multiply by 4^(2*n), where n is the number of cuts, and divide by number of samples.

Parameters:
  • results (list[TotalResult]) – results from experiment circuits

  • coefficients (list[int]) – list of coefficients for each subcircuit group

  • cut_locations (np.ndarray[CutLocation]) – cut locations

  • observables (list[int | list[int]]) – observables to calculate expectation values for

Returns:

expectation values as a list of floats

Return type:

list[float]

QCut.expectation_values(counts: dict, observables: list, shots: int) list[source]#

Calculate expectation values.

Parameters:
  • counts (dict) – Counts obtained from circuit run, where keys are measurement outcomes and values are the number of times each outcome was observed.

  • observables (list) – List of observables to calculate expectation values for. Each observable can be an integer (index of a single qubit) or a list of integers (indices of multiple qubits).

  • shots (int) – Number of shots (total number of measurements).

Returns:

A list of expectation values for each observable.

Return type:

list

QCut.find_cuts(circuit, num_partitions=None, max_qubits=None, cuts='both', more_data=False)[source]#

Partition a quantum circuit into subcircuits by inserting cut operations.

Converts the input circuit into a graph representation and partitions it into the specified number of subcircuits using METIS partitioning. Optionally refines the partitioning to respect maximum qubit constraints per subcircuit. Cut operations are inserted at the partition boundaries, and the resulting subcircuits and their mappings are returned.

Parameters:
  • circuit (QuantumCircuit) – The input quantum circuit to partition.

  • num_partitions (int, optional) – Number of partitions to create. If None, determined from max_qubits.

  • max_qubits (list[int], optional) – Maximum number of qubits allowed in each partition. If specified, must match num_partitions.

  • cuts (str, optional) – Type of cuts to insert. Can be “both”, “gate”, or “wire”. Defaults to “both”.

  • more_data (bool, optional) – If True, returns additional data for debugging and analysis. Defaults to False.

Returns:

If more_data is False, returns:
  • list[list[int]]: Qubit locations for each subcircuit.

  • list[QuantumCircuit]: List of subcircuits after cuts.

  • list[dict]: Mapping of qubits for each subcircuit.

If more_data is True, returns:
  • list[list[int]]: Qubit locations for each subcircuit.

  • list[QuantumCircuit]: List of subcircuits after cuts.

  • list[dict]: Mapping of qubits for each subcircuit.

  • QuantumCircuit: The circuit with cuts inserted.

  • list: Data describing the cuts.

  • list: Additional cut data for testing.

  • dict: Partition labels for each node.

  • rustworkx.PyGraph: The graph representation of the circuit.

  • dict: Mapping of nodes to qubits.

Return type:

tuple

QCut.get_experiment_circuits(subcircuits: list[QuantumCircuit] | CutCircuit, cut_locations: ndarray[CutLocation]) tuple[CutCircuit, list[int], list[tuple[int, int, int]]][source]#

Generate experiment circuits by inserting QPD operations on measure/initialize nodes.

Loop through qpd combinations. Calculate coefficient for subcircuit group by taking the product of all coefficients in the current qpd row. Loop through subcircuits generated in 4. Make deepcopy of subcircuit and iterate over its circuit data. When hit either Meas_{ind} of Init_{ind} repace it with operation found in qpd[ind][“op_0”/”op_1”]. WHile generating experiment circuits also generate a list of locations that have an identity basis measurement. These measurement outcomes need to be added during post-processing. Locations added as [index of experiment circuit, index of subcircuit, index of classical bit corresponding to measurement]. Repeat untill looped through all qpd rows. sircuits reutrned as [circuit_group0, circuit_group1, …], where circuit_goup is [subciruit0, subcircuit1, …].

Parameters:
  • subcircuits (list[QuantumCircuit]) – subcircuits with measure/initialize nodes.

  • cut_locations (np.ndarray[CutLocation]) – cut locations.

Returns:

A tuple containing:
  • CutCircuit: A CutCircuit object containing the experiment circuits.

  • list[int]: A list of coefficients for each circuit.

  • list[tuple[int, int, int]]:

    A list of index pointers to results that need additional post-processing due to identity basis measurement.

Return type:

tuple

QCut.get_locations_and_subcircuits(circuit: QuantumCircuit, max_qubits: list[int] | None = None)[source]#

Get cut locations and subcircuits with placeholder operations.

Parameters:
  • circuit (QuantumCircuit) – circuit with cuts inserted

  • max_qubits (list[int], optional) – list of maximum qubits per subcircuit when using automatic cut finding. If None, no constraint is used. Defaults to None. In general it is not necesary to manually specify this parameter.

Returns:

A tuple containing:
  • list[SingleQubitCutLocation]: Locations of the cuts as a list

  • list[QuantumCircuit]: Subcircuits with placeholder operations

  • dict[int:int]: map of subcircuit qubit indices to original circuit

    qubit indices

Return type:

tuple

QCut.get_pauli_list(input_list: list, length: int) PauliList[source]#

Transform list of observable indices to Paulilist of Z observables.

Parameters:
  • input_list (list) – list of observables as qubit indices

  • length (int) – number of qubits in the circuit

Returns:

a PauliList of Z observables

Return type:

PauliList

QCut.run(circuit: QuantumCircuit, observables: list[int, list[int]], backend=AerSimulator('aer_simulator')) list[float][source]#

Run the whole circuit knitting sequence with one function call.

Parameters:
  • circuit (QuantumCircuit) – circuit with cut experiments

  • observables (list[int | list[int]]) – list of observbles in the form of qubit indices (Z-obsevable).

  • backend – backend to use for running experiment circuits (optional)

Returns:

a list of expectation values

Return type:

list

QCut.run_and_expectation_value(circuit: QuantumCircuit, backend, observables: list, shots: int) tuple[dict, list][source]#

Run circuit and calculate expectation value.

Parameters:
  • circuit (QuantumCircuit) – A quantum circuit.

  • backend – Backend to run circuit on.

  • observables (list) – Observables to calculate expectation values for.

  • shots (int) – Number of shots.

Returns:

A tuple containing:
  • dict: Counts from the circuit run.

  • list: A list of expectation values.

Return type:

tuple

QCut.run_cut_circuit(subcircuits: list[QuantumCircuit], cut_locations: ndarray[SingleQubitCutLocation], observables: list[int | list[int]], map_qubits: dict[int, int], backend=AerSimulator('aer_simulator')) ndarray[float][source]#

After splitting the circuit run the rest of the circuit knitting sequence.

Parameters:
  • subcircuits (list[QuantumCircuit]) – subcircuits containing the placeholder operations

  • cut_locations (np.ndarray[CutLocation]) – list of cut locations

  • observables (list[int | list[int]]) – list of observables as qubit indices (Z observable)

  • backend – backend to use for running experiment circuits (optional)

Returns:

a list of expectation values

Return type:

list

QCut.run_experiments(experiment_circuits: CutCircuit, cut_locations: ndarray[CutLocation], id_meas: list[tuple[int, int, int]], shots: int = 4096, backend: None = None) list[TotalResult][source]#

Run experiment circuits.

Loop through experiment circuits and then loop through circuit group and run each circuit. Store results as [group0, group1, …] where group is [res0, res1, …]. where res is “xxx yy”: count xxx are the measurements from the end of circuit measurements on the meas classical register and yy are the qpd basis measurement results from the qpd_meas class register.

Parameters:
  • experiment_circuits (CutCircuit) – experiment circuits

  • cut_locations (np.ndarray[CutLocation]) – list of cut locations

  • id_meas (list[int, int, int]) – list of identity basis measurement locations

  • shots (int) – number of shots per circuit run (optional)

  • backend – backend used for running the circuits (optional)

Returns:

list of transformed results

Return type:

list[TotalResult]

QCut.run_on_backend(circuit: QuantumCircuit, backend, shots: int) dict[source]#

Run a quantum circuit on a specified backend.

Parameters:
  • circuit (QuantumCircuit) – The quantum circuit to be executed.

  • backend (Backend) – The backend to use for executing the circuit.

  • shots (int) – The number of shots (repetitions) to run the circuit.

Returns:

A dictionary of counts from the circuit run.

Return type:

dict

QCut.transpile_experiments(experiment_circuits: list | CutCircuit, backend, transpile_options: dict = None) CutCircuit[source]#

Transpile experiment circuits. Transpiles all generated experiment circuits for a given backend. Most often one should use transpile_subcircuits instead, as that only subcircuits before experiment generation which is alot more efficient. This function is mainly provided for special cases where one needs/wants extra control over the tranpilation of experiment circuits.

Parameters:
  • experiment_circuits – (list): Experiment circuits to be transpiled.

  • backend (str) – Backend to transpile to.

  • transpile_options (dict) – Arguments passed to qiskit transpile function.

Returns:

Transpiled experiment circuits wrapped in CutCircuit class.

Return type:

CutCircuit

QCut.transpile_subcircuits(subcircuits: list[QuantumCircuit], cut_locations: list, backend, optimization_level: int = 0, transpile_options: dict = None) CutCircuit[source]#

Transpile subcircuits for a given backend. More efficient than transpiling experiment circuits as it only transpiles each subcircuit once instead of each experiment circuit. However, may lead to suboptimal transpilation results as the tranpiler cannot use the backend object directly due to need to retain some placeholder gates for cuts and observables. transpile_options can be used to pass additional options to the transpiler. For more control over transpilation of experiment circuits, use transpile_experiments or manually transpile them.

Parameters:
  • subcircuits (list[QuantumCircuit]) – List of subcircuits to be transpiled.

  • backend – Backend to transpile to.

  • optimization_level (int) – Optimization level for transpilation (0-3).

  • transpile_options (dict) – Arguments passed to qiskit transpile function.

Returns:

Transpiled subcircuits wrapped in CutCircuit class.

Return type:

CutCircuit