def get_processor( meas_level: MeasLevel = MeasLevel.CLASSIFIED, meas_return: str = "avg", normalize: bool = True, ) -> DataProcessor: """Get a DataProcessor that produces a continuous signal given the options. Args: meas_level: The measurement level of the data to process. meas_return: The measurement return (single or avg) of the data to process. normalize: Add a data normalization node to the Kerneled data processor. Returns: An instance of DataProcessor capable of dealing with the given options. Raises: DataProcessorError: if the measurement level is not supported. """ if meas_level == MeasLevel.CLASSIFIED: return DataProcessor("counts", [Probability("1")]) if meas_level == MeasLevel.KERNELED: if meas_return == "single": processor = DataProcessor("memory", [AverageData(axis=1), SVD()]) else: processor = DataProcessor("memory", [SVD()]) if normalize: processor.append(MinMaxNormalize()) return processor raise DataProcessorError(f"Unsupported measurement level {meas_level}.")
def test_to_imag(self): """Test that we can average the data.""" processor = DataProcessor("memory") processor.append(ToImag(scale=1e-3)) exp_data = ExperimentData(FakeExperiment()) exp_data.add_data(self.result_lvl1) new_data, error = processor(exp_data.data(0)) expected_old = { "memory": [ [[1103260.0, -11378508.0], [2959012.0, -16488753.0]], [[442170.0, -19283206.0], [-5279410.0, -15339630.0]], [[3016514.0, -14548009.0], [-3404756.0, -16743348.0]], ], "metadata": { "experiment_type": "fake_test_experiment" }, "job_id": "job-123", "meas_level": 1, "shots": 3, } expected_new = np.array([ [-11378.508, -16488.753], [-19283.206000000002, -15339.630000000001], [-14548.009, -16743.348], ]) self.assertEqual(exp_data.data(0), expected_old) self.assertTrue(np.allclose(new_data, expected_new)) self.assertIsNone(error) # Test that we can call with history. new_data, error, history = processor.call_with_history( exp_data.data(0)) self.assertEqual(exp_data.data(0), expected_old) self.assertTrue(np.allclose(new_data, expected_new)) self.assertEqual(history[0][0], "ToImag") self.assertTrue(np.allclose(history[0][1], expected_new)) # Test to imaginary on more than one datum new_data, error = processor(exp_data.data()) expected_new = np.array([ [[-11378.508, -16488.753], [-19283.206, -15339.630], [-14548.009, -16743.348]], [[-16630.257, -13752.518], [-16031.913, -15840.465], [-14955.998, -14538.923]], ]) self.assertTrue(np.allclose(new_data, expected_new))
def test_validation(self): """Test the validation mechanism.""" for validate, error in [(False, AttributeError), (True, DataProcessorError)]: processor = DataProcessor("counts") processor.append(Probability("00", validate=validate)) with self.assertRaises(error): processor({"counts": [0, 1, 2]})
def test_populations(self): """Test that counts are properly converted to a population.""" processor = DataProcessor("counts") processor.append(Probability("00")) new_data = processor(self.exp_data_lvl2.data[0]) self.assertEqual(new_data[0], 0.4) self.assertEqual(new_data[1], 0.4 * (1 - 0.4) / 10)
def test_populations(self): """Test that counts are properly converted to a population.""" processor = DataProcessor("counts") processor.append(Probability("00")) # Test on a single datum. new_data, error = processor(self.exp_data_lvl2.data(0)) self.assertEqual(new_data, 0.4) self.assertEqual(error, np.sqrt(0.4 * (1 - 0.4) / 10)) # Test on all the data new_data, error = processor(self.exp_data_lvl2.data()) self.assertTrue(np.allclose(new_data, np.array([0.4, 0.2])))
def test_populations(self): """Test that counts are properly converted to a population.""" processor = DataProcessor("counts") processor.append(Probability("00", alpha_prior=1.0)) # Test on a single datum. new_data = processor(self.exp_data_lvl2.data(0)) self.assertAlmostEqual(float(unp.nominal_values(new_data)), 0.41666667) self.assertAlmostEqual(float(unp.std_devs(new_data)), 0.13673544235706114) # Test on all the data new_data = processor(self.exp_data_lvl2.data()) np.testing.assert_array_almost_equal( unp.nominal_values(new_data), np.array([0.41666667, 0.25]), )
def test_to_imag(self): """Test that we can average the data.""" processor = DataProcessor("memory") processor.append(ToImag(scale=1e-3)) exp_data = ExperimentData(FakeExperiment()) exp_data.add_data(self.result_lvl1) new_data = processor(exp_data.data[0]) expected_old = { "memory": [ [[1103260.0, -11378508.0], [2959012.0, -16488753.0]], [[442170.0, -19283206.0], [-5279410.0, -15339630.0]], [[3016514.0, -14548009.0], [-3404756.0, -16743348.0]], ], "metadata": { "experiment_type": "fake_test_experiment", "x_values": 0.0 }, } expected_new = np.array([ [-11378.508, -16488.753], [-19283.206000000002, -15339.630000000001], [-14548.009, -16743.348], ]) self.assertEqual(exp_data.data[0], expected_old) self.assertTrue(np.allclose(new_data, expected_new)) # Test that we can call with history. new_data, history = processor.call_with_history(exp_data.data[0]) self.assertEqual(exp_data.data[0], expected_old) self.assertTrue(np.allclose(new_data, expected_new)) self.assertEqual(history[0][0], "ToImag") self.assertTrue(np.allclose(history[0][1], expected_new))
def get_processor( experiment_data: ExperimentData, analysis_options: Options, index: int = -1, ) -> DataProcessor: """Get a DataProcessor that produces a continuous signal given the options. Args: experiment_data: The experiment data that holds all the data and metadata needed to determine the data processor to use to process the data for analysis. analysis_options: The analysis options with which to analyze the data. The options that are relevant for the configuration of a data processor are: - normalization (bool): A boolean to specify if the data should be normalized to the interval [0, 1]. The default is True. This option is only relevant if kerneled data is used. - dimensionality_reduction: An optional string or instance of :class:`ProjectorType` to represent the dimensionality reduction node for Kerneled data. For the supported nodes, see :class:`ProjectorType`. Typically, these nodes convert complex IQ data to real data, for example by performing a singular-value decomposition. This argument is only needed for Kerneled data (i.e. level 1) and can thus be ignored if Classified data (the default) is used. - outcome (string): The measurement outcome that will be passed to a Probability node. The default value is a string of 1's where the length of the string is the number of qubits, e.g. '111' for three qubits. index: The index of the job for which to get a data processor. The default value is -1. Returns: An instance of DataProcessor capable of processing the data for the corresponding job. Notes: The `num_qubits` argument is extracted from the `experiment_data` metadata and is used to determine the default `outcome` to extract from classified data if it was not given in the analysis options. Raises: DataProcessorError: if the measurement level is not supported. DataProcessorError: if the wrong dimensionality reduction for kerneled data is specified. """ run_options = experiment_data.metadata["job_metadata"][index].get( "run_options", {}) meas_level = run_options.get("meas_level", MeasLevel.CLASSIFIED) meas_return = run_options.get("meas_return", MeasReturnType.AVERAGE) normalize = analysis_options.get("normalization", True) dimensionality_reduction = analysis_options.get("dimensionality_reduction", ProjectorType.SVD) if meas_level == MeasLevel.CLASSIFIED: num_qubits = experiment_data.metadata.get("num_qubits", 1) outcome = analysis_options.get("outcome", "1" * num_qubits) return DataProcessor("counts", [nodes.Probability(outcome)]) if meas_level == MeasLevel.KERNELED: try: if isinstance(dimensionality_reduction, ProjectorType): projector_name = dimensionality_reduction.name else: projector_name = dimensionality_reduction projector = ProjectorType[projector_name].value except KeyError as error: raise DataProcessorError( f"Invalid dimensionality reduction: {dimensionality_reduction}." ) from error if meas_return == "single": processor = DataProcessor("memory", [nodes.AverageData(axis=1), projector()]) else: processor = DataProcessor("memory", [projector()]) if normalize: processor.append(nodes.MinMaxNormalize()) return processor raise DataProcessorError(f"Unsupported measurement level {meas_level}.")