Source code for qiskit_experiments.framework.composite.batch_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."""Batch Experiment class."""fromtypingimportList,Optional,DictfromcollectionsimportOrderedDict,defaultdictfromqiskitimportQuantumCircuitfromqiskit.providersimportJob,Backend,Optionsfromqiskit_ibm_runtimeimportSamplerV2asSamplerfrom.composite_experimentimportCompositeExperiment,BaseExperimentfrom.composite_analysisimportCompositeAnalysis
[docs]classBatchExperiment(CompositeExperiment):"""Combine multiple experiments into a batch experiment. Batch experiments combine individual experiments on any subset of qubits into a single composite experiment which appends all the circuits from each component experiment into a single batch of circuits to be executed as one experiment job. Analysis of batch experiments is performed using the :class:`~qiskit_experiments.framework.CompositeAnalysis` class which handles sorting 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 a batch experiment. 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. """# Generate qubit mapself._qubit_map=OrderedDict()logical_qubit=0forexprinexperiments:forphysical_qubitinexpr.physical_qubits:ifphysical_qubitnotinself._qubit_map:self._qubit_map[physical_qubit]=logical_qubitlogical_qubit+=1qubits=tuple(self._qubit_map.keys())super().__init__(experiments,qubits,backend=backend,analysis=analysis,flatten_results=flatten_results,experiment_type=experiment_type,)
def_transpiled_circuits(self):returnself._batch_circuits(to_transpile=True)def_batch_circuits(self,to_transpile=False):batch_circuits=[]# Generate data for combinationforindex,exprinenumerate(self._experiments):ifself.physical_qubits==expr.physical_qubitsorto_transpile:qubit_mapping=Noneelse:qubit_mapping=[self._qubit_map[qubit]forqubitinexpr.physical_qubits]ifisinstance(expr,BatchExperiment):# Batch experiments don't contain their own native circuits.# If to_transpile is True then the circuits will be transpiled at the non-batch# experiments.# Fetch the circuits from the sub-experiments.expr_circuits=expr._batch_circuits(to_transpile)elifto_transpile:expr_circuits=expr._transpiled_circuits()else:expr_circuits=expr.circuits()forcircuitinexpr_circuits:# Update metadatacircuit.metadata={"experiment_type":self._type,"composite_metadata":[circuit.metadata],"composite_index":[index],}# Remap qubits if requiredifqubit_mapping:circuit=self._remap_qubits(circuit,qubit_mapping)batch_circuits.append(circuit)returnbatch_circuitsdef_remap_qubits(self,circuit,qubit_mapping):"""Remap qubits if physical qubit layout is different to batch layout"""num_qubits=self.num_qubitsnum_clbits=circuit.num_clbitsnew_circuit=QuantumCircuit(num_qubits,num_clbits,name="batch_"+circuit.name)new_circuit.metadata=circuit.metadatanew_circuit.append(circuit,qubit_mapping,list(range(num_clbits)))returnnew_circuitdef_run_jobs_recursive(self,circuits:List[QuantumCircuit],truncated_metadata:List[Dict],sampler:Sampler=None,**run_options,)->List[Job]:# The truncated metadata is a truncation of the original composite metadata.# During the recursion, the current experiment (self) will be at the head of the truncated# metadata.ifself.experiment_options.separate_jobs:# A dictionary that maps sub-experiments to their circuitscircs_by_subexps=defaultdict(list)forcirc_iter,(circ,tmd)inenumerate(zip(circuits,truncated_metadata)):# For batch experiments the composite index is always a list of length 1,# because unlike parallel experiment, each circuit originates from a single# sub-experiment.circ_index=tmd["composite_index"][0]circs_by_subexps[circ_index].append(circ)# For batch experiments the composite metadata is always a list of length 1,# because unlike parallel experiment, each circuit originates from a single# sub-experiment.truncated_metadata[circ_iter]=tmd["composite_metadata"][0]jobs=[]forindex,expinenumerate(self.component_experiment()):# Currently all the sub-experiments must use the same set of run options,# even if they run in different jobsifisinstance(exp,BatchExperiment):new_jobs=exp._run_jobs_recursive(circs_by_subexps[index],truncated_metadata,sampler,**run_options)else:new_jobs=exp._run_jobs(circs_by_subexps[index],sampler,**run_options)jobs.extend(new_jobs)else:jobs=super()._run_jobs(circuits,sampler,**run_options)returnjobsdef_run_jobs(self,circuits:List[QuantumCircuit],sampler:Sampler=None,**run_options)->List[Job]:truncated_metadata=[circ.metadataforcircincircuits]jobs=self._run_jobs_recursive(circuits,truncated_metadata,sampler,**run_options)returnjobs@classmethoddef_default_experiment_options(cls)->Options:"""Default experiment options. Experiment Options: separate_jobs (Boolean): Whether to route different sub-experiments to different jobs. """options=super()._default_experiment_options()options.separate_jobs=Falsereturnoptions