Source code for qiskit_experiments.framework.composite.parallel_experiment
# This code is part of Qiskit.## (C) Copyright IBM 2021.## This code is licensed under the Apache License, Version 2.0. You may# obtain a copy of this license in the LICENSE.txt file in the root directory# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.## Any modifications or derivative works of this code must retain this# copyright notice, and modified files need to carry a notice indicating# that they have been altered from the originals."""Parallel Experiment class."""fromtypingimportList,OptionalimportnumpyasnpfromqiskitimportQuantumCircuit,ClassicalRegisterfromqiskit.circuitimportClbitfromqiskit.providers.backendimportBackendfromqiskit_experiments.exceptionsimportQiskitErrorfrom.composite_experimentimportCompositeExperiment,BaseExperimentfrom.composite_analysisimportCompositeAnalysis
[docs]classParallelExperiment(CompositeExperiment):"""Combine multiple experiments into a parallel experiment. Parallel experiments combine individual experiments on disjoint subsets of qubits into a single composite experiment on the union of those qubits. The component experiment circuits are combined to run in parallel on the respective qubits. Analysis of parallel experiments is performed using the :class:`~qiskit_experiments.framework.CompositeAnalysis` class which handles marginalizing the composite experiment circuit data into individual child :class:`ExperimentData` containers for each component experiment which are then analyzed using the corresponding analysis class for that component experiment. See :class:`~qiskit_experiments.framework.CompositeAnalysis` documentation for additional information. """def__init__(self,experiments:List[BaseExperiment],backend:Optional[Backend]=None,flatten_results:bool=True,analysis:Optional[CompositeAnalysis]=None,experiment_type:Optional[str]=None,):"""Initialize the analysis object. Args: experiments: a list of experiments. backend: Optional, the backend to run the experiment on. flatten_results: If True flatten all component experiment results into a single ExperimentData container, including nested composite experiments. If False save each component experiment results as a separate child ExperimentData container. This kwarg is ignored if the analysis kwarg is used. analysis: Optional, the composite analysis class to use. If not provided this will be initialized automatically from the supplied experiments. """qubits=[]forexpinexperiments:qubits+=exp.physical_qubitssuper().__init__(experiments,qubits,backend=backend,analysis=analysis,flatten_results=flatten_results,experiment_type=experiment_type,)
def_transpiled_circuits(self):returnself._combined_circuits(device_layout=True)def_combined_circuits(self,device_layout:bool)->List[QuantumCircuit]:"""Generate combined parallel circuits from transpiled subcircuits."""ifnotdevice_layout:# Num qubits will be computed from sub experimentsnum_qubits=len(self.physical_qubits)else:# Work around for backend coupling map circuit inflationcoupling_map=getattr(self.transpile_options,"coupling_map",None)ifcoupling_mapisNoneandself.backend:coupling_map=self._backend_data.coupling_mapifcoupling_mapisnotNone:num_qubits=1+max(*self.physical_qubits,np.max(coupling_map))else:num_qubits=1+max(self.physical_qubits)joint_circuits=[]sub_qubits=0forexp_idx,sub_expinenumerate(self._experiments):# Generate transpiled subcircuitssub_circuits=sub_exp._transpiled_circuits()# Qubit remapping for non-transpiled circuitsifnotdevice_layout:qubits=list(range(sub_qubits,sub_qubits+sub_exp.num_qubits))qargs_map={q:qubits[i]fori,qinenumerate(sub_exp.physical_qubits)}sub_qubits+=sub_exp.num_qubitselse:qubits=list(sub_exp.physical_qubits)qargs_map={q:qforqinsub_exp.physical_qubits}forcirc_idx,sub_circinenumerate(sub_circuits):ifcirc_idx>=len(joint_circuits):# Initialize new joint circuit or extract# existing circuit if already initializednew_circuit=QuantumCircuit(num_qubits,name=f"parallel_exp_{circ_idx}")new_circuit.metadata={"experiment_type":self._type,"composite_index":[],"composite_metadata":[],"composite_qubits":[],"composite_clbits":[],}joint_circuits.append(new_circuit)# Add classical registers required by subcircuitcircuit=joint_circuits[circ_idx]num_clbits=circuit.num_clbitssub_clbits=sub_circ.num_clbitsclbits=list(range(num_clbits,num_clbits+sub_clbits))ifsub_clbits:creg=ClassicalRegister(sub_clbits)sub_cargs=[Clbit(creg,i)foriinrange(sub_clbits)]circuit.add_register(creg)else:sub_cargs=[]# Apply transpiled subcircuit# Note that this assumes the circuit was not expanded to use# any qubits outside the specified physical qubitsfordatainsub_circ.data:inst=data.operationqargs=data.qubitscargs=data.clbitsmapped_cargs=[sub_cargs[sub_circ.find_bit(i).index]foriincargs]try:mapped_qargs=[circuit.qubits[qargs_map[sub_circ.find_bit(i).index]]foriinqargs]exceptKeyErrorasex:# Instruction is outside physical qubits for the component# experiment.# This could legitimately happen if the subcircuit was# explicitly scheduled during transpilation which would# insert delays on all auxillary device qubits.# We skip delay instructions to allow for this.ifinst.name=="delay":continueraiseQiskitError("Component experiment has been transpiled outside of the ""allowed physical qubits for that component. Check the ""experiment is valid on the backends coupling map.")fromexcircuit._append(inst,mapped_qargs,mapped_cargs)# Add subcircuit metadatacircuit.metadata["composite_index"].append(exp_idx)circuit.metadata["composite_metadata"].append(sub_circ.metadata)circuit.metadata["composite_qubits"].append(qubits)circuit.metadata["composite_clbits"].append(clbits)# Add the calibrationsforgate,calsinsub_circ.calibrations.items():forkey,schedincals.items():circuit.add_calibration(gate,qubits=key[0],schedule=sched,params=key[1])returnjoint_circuits