|
| 1 | +Work with experiment artifacts |
| 2 | +============================== |
| 3 | + |
| 4 | +Problem |
| 5 | +------- |
| 6 | + |
| 7 | +You want to view, add, remove, and save artifacts associated with your :class:`.ExperimentData` instance. |
| 8 | + |
| 9 | +Solution |
| 10 | +-------- |
| 11 | + |
| 12 | +Artifacts are used to store auxiliary data for an experiment that don't fit neatly in the |
| 13 | +:class:`.AnalysisResult` model. Any data that can be serialized, such as fit data, can be added as |
| 14 | +:class:`.ArtifactData` artifacts to :class:`.ExperimentData`. |
| 15 | + |
| 16 | +For example, after an experiment that uses :class:`.CurveAnalysis` is run, its :class:`.ExperimentData` |
| 17 | +object is automatically populated with ``fit_summary`` and ``curve_data`` artifacts. The ``fit_summary`` |
| 18 | +artifact has one or more :class:`.CurveFitResult` objects that contain parameters from the fit. The |
| 19 | +``curve_data`` artifact has a :class:`.ScatterTable` object that contains raw and fitted data in a pandas |
| 20 | +:class:`~pandas:pandas.DataFrame`. |
| 21 | + |
| 22 | +Viewing artifacts |
| 23 | +~~~~~~~~~~~~~~~~~ |
| 24 | + |
| 25 | +Here we run a parallel experiment consisting of two :class:`.T1` experiments in parallel and then view the output |
| 26 | +artifacts as a list of :class:`.ArtifactData` objects accessed by :meth:`.ExperimentData.artifacts`: |
| 27 | + |
| 28 | +.. jupyter-execute:: |
| 29 | + |
| 30 | + from qiskit_ibm_runtime.fake_provider import FakePerth |
| 31 | + from qiskit_aer import AerSimulator |
| 32 | + from qiskit_experiments.library import T1 |
| 33 | + from qiskit_experiments.framework import ParallelExperiment |
| 34 | + import numpy as np |
| 35 | + |
| 36 | + backend = AerSimulator.from_backend(FakePerth()) |
| 37 | + exp1 = T1(physical_qubits=[0], delays=np.arange(1e-6, 6e-4, 5e-5)) |
| 38 | + exp2 = T1(physical_qubits=[1], delays=np.arange(1e-6, 6e-4, 5e-5)) |
| 39 | + data = ParallelExperiment([exp1, exp2], flatten_results=True).run(backend).block_for_results() |
| 40 | + data.artifacts() |
| 41 | + |
| 42 | +Artifacts can be accessed using either the artifact ID, which has to be unique in each |
| 43 | +:class:`.ExperimentData` object, or the artifact name, which does not have to be unique and will return |
| 44 | +all artifacts with the same name: |
| 45 | + |
| 46 | +.. jupyter-execute:: |
| 47 | + |
| 48 | + print("Number of curve_data artifacts:", len(data.artifacts("curve_data"))) |
| 49 | + # retrieve by name and index |
| 50 | + curve_data_id = data.artifacts("curve_data")[0].artifact_id |
| 51 | + # retrieve by ID |
| 52 | + scatter_table = data.artifacts(curve_data_id).data |
| 53 | + print("The first curve_data artifact:\n") |
| 54 | + scatter_table.dataframe |
| 55 | + |
| 56 | +In composite experiments, artifacts behave like analysis results and figures in that if |
| 57 | +``flatten_results`` isn't ``True``, they are accessible in the :meth:`.artifacts` method of each |
| 58 | +:meth:`.child_data`. The artifacts in a large composite experiment with ``flatten_results=True`` can be |
| 59 | +distinguished from each other using the :attr:`~.ArtifactData.experiment` and |
| 60 | +:attr:`~.ArtifactData.device_components` |
| 61 | +attributes. |
| 62 | + |
| 63 | +One useful pattern is to load raw or fitted data from ``curve_data`` for further data manipulation. You |
| 64 | +can work with the dataframe using standard pandas dataframe methods or the built-in |
| 65 | +:class:`.ScatterTable` methods: |
| 66 | + |
| 67 | +.. jupyter-execute:: |
| 68 | + |
| 69 | + import matplotlib.pyplot as plt |
| 70 | + |
| 71 | + exp_type = data.artifacts(curve_data_id).experiment |
| 72 | + component = data.artifacts(curve_data_id).device_components[0] |
| 73 | + |
| 74 | + raw_data = scatter_table.filter(category="raw") |
| 75 | + fitted_data = scatter_table.filter(category="fitted") |
| 76 | + |
| 77 | + # visualize the data |
| 78 | + plt.figure() |
| 79 | + plt.errorbar(raw_data.x, raw_data.y, yerr=raw_data.y_err, capsize=5, label="raw data") |
| 80 | + plt.errorbar(fitted_data.x, fitted_data.y, yerr=fitted_data.y_err, capsize=5, label="fitted data") |
| 81 | + plt.title(f"{exp_type} experiment on {component}") |
| 82 | + plt.xlabel('x') |
| 83 | + plt.ylabel('y') |
| 84 | + plt.legend() |
| 85 | + plt.show() |
| 86 | + |
| 87 | +Adding artifacts |
| 88 | +~~~~~~~~~~~~~~~~ |
| 89 | + |
| 90 | +You can add arbitrary data as an artifact as long as it's serializable with :class:`.ExperimentEncoder`, |
| 91 | +which extends Python's default JSON serialization with support for other data types commonly used with |
| 92 | +Qiskit Experiments. |
| 93 | + |
| 94 | +.. jupyter-execute:: |
| 95 | + |
| 96 | + from qiskit_experiments.framework import ArtifactData |
| 97 | + |
| 98 | + new_artifact = ArtifactData(name="experiment_notes", data={"content": "Testing some new ideas."}) |
| 99 | + data.add_artifacts(new_artifact) |
| 100 | + data.artifacts("experiment_notes") |
| 101 | + |
| 102 | +.. jupyter-execute:: |
| 103 | + |
| 104 | + print(data.artifacts("experiment_notes").data) |
| 105 | + |
| 106 | +Saving and loading artifacts |
| 107 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 108 | + |
| 109 | +.. note:: |
| 110 | + This feature is only for those who have access to the cloud service. You can |
| 111 | + check whether you do by logging into the IBM Quantum interface |
| 112 | + and seeing if you can see the `database <https://quantum.ibm.com/experiments>`__. |
| 113 | + |
| 114 | +Artifacts are saved and loaded to and from the cloud service along with the rest of the |
| 115 | +:class:`ExperimentData` object. Artifacts are stored as ``.zip`` files in the cloud service grouped by |
| 116 | +the artifact name. For example, the composite experiment above will generate two artifact files, ``fit_summary.zip`` and |
| 117 | +``curve_data.zip``. Each of these zipfiles will contain serialized artifact data in JSON format named |
| 118 | +by their unique artifact ID: |
| 119 | + |
| 120 | +.. jupyter-execute:: |
| 121 | + :hide-code: |
| 122 | + |
| 123 | + print("fit_summary.zip") |
| 124 | + print(f"|- {data.artifacts('fit_summary')[0].artifact_id}.json") |
| 125 | + print(f"|- {data.artifacts('fit_summary')[1].artifact_id}.json") |
| 126 | + print("curve_data.zip") |
| 127 | + print(f"|- {data.artifacts('curve_data')[0].artifact_id}.json") |
| 128 | + print(f"|- {data.artifacts('curve_data')[1].artifact_id}.json") |
| 129 | + print("experiment_notes.zip") |
| 130 | + print(f"|- {data.artifacts('experiment_notes').artifact_id}.json") |
| 131 | +
|
| 132 | +Note that for performance reasons, the auto save feature does not apply to artifacts. You must still |
| 133 | +call :meth:`.ExperimentData.save` once the experiment analysis has completed to upload artifacts to the |
| 134 | +cloud service. |
| 135 | + |
| 136 | +Note also though individual artifacts can be deleted, currently artifact files cannot be removed from the |
| 137 | +cloud service. Instead, you can delete all artifacts of that name |
| 138 | +using :meth:`~.delete_artifact` and then call :meth:`.ExperimentData.save`. |
| 139 | +This will save an empty file to the service, and the loaded experiment data will not contain |
| 140 | +these artifacts. |
| 141 | + |
| 142 | +See Also |
| 143 | +-------- |
| 144 | + |
| 145 | +* :ref:`Curve Analysis: Data management with scatter table <data_management_with_scatter_table>` tutorial |
| 146 | +* :class:`.ArtifactData` API documentation |
| 147 | +* :class:`.ScatterTable` API documentation |
| 148 | +* :class:`.CurveFitResult` API documentation |
0 commit comments