"""
Utility functions for running on real backends.
"""
from __future__ import annotations
from qiskit import transpile
from qiskit.circuit import Gate
from qiskit.transpiler import Target
from QCut.circuit_utils import _remove_idle_wires
from QCut.cutcircuit import CutCircuit, CutExperiment
from QCut.cutlocation import CutLocation, SingleQubitCutLocation
[docs]
def transpile_subcircuits(cut_circuit: CutCircuit,
backend,
optimization_level: int = 0,
transpile_options: dict | None = None) -> CutCircuit:
"""
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.
Args:
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:
CutCircuit: Transpiled subcircuits wrapped in CutCircuit class.
"""
if not isinstance(cut_circuit, CutCircuit):
raise ValueError("cut_circuit must be of type CutCircuit.")
custom_gates = {}
for ind, i in enumerate(cut_circuit.cut_locations):
if isinstance(i, CutLocation):
custom_gates[f"cutCZ_t_{ind}"] = Gate(
num_qubits=1, name=f"cutCZ_t_{ind}", params=[], label=f"cutCZ_t_{ind}"
)
custom_gates[f"cutCZ_c_{ind}"] = Gate(
num_qubits=1, name=f"cutCZ_c_{ind}", params=[], label=f"cutCZ_c_{ind}"
)
elif isinstance(i, SingleQubitCutLocation):
custom_gates[f"Meas_{ind}"] = Gate(
num_qubits=1, name=f"Meas_{ind}", params=[], label=f"Meas_{ind}"
)
custom_gates[f"Init_{ind}"] = Gate(
num_qubits=1, name=f"Init_{ind}", params=[], label=f"Init_{ind}"
)
for i in range(sum([x.num_qubits for x in cut_circuit.subcircuits])):
custom_gates[f"obs_{i}"] = Gate(
num_qubits=1, name=f"obs_{i}", params=[], label=f"obs_{i}"
)
target = Target()
try:
basis_gates = list({i[0].name for i in backend._target.instructions})
except Exception:
return cut_circuit
target = target.from_configuration(
num_qubits=backend.num_qubits,
coupling_map=backend._coupling_map,
basis_gates=basis_gates + list(custom_gates.keys()),
custom_name_mapping=custom_gates,
)
transpiled = transpile(cut_circuit.subcircuits,
target=target,
optimization_level=optimization_level,
**(transpile_options or {}))
transpiled = [_remove_idle_wires(circ) for circ in transpiled]
return CutCircuit(subcircuits=transpiled, cut_locations=cut_circuit.cut_locations,
map_qubit=cut_circuit.map_qubit, backend=backend)
[docs]
def transpile_experiments(cut_experiment: CutExperiment,
backend,
optimization_level: int = 0,
transpile_options: dict | None = None) -> CutExperiment:
"""
Transpile experiment circuits. Transpiles all generated experiment circuits for
a given backend. Most often one should use `transpile_subcircuits` instead, as that
only transpiles subcircuits before experiment generation which is a lot more
efficient. This function is mainly provided for special cases where one needs/wants
extra control over the transpilation of experiment circuits.
Args:
cut_experiment: (CutExperiment): Experiment circuits to be transpiled.
backend (str): Backend to transpile to.
optimization_level (int): Optimization level for transpilation (0-3).
transpile_options (dict): Arguments passed to qiskit transpile function.
Returns:
CutExperiment: Transpiled experiment circuits wrapped in CutExperiment class.
"""
if not isinstance(cut_experiment, CutExperiment):
raise ValueError("cut_experiment must be of type CutExperiment.")
subexperiments = [
[
{
ind: transpile(circ,
backend=backend,
optimization_level=optimization_level,
**(transpile_options or {}))
for ind, circ in exp.items()
}
for exp in exps
]
for exps in cut_experiment.experiments
]
return CutExperiment(subexperiments,
cut_locations=cut_experiment.cut_locations,
map_qubit=cut_experiment.map_qubit,
coefficients=cut_experiment.coefficients,
observables=cut_experiment.observables,
backend=backend)