diff --git a/docs/manuals/characterization/t1.rst b/docs/manuals/characterization/t1.rst index afe0bb1130..8a926e9254 100644 --- a/docs/manuals/characterization/t1.rst +++ b/docs/manuals/characterization/t1.rst @@ -98,11 +98,10 @@ that is close to a logical value '0'. mu = 1e-6 # qubit properties - t1 = [45 * mu, 45 * mu] - t2 = [value/2 for value in t1] + t1 = 45 * mu # we will guess that our guess is 10% off the exact value of t1 for qubit 0. - t1_estimated_shift = t1[0]/10 + t1_estimated_shift = t1/10 # We use log space for the delays because of the noise properties delays = np.logspace(1, 11, num=23, base=np.exp(1)) @@ -111,25 +110,28 @@ that is close to a logical value '0'. # Adding circuits with delay=0 and long delays so the centers in the IQ plane won't be misplaced. # Without this, the fitting can provide wrong results. delays = np.insert(delays, 0, 0) - delays = np.append(delays, [t1[0]*3]) + delays = np.append(delays, [t1*3]) num_qubits = 2 num_shots = 2048 backend = MockIQBackend( - MockIQT1Helper(t1=t1, iq_cluster_centers=[((-5.0, -4.0), (-5.0, 4.0)), ((3.0, 1.0), (5.0, -3.0))] - , iq_cluster_width=[1.0, 2.0]) + MockIQT1Helper( + t1=t1, + iq_cluster_centers=[((-5.0, -4.0), (-5.0, 4.0)), ((3.0, 1.0), (5.0, -3.0))], + iq_cluster_width=[1.0, 2.0], + ) ) # Creating a T1 experiment expT1_kerneled = T1((0,), delays) expT1_kerneled.analysis = T1KerneledAnalysis() - expT1_kerneled.analysis.set_options(p0={"amp": 1, "tau": t1[0] + t1_estimated_shift, "base": 0}) + expT1_kerneled.analysis.set_options(p0={"amp": 1, "tau": t1 + t1_estimated_shift, "base": 0}) # Running the experiment - expdataT1_kerneled = expT1_kerneled.run(backend=backend, meas_return="avg", - meas_level=MeasLevel.KERNELED, - shots=num_shots).block_for_results() + expdataT1_kerneled = expT1_kerneled.run( + backend=backend, meas_return="avg", meas_level=MeasLevel.KERNELED, shots=num_shots + ).block_for_results() # Displaying results display(expdataT1_kerneled.figure(0)) diff --git a/docs/tutorials/custom_experiment.rst b/docs/tutorials/custom_experiment.rst index c1ccf36546..c97d07b9ce 100644 --- a/docs/tutorials/custom_experiment.rst +++ b/docs/tutorials/custom_experiment.rst @@ -365,6 +365,19 @@ signature to restore the output to what it should be without the random Pauli fr at the end. We make a new :class:`.AnalysisResultData` object since we're rewriting the counts from the original experiment. +.. note:: + + As you may find here, circuit metadata is mainly used to generate a structured data + in the analysis class for convenience of result handling. + A metadata supplied to a particular circuit should appear in the corresponding + experiment result data dictionary stored in the experiment data. + If you attach large amount of metadata which is not expected to be used in the analysis, + the metadata just unnecessarily increases the job payload memory footprint, + and it prevents your experiment class from scaling in qubit size through + the composite experiment tooling. + If you still want to store some experiment setting, which is common to all circuits + or irrelevant to the analysis, use the experiment metadata instead. + .. jupyter-input:: from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData diff --git a/qiskit_experiments/library/characterization/drag.py b/qiskit_experiments/library/characterization/drag.py index fb48add906..6a338212e8 100644 --- a/qiskit_experiments/library/characterization/drag.py +++ b/qiskit_experiments/library/characterization/drag.py @@ -171,8 +171,6 @@ def circuits(self) -> List[QuantumCircuit]: assigned_circuit = circuit.assign_parameters({beta: beta_val}, inplace=False) assigned_circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, "xval": beta_val, "nrep": rep, } diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 7cf32a6138..69232e2e48 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -162,10 +162,7 @@ def _spam_cal_circuits(self, meas_circuit: QuantumCircuit) -> List[QuantumCircui circ.compose(meas_circuit, inplace=True) circ.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, "xval": add_x, - "unit": "gate number", "series": "spam-cal", } @@ -229,10 +226,7 @@ def circuits(self) -> List[QuantumCircuit]: circuit.compose(meas_circ, qubits, range(meas_circ.num_clbits), inplace=True) circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, "xval": repetition, - "unit": "gate number", "series": 1, } diff --git a/qiskit_experiments/library/characterization/fine_drag.py b/qiskit_experiments/library/characterization/fine_drag.py index 668fdec8d1..24dafb060c 100644 --- a/qiskit_experiments/library/characterization/fine_drag.py +++ b/qiskit_experiments/library/characterization/fine_drag.py @@ -218,12 +218,7 @@ def circuits(self) -> List[QuantumCircuit]: params=[], ) - circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "xval": repetition, - "unit": "gate number", - } + circuit.metadata = {"xval": repetition} circuits.append(circuit) diff --git a/qiskit_experiments/library/characterization/fine_frequency.py b/qiskit_experiments/library/characterization/fine_frequency.py index 5505b320a9..a1a913e7f8 100644 --- a/qiskit_experiments/library/characterization/fine_frequency.py +++ b/qiskit_experiments/library/characterization/fine_frequency.py @@ -127,12 +127,7 @@ def circuits(self) -> List[QuantumCircuit]: circuit.sx(0) circuit.measure_all() - circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "xval": repetition, - "unit": "Number of delays", - } + circuit.metadata = {"xval": repetition} circuits.append(circuit) diff --git a/qiskit_experiments/library/characterization/half_angle.py b/qiskit_experiments/library/characterization/half_angle.py index 4743149512..c41adb677d 100644 --- a/qiskit_experiments/library/characterization/half_angle.py +++ b/qiskit_experiments/library/characterization/half_angle.py @@ -160,12 +160,7 @@ def circuits(self) -> List[QuantumCircuit]: circuit.sx(0) circuit.measure_all() - circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "xval": repetition, - "unit": "repetition number", - } + circuit.metadata = {"xval": repetition} circuits.append(circuit) diff --git a/qiskit_experiments/library/characterization/qubit_spectroscopy.py b/qiskit_experiments/library/characterization/qubit_spectroscopy.py index aacc2da5a7..064e10ac6c 100644 --- a/qiskit_experiments/library/characterization/qubit_spectroscopy.py +++ b/qiskit_experiments/library/characterization/qubit_spectroscopy.py @@ -123,7 +123,7 @@ def circuits(self): freq_shift = np.round(freq_shift, decimals=3) assigned_circ = circuit.assign_parameters({freq_param: freq_shift}, inplace=False) - self._add_metadata(assigned_circ, freq, sched) + self._add_metadata(assigned_circ, freq) circs.append(assigned_circ) diff --git a/qiskit_experiments/library/characterization/rabi.py b/qiskit_experiments/library/characterization/rabi.py index 9193b22dd5..3fb2a682d6 100644 --- a/qiskit_experiments/library/characterization/rabi.py +++ b/qiskit_experiments/library/characterization/rabi.py @@ -163,13 +163,7 @@ def circuits(self) -> List[QuantumCircuit]: # which isn't serializable in the metadata. amp = float(np.round(amp, decimals=6)) assigned_circ = circuit.assign_parameters({param: amp}, inplace=False) - assigned_circ.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "xval": amp, - "unit": "arb. unit", - "amplitude": amp, - } + assigned_circ.metadata = {"xval": amp} circs.append(assigned_circ) diff --git a/qiskit_experiments/library/characterization/ramsey_xy.py b/qiskit_experiments/library/characterization/ramsey_xy.py index 3a4046253e..1d0238b652 100644 --- a/qiskit_experiments/library/characterization/ramsey_xy.py +++ b/qiskit_experiments/library/characterization/ramsey_xy.py @@ -155,20 +155,12 @@ def circuits(self) -> List[QuantumCircuit]: rotation_angle = rotation_angle * timing.dt # Create the X and Y circuits. - metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "osc_freq": self.experiment_options.osc_freq, - "unit": "s", - } - ram_x = self._pre_circuit() ram_x.sx(0) ram_x.delay(p_delay, 0, timing.delay_unit) ram_x.rz(rotation_angle, 0) ram_x.sx(0) ram_x.measure_active() - ram_x.metadata = metadata.copy() ram_y = self._pre_circuit() ram_y.sx(0) @@ -176,21 +168,22 @@ def circuits(self) -> List[QuantumCircuit]: ram_y.rz(rotation_angle - np.pi / 2, 0) ram_y.sx(0) ram_y.measure_active() - ram_y.metadata = metadata.copy() circs = [] for delay in self.experiment_options.delays: - assigned_x = ram_x.assign_parameters( - {p_delay: timing.round_delay(time=delay)}, inplace=False - ) - assigned_x.metadata["series"] = "X" - assigned_x.metadata["xval"] = timing.delay_time(time=delay) - - assigned_y = ram_y.assign_parameters( - {p_delay: timing.round_delay(time=delay)}, inplace=False - ) - assigned_y.metadata["series"] = "Y" - assigned_y.metadata["xval"] = timing.delay_time(time=delay) + delay_dt = timing.round_delay(time=delay) + delay_sec = timing.delay_time(time=delay) + + assigned_x = ram_x.assign_parameters({p_delay: delay_dt}, inplace=False) + assigned_x.metadata = { + "series": "X", + "xval": delay_sec, + } + assigned_y = ram_y.assign_parameters({p_delay: delay_dt}, inplace=False) + assigned_y.metadata = { + "series": "Y", + "xval": delay_sec, + } circs.extend([assigned_x, assigned_y]) diff --git a/qiskit_experiments/library/characterization/readout_angle.py b/qiskit_experiments/library/characterization/readout_angle.py index 72df20c40b..b1c0c88a4f 100644 --- a/qiskit_experiments/library/characterization/readout_angle.py +++ b/qiskit_experiments/library/characterization/readout_angle.py @@ -85,16 +85,11 @@ def circuits(self) -> List[QuantumCircuit]: """ circ0 = QuantumCircuit(1, 1) circ0.measure(0, 0) + circ0.metadata = {"xval": 0} circ1 = QuantumCircuit(1, 1) circ1.x(0) circ1.measure(0, 0) - - for i, circ in enumerate([circ0, circ1]): - circ.metadata = { - "experiment_type": self._type, - "qubit": self.physical_qubits[0], - "xval": i, - } + circ1.metadata = {"xval": 1} return [circ0, circ1] diff --git a/qiskit_experiments/library/characterization/resonator_spectroscopy.py b/qiskit_experiments/library/characterization/resonator_spectroscopy.py index 1d7f6eabd3..373bf18e6f 100644 --- a/qiskit_experiments/library/characterization/resonator_spectroscopy.py +++ b/qiskit_experiments/library/characterization/resonator_spectroscopy.py @@ -283,7 +283,7 @@ def circuits(self): circuit = self._template_circuit() circuit.add_calibration("measure", self.physical_qubits, sched_) - self._add_metadata(circuit, freq, sched) + self._add_metadata(circuit, freq) circs.append(circuit) diff --git a/qiskit_experiments/library/characterization/spectroscopy.py b/qiskit_experiments/library/characterization/spectroscopy.py index 3ca3e3687f..90ec4b9281 100644 --- a/qiskit_experiments/library/characterization/spectroscopy.py +++ b/qiskit_experiments/library/characterization/spectroscopy.py @@ -17,7 +17,6 @@ import numpy as np from qiskit import QuantumCircuit -from qiskit import pulse from qiskit.exceptions import QiskitError from qiskit.providers import Backend from qiskit.qobj.utils import MeasLevel @@ -109,19 +108,13 @@ def _backend_center_frequency(self) -> float: which depends on the nature of the spectroscopy experiment. """ - def _add_metadata(self, circuit: QuantumCircuit, freq: float, sched: pulse.ScheduleBlock): + def _add_metadata(self, circuit: QuantumCircuit, freq: float): """Helper method to add the metadata to avoid code duplication with subclasses.""" if not self._absolute: freq += self._backend_center_frequency - circuit.metadata = { - "experiment_type": self._type, - "qubits": self.physical_qubits, - "xval": np.round(freq, decimals=3), - "unit": "Hz", - "schedule": str(sched), - } + circuit.metadata = {"xval": np.round(freq, decimals=3)} def _metadata(self): metadata = super()._metadata() diff --git a/qiskit_experiments/library/characterization/t1.py b/qiskit_experiments/library/characterization/t1.py index 43275d836c..751f9a569b 100644 --- a/qiskit_experiments/library/characterization/t1.py +++ b/qiskit_experiments/library/characterization/t1.py @@ -105,12 +105,7 @@ def circuits(self) -> List[QuantumCircuit]: circ.barrier(0) circ.measure(0, 0) - circ.metadata = { - "experiment_type": self._type, - "qubit": self.physical_qubits[0], - "unit": "s", - } - circ.metadata["xval"] = timing.delay_time(time=delay) + circ.metadata = {"xval": timing.delay_time(time=delay)} circuits.append(circ) diff --git a/qiskit_experiments/library/characterization/t2hahn.py b/qiskit_experiments/library/characterization/t2hahn.py index d15a9e8d70..57ce07d363 100644 --- a/qiskit_experiments/library/characterization/t2hahn.py +++ b/qiskit_experiments/library/characterization/t2hahn.py @@ -131,18 +131,12 @@ def circuits(self) -> List[QuantumCircuit]: """ timing = BackendTiming(self.backend) - template = QuantumCircuit(1, 1) - template.metadata = { - "experiment_type": self._type, - "qubit": self.physical_qubits[0], - "unit": "s", - } - delay_param = Parameter("delay") num_echoes = self.experiment_options.num_echoes # First X rotation in 90 degrees + template = QuantumCircuit(1, 1) template.rx(np.pi / 2, 0) # Brings the qubit to the X Axis if num_echoes == 0: # if number of echoes is 0 then just apply the delay gate @@ -174,7 +168,7 @@ def circuits(self) -> List[QuantumCircuit]: assigned = template.assign_parameters( {delay_param: timing.round_delay(time=single_delay)}, inplace=False ) - assigned.metadata["xval"] = total_delay + assigned.metadata = {"xval": total_delay} circuits.append(assigned) return circuits diff --git a/qiskit_experiments/library/characterization/t2ramsey.py b/qiskit_experiments/library/characterization/t2ramsey.py index 920c6f25f0..489839f0f1 100644 --- a/qiskit_experiments/library/characterization/t2ramsey.py +++ b/qiskit_experiments/library/characterization/t2ramsey.py @@ -125,13 +125,7 @@ def circuits(self) -> List[QuantumCircuit]: circ.barrier(0) circ.measure(0, 0) - circ.metadata = { - "experiment_type": self._type, - "qubit": self.physical_qubits[0], - "xval": timing.delay_time(time=delay), - "osc_freq": self.experiment_options.osc_freq, - "unit": "s", - } + circ.metadata = {"xval": timing.delay_time(time=delay)} circuits.append(circ) @@ -144,4 +138,5 @@ def _metadata(self): for run_opt in ["meas_level", "meas_return"]: if hasattr(self.run_options, run_opt): metadata[run_opt] = getattr(self.run_options, run_opt) + metadata["osc_freq"] = self.experiment_options.osc_freq return metadata diff --git a/qiskit_experiments/library/characterization/zz_ramsey.py b/qiskit_experiments/library/characterization/zz_ramsey.py index 79b2b05a3d..daf546302b 100644 --- a/qiskit_experiments/library/characterization/zz_ramsey.py +++ b/qiskit_experiments/library/characterization/zz_ramsey.py @@ -221,10 +221,6 @@ def _template_circuits( Returns: Circuits for series 0 and 1 """ - metadata = { - "unit": "s", - } - delay = Parameter("delay") timing = BackendTiming(self.backend) @@ -244,7 +240,7 @@ def _template_circuits( # Template circuit for series 0 # Control qubit starting in |0> state, flipping to |1> in middle - circ0 = QuantumCircuit(2, 1, metadata=metadata.copy()) + circ0 = QuantumCircuit(2, 1) circ0.metadata["series"] = "0" circ0.sx(0) @@ -271,7 +267,7 @@ def _template_circuits( # Template circuit for series 1 # Control qubit starting in |1> state, flipping to |0> in middle - circ1 = QuantumCircuit(2, 1, metadata=metadata.copy()) + circ1 = QuantumCircuit(2, 1) circ1.metadata["series"] = "1" circ1.x(1) diff --git a/qiskit_experiments/library/quantum_volume/qv_experiment.py b/qiskit_experiments/library/quantum_volume/qv_experiment.py index 9e8bea0012..fc61770986 100644 --- a/qiskit_experiments/library/quantum_volume/qv_experiment.py +++ b/qiskit_experiments/library/quantum_volume/qv_experiment.py @@ -167,10 +167,8 @@ def circuits(self) -> List[QuantumCircuit]: qv_circ = QuantumVolumeCircuit(depth, depth, seed=rng) qv_circ.measure_active() qv_circ.metadata = { - "experiment_type": self._type, "depth": depth, "trial": trial, - "qubits": self.physical_qubits, "ideal_probabilities": self._get_ideal_data(qv_circ), } circuits.append(qv_circ) diff --git a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py index 54746737ef..0d105ece0f 100644 --- a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py +++ b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py @@ -199,7 +199,6 @@ def circuits(self) -> List[QuantumCircuit]: circ.metadata = { "xval": len(seq), "group": "Clifford", - "physical_qubits": self.physical_qubits, "interleaved": False, } # Build circuits of interleaved sequences @@ -215,7 +214,6 @@ def circuits(self) -> List[QuantumCircuit]: circ.metadata = { "xval": len(seq), # set length of the reference sequence "group": "Clifford", - "physical_qubits": self.physical_qubits, "interleaved": True, } diff --git a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py index e87091fd3f..da03d37bf8 100644 --- a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py +++ b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py @@ -187,7 +187,6 @@ def circuits(self) -> List[QuantumCircuit]: circ.metadata = { "xval": len(seq), "group": "Clifford", - "physical_qubits": self.physical_qubits, } return circuits diff --git a/qiskit_experiments/test/mock_iq_helpers.py b/qiskit_experiments/test/mock_iq_helpers.py index 7f093cde95..a98d222a8b 100644 --- a/qiskit_experiments/test/mock_iq_helpers.py +++ b/qiskit_experiments/test/mock_iq_helpers.py @@ -389,57 +389,34 @@ def _parallel_exp_circ_splitter(self, qc_list: List[QuantumCircuit]): experiment. TypeError: The data type provided doesn't match the expected type (`tuple` or `int`). """ - # exp_idx_map connects an experiment to its circuit in the output. - exp_idx_map = {exp: exp_idx for exp_idx, exp in enumerate(self.exp_list)} - qubit_exp_map = self._create_qubit_exp_map() - exp_circuits_list = [[] for _ in self.exp_list] + qubits_expid_map = {exp.physical_qubits: i for i, exp in enumerate(self.exp_list)} for qc in qc_list: - # Quantum Register to qubit mapping - qubit_indices = {bit: idx for idx, bit in enumerate(qc.qubits)} - # initialize quantum circuit for each experiment for this instance of circuit to fill # with instructions. - for exp_circuit in exp_circuits_list: + for i in range(len(self.exp_list)): # we copy the circuit to ensure that the circuit properties (e.g. calibrations and qubit # frequencies) are the same in the new circuit. - qcirc = qc.copy() - qcirc.data.clear() - qcirc.metadata.clear() - exp_circuit.append(qcirc) + empty_qc = qc.copy_empty_like() + empty_qc.metadata.clear() + exp_circuits_list[i].append(empty_qc) # fixing metadata - for exp_metadata in qc.metadata["composite_metadata"]: - # getting a qubit of one of the experiments that we ran in parallel. The key in the - # metadata is different for different experiments. - qubit_metadata = ( - exp_metadata.get("qubit") - if exp_metadata.get("qubit") is not None - else exp_metadata.get("qubits") - ) - if isinstance(qubit_metadata, tuple): - exp = qubit_exp_map[qubit_metadata[0]] - elif isinstance(qubit_metadata, int): - exp = qubit_exp_map[qubit_metadata] - else: - raise TypeError( - f"The qubit information in the metadata is of type {type(qubit_metadata)}." - f" Supported formats are `tuple` and `int`" - ) - # using the qubit to access the experiment. Then, we go to the last circuit in - # `exp_circuit` of the corresponding experiment, and we overwrite the metadata. - exp_circuits_list[exp_idx_map[exp]][-1].metadata = exp_metadata.copy() + for exp_idx, sub_metadata in zip( + qc.metadata["composite_index"], + qc.metadata["composite_metadata"], + ): + exp_circuits_list[exp_idx][-1].metadata = sub_metadata.copy() + # sorting instructions by qubits indexes and inserting them into a circuit of the relevant # experiment for inst, qarg, carg in qc.data: - exp = qubit_exp_map[qubit_indices[qarg[0]]] - # making a list from the qubits the instruction affects - qubit_indexes = [qubit_indices[qr] for qr in qarg] - # check that the instruction is part of the experiment - if set(qubit_indexes).issubset(set(exp.physical_qubits)): - # appending exp_circuits_list[experiment_index][last_circuit] - exp_circuits_list[exp_idx_map[exp]][-1].append(inst, qarg, carg) + qubit_indices = set(qc.find_bit(qr).index for qr in qarg) + for qubits, exp_idx in qubits_expid_map.items(): + if qubit_indices.issubset(qubits): + exp_circuits_list[exp_idx][-1].append(inst, qarg, carg) + break else: raise QiskitError( "A gate operates on two qubits that don't belong to the same experiment." @@ -453,27 +430,6 @@ def _parallel_exp_circ_splitter(self, qc_list: List[QuantumCircuit]): return exp_circuits_list - def _create_qubit_exp_map(self) -> Dict[int, BaseExperiment]: - """ - Creating a dictionary that connect qubits to their respective experiments. - Returns: - Dict: A dictionary in the form {num: experiment} where num in experiment.physical_qubits - - Raises: - QiskitError: If a qubit belong to two experiments. - """ - qubit_experiment_mapping = {} - for exp in self.exp_list: - for qubit in exp.physical_qubits: - if qubit not in qubit_experiment_mapping: - qubit_experiment_mapping[qubit] = exp - else: - raise QiskitError( - "There are duplications of qubits between parallel experiments" - ) - - return qubit_experiment_mapping - class MockIQDragHelper(MockIQExperimentHelper): """Functions needed for test_drag""" @@ -861,12 +817,12 @@ class MockIQT1Helper(MockIQExperimentHelper): def __init__( self, - t1: List[float] = None, + t1: float = None, iq_cluster_centers: Optional[List[Tuple[IQPoint, IQPoint]]] = None, iq_cluster_width: Optional[List[float]] = None, ): super().__init__(iq_cluster_centers, iq_cluster_width) - self._t1 = t1 or [90e-6] + self._t1 = t1 or 90e-6 def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str, float]]: """Return the probability of being in the excited state.""" @@ -875,13 +831,10 @@ def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str probability_output_dict = {} # extracting information from the circuit. - qubit_idx = circuit.metadata["qubit"] delay = circuit.metadata["xval"] # creating a probability dict. - if qubit_idx >= len(self._t1): - raise QiskitError(f"There is no 'T1' value for qubit index {qubit_idx}.") - probability_output_dict["1"] = np.exp(-delay / self._t1[qubit_idx]) + probability_output_dict["1"] = np.exp(-delay / self._t1) probability_output_dict["0"] = 1 - probability_output_dict["1"] output_dict_list.append(probability_output_dict) diff --git a/releasenotes/notes/upgrade-remove-circuit-metadata-ec7d3c6b08781184.yaml b/releasenotes/notes/upgrade-remove-circuit-metadata-ec7d3c6b08781184.yaml new file mode 100644 index 0000000000..b54e867d17 --- /dev/null +++ b/releasenotes/notes/upgrade-remove-circuit-metadata-ec7d3c6b08781184.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + Removed unnecessary circuit metadata from the builtin experiment classes. + Circuit metadata such as the associated qubit indices and experiment type + are separately stored in the experiment metadata, and never used in the analysis. + Removal of unnecessary circuit metadata compresses the job payload and + thus is expected to benefit scalability. diff --git a/test/library/characterization/test_t1.py b/test/library/characterization/test_t1.py index 7c60e96ddd..6c49a9130d 100644 --- a/test/library/characterization/test_t1.py +++ b/test/library/characterization/test_t1.py @@ -57,13 +57,13 @@ def test_t1_measurement_level_1(self): ns = 1e-9 mu = 1e-6 - t1 = [45 * mu, 45 * mu] + t1 = 45 * mu # delays delays = np.logspace(1, 11, num=23, base=np.exp(1)) delays *= ns delays = np.insert(delays, 0, 0) - delays = np.append(delays, [t1[0] * 3]) + delays = np.append(delays, [t1 * 3]) num_shots = 4096 backend = MockIQBackend( @@ -78,7 +78,7 @@ def test_t1_measurement_level_1(self): exp0 = T1([0], delays) exp0.analysis = T1KerneledAnalysis() - exp0.analysis.set_options(p0={"amp": 1, "tau": t1[0], "base": 0}) + exp0.analysis.set_options(p0={"amp": 1, "tau": t1, "base": 0}) expdata0 = exp0.run( backend=backend, meas_return="avg", @@ -92,7 +92,7 @@ def test_t1_measurement_level_1(self): res = expdata0.analysis_results("T1") self.assertEqual(res.quality, "good") - self.assertAlmostEqual(res.value.n, t1[0], delta=3) + self.assertAlmostEqual(res.value.n, t1, delta=3) self.assertEqual(res.extra["unit"], "s") def test_t1_parallel(self): @@ -129,34 +129,26 @@ def test_t1_parallel_measurement_level_1(self): ns = 1e-9 mu = 1e-6 - t1 = [25 * mu, 20 * mu, 15 * mu] + t1s = [25 * mu, 20 * mu] + qubits = [0, 1] num_shots = 4096 - # qubits - qubit0 = 0 - qubit1 = 1 - - quantum_bit = [qubit0, qubit1] - # Delays delays = np.logspace(1, 11, num=23, base=np.exp(1)) delays *= ns delays = np.insert(delays, 0, 0) - delays = np.append(delays, [t1[0] * 3]) - - # Experiments - exp0 = T1(physical_qubits=[qubit0], delays=delays) - exp0.analysis = T1KerneledAnalysis() - - exp2 = T1(physical_qubits=[qubit1], delays=delays) - exp2.analysis = T1KerneledAnalysis() - - par_exp_list = [exp0, exp2] - par_exp = ParallelExperiment([exp0, exp2], flatten_results=False) - - # Helpers - exp_helper = [ - MockIQT1Helper( + delays = np.append(delays, [t1s[0] * 3]) + + par_exp_list = [] + exp_helpers = [] + for qidx, t1 in zip(qubits, t1s): + # Experiment + exp = T1(physical_qubits=[qidx], delays=delays) + exp.analysis = T1KerneledAnalysis() + par_exp_list.append(exp) + + # Helper + helper = MockIQT1Helper( t1=t1, iq_cluster_centers=[ ((-5.0, -4.0), (-5.0, 4.0)), @@ -165,10 +157,15 @@ def test_t1_parallel_measurement_level_1(self): ], iq_cluster_width=[1.0, 2.0, 1.0], ) - for _ in par_exp_list - ] + exp_helpers.append(helper) + + par_exp = ParallelExperiment( + par_exp_list, + flatten_results=False, + ) par_helper = MockIQParallelExperimentHelper( - exp_list=par_exp_list, exp_helper_list=exp_helper + exp_list=par_exp_list, + exp_helper_list=exp_helpers, ) # Backend @@ -185,10 +182,10 @@ def test_t1_parallel_measurement_level_1(self): self.assertExperimentDone(res) # Checking analysis - for i, qb in enumerate(quantum_bit): + for i, t1 in enumerate(t1s): sub_res = res.child_data(i).analysis_results("T1") self.assertEqual(sub_res.quality, "good") - self.assertAlmostEqual(sub_res.value.n, t1[qb], delta=3) + self.assertAlmostEqual(sub_res.value.n, t1, delta=3) def test_t1_analysis(self): """ @@ -204,12 +201,7 @@ def test_t1_analysis(self): data.add_data( { "counts": {"0": count0, "1": 10000 - count0}, - "metadata": { - "xval": (3 * i + 1) * 1e-9, - "experiment_type": "T1", - "qubit": 0, - "unit": "s", - }, + "metadata": {"xval": (3 * i + 1) * 1e-9}, } ) @@ -230,16 +222,8 @@ def test_t1_metadata(self): self.assertEqual(len(circs), len(delays)) for delay, circ in zip(delays, circs): - xval = circ.metadata.pop("xval") - self.assertAlmostEqual(xval, delay) - self.assertEqual( - circ.metadata, - { - "experiment_type": "T1", - "qubit": 0, - "unit": "s", - }, - ) + # xval is rounded to nealest granularity value. + self.assertAlmostEqual(circ.metadata["xval"], delay) def test_t1_low_quality(self): """ @@ -253,12 +237,7 @@ def test_t1_low_quality(self): data.add_data( { "counts": {"0": 10, "1": 10}, - "metadata": { - "xval": i * 1e-9, - "experiment_type": "T1", - "qubit": 0, - "unit": "s", - }, + "metadata": {"xval": i * 1e-9}, } ) @@ -338,13 +317,5 @@ def test_circuits_with_backend(self): self.assertEqual(len(circs), len(delays)) for delay, circ in zip(delays, circs): - xval = circ.metadata.pop("xval") - self.assertAlmostEqual(xval, delay) - self.assertEqual( - circ.metadata, - { - "experiment_type": "T1", - "qubit": 0, - "unit": "s", - }, - ) + # xval is rounded to nealest granularity value. + self.assertAlmostEqual(circ.metadata["xval"], delay) diff --git a/test/library/characterization/test_tphi.py b/test/library/characterization/test_tphi.py index 619289a088..3581c747c0 100644 --- a/test/library/characterization/test_tphi.py +++ b/test/library/characterization/test_tphi.py @@ -83,11 +83,12 @@ def test_tphi_with_changing_params(self): x_values_t1 = [] x_values_t2 = [] for datum in expdata.data(): - comp_meta = datum["metadata"]["composite_metadata"][0] - if comp_meta["experiment_type"] == "T1": - x_values_t1.append(comp_meta["xval"]) + metadata = datum["metadata"] + xval = metadata["composite_metadata"][0]["xval"] + if metadata["composite_index"][0] == 0: + x_values_t1.append(xval) else: - x_values_t2.append(comp_meta["xval"]) + x_values_t2.append(xval) self.assertListEqual(x_values_t1, delays_t1, "Incorrect delays_t1") self.assertListEqual(x_values_t2, delays_t2, "Incorrect delays_t2") @@ -104,15 +105,14 @@ def test_tphi_with_changing_params(self): # Extract x values from metadata x_values_t1 = [] x_values_t2 = [] - new_freq_t2 = None + new_freq_t2 = expdata.metadata["component_metadata"][1]["osc_freq"] for datum in expdata.data(): - comp_meta = datum["metadata"]["composite_metadata"][0] - if comp_meta["experiment_type"] == "T1": - x_values_t1.append(comp_meta["xval"]) + metadata = datum["metadata"] + xval = metadata["composite_metadata"][0]["xval"] + if metadata["composite_index"][0] == 0: + x_values_t1.append(xval) else: - x_values_t2.append(comp_meta["xval"]) - if new_freq_t2 is None: - new_freq_t2 = comp_meta["osc_freq"] + x_values_t2.append(xval) self.assertListEqual(x_values_t1, new_delays_t1, "Incorrect delays_t1") self.assertListEqual(x_values_t2, new_delays_t2, "Incorrect delays_t2") self.assertEqual(new_freq_t2, new_osc_freq, "Option osc_freq not set correctly")