diff --git a/.github/workflows/cron-staging.yml b/.github/workflows/cron-staging.yml index dec1ffc210..580fc00c73 100644 --- a/.github/workflows/cron-staging.yml +++ b/.github/workflows/cron-staging.yml @@ -44,4 +44,35 @@ jobs: if: runner.os == 'macOS' env: TEST_TIMEOUT: 120 - OMP_NUM_THREADS: 1 \ No newline at end of file + OMP_NUM_THREADS: 1 + docs: + name: docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Pip cache + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-docs-${{ hashFiles('setup.py','requirements.txt','requirements-extras.txt','requirements-dev.txt','constraints.txt') }} + - name: Install Deps + run: | + python -m pip install -U tox + sudo apt-get install -y pandoc graphviz + - name: Build Docs + run: tox -edocs-terra-main + - name: Compress Artifacts + run: | + mkdir artifacts + tar -Jcvf html_docs.tar.xz docs/_build/html + mv html_docs.tar.xz artifacts/. + - uses: actions/upload-artifact@v3 + with: + name: html_docs + path: artifacts \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f4b45191f..e54b267a0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,21 +104,17 @@ https://stestr.readthedocs.io/en/stable/MANUAL.html#test-selection If you want to run a single test module, test class, or individual test method you can do this faster with the `-n`/`--no-discover` option. For example, to run a module: ``` -tox -- -n test.python.test_examples +tox -epy310 -- -n test.framework.test_composite ``` -Or to run the same module by path: -``` -tox -- -n test/python/test_examples.py -``` To run a class: - ``` -tox -- -n test.python.test_examples.TestPythonExamples +tox -epy310 -- -n test.framework.test_composite.TestCompositeExperimentData ``` + To run a method: ``` -tox -- -n test.python.test_examples.TestPythonExamples.test_all_examples +tox -epy310 -- -n test.framework.test_composite.TestCompositeExperimentData.test_composite_save_load ``` Note that tests will fail automatically if they do not finish execution within 60 seconds. diff --git a/docs/_ext/custom_styles/formatter.py b/docs/_ext/custom_styles/formatter.py index a218f9476e..5a3b2d45af 100644 --- a/docs/_ext/custom_styles/formatter.py +++ b/docs/_ext/custom_styles/formatter.py @@ -38,7 +38,7 @@ def format_header(self, lines: List[str]) -> List[str]: def format_overview(self, lines: List[str]) -> List[str]: """Format overview section.""" format_lines = [ - "" + "", ".. rubric:: Overview", "", ] @@ -167,7 +167,7 @@ def format_analysis_opts(self, lines: List[str]) -> List[str]: format_lines = [ ".. rubric:: Analysis options", "", - "These are the keyword arguments of :meth:`run` method.", + "These are the keyword arguments of the :meth:`run` method.", "", ] for line in _write_options(lines, self.indent): diff --git a/docs/_ext/custom_styles/option_parser.py b/docs/_ext/custom_styles/option_parser.py index bb11b5645b..da9b1e4c70 100644 --- a/docs/_ext/custom_styles/option_parser.py +++ b/docs/_ext/custom_styles/option_parser.py @@ -26,7 +26,7 @@ from sphinx.ext.napoleon import GoogleDocstring -_parameter_doc_regex = re.compile(r'(.+?)\(\s*(.*[^\s]+)\s*\):(.*[^\s]+)') +_parameter_doc_regex = re.compile(r"(.+?)\(\s*(.*[^\s]+)\s*\):(.*[^\s]+)") class QiskitExperimentsOptionsDocstring(GoogleDocstring): @@ -201,8 +201,11 @@ def _value_repr(value: Any) -> str: return f"{{{dict_repr}}}" if value.__class__.__module__ == "builtins": return f":obj:`{value}`" - if value.__class__.__module__.startswith("qiskit"): + if value.__class__.__module__ and value.__class__.__module__.startswith("qiskit"): return f"Instance of :class:`.{value.__class__.__name__}`" + # for singleton gates that don't have directly accessible module names + if hasattr(value, "base_class") and value.base_class.__module__.startswith("qiskit"): + return f"Instance of :class:`.{value.base_class.__name__}`" if callable(value): return f"Callable :func:`{value.__name__}`" if isinstance(value, np.ndarray): diff --git a/docs/_templates/autosummary/analysis.rst b/docs/_templates/autosummary/analysis.rst index 222df215af..d8811523ac 100644 --- a/docs/_templates/autosummary/analysis.rst +++ b/docs/_templates/autosummary/analysis.rst @@ -17,11 +17,9 @@ .. rubric:: Attributes - .. autosummary:: - :toctree: ../stubs/ {% for item in all_attributes %} {%- if not item.startswith('_') %} - {{ name }}.{{ item }} + .. autoattribute:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% endif %} @@ -32,16 +30,14 @@ .. rubric:: Methods - .. autosummary:: - :toctree: ../stubs/ {% for item in all_methods %} {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% for item in inherited_members %} {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst index f6e8a53f58..fd25d5706e 100644 --- a/docs/_templates/autosummary/class.rst +++ b/docs/_templates/autosummary/class.rst @@ -12,58 +12,37 @@ :no-inherited-members: :no-special-members: - {% block attributes_summary %} - {% if attributes %} +{% block attributes_summary %} - {# This counter lets us only render the heading if there's at least - one valid entry. #} - {% set count = namespace(value=0) %} + {% set wanted_attributes = [] %} + {% for item in attributes%} + {%- if not item.startswith('_') %} + {% set _ = wanted_attributes.append(item)%} + {%- endif -%} + {%- endfor %} - {% for item in attributes %} - {% if not item.startswith('_') %} - {% set count.value = count.value + 1 %} - {% if count.value == 1 %} + {% if wanted_attributes %} .. rubric:: Attributes - - .. autosummary:: - :toctree: ../stubs/ - {% endif %} - - {{ name }}.{{ item }} - {% endif %} - {% endfor %} + {% for item in wanted_attributes %} + .. autoattribute:: {{ name }}.{{ item }} + {%- endfor %} {% endif %} - {% endblock %} +{% endblock %} - {% block methods_summary %} - {% if methods %} +{% block methods_summary %} - {% set count = namespace(value=0) %} + {% set wanted_methods = [] %} {% for item in all_methods %} - {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {% set count.value = count.value + 1 %} - {% if count.value == 1 %} - .. rubric:: Methods - - .. autosummary:: - :toctree: ../stubs/ - {% endif %} - {{ name }}.{{ item }} + {% set _ = wanted_methods.append(item)%} {%- endif -%} {%- endfor %} - {% for item in inherited_members %} - {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {% set count.value = count.value + 1 %} - {% if count.value == 1 %} - .. rubric:: Methods - .. autosummary:: - :toctree: ../stubs/ - {% endif %} - {{ name }}.{{ item }} - {%- endif -%} + {% if wanted_methods%} + .. rubric:: Methods + {% for item in wanted_methods %} + .. automethod:: {{ name }}.{{ item }} {%- endfor %} {% endif %} - {% endblock %} +{% endblock %} \ No newline at end of file diff --git a/docs/_templates/autosummary/class_no_inherited_members.rst b/docs/_templates/autosummary/class_no_inherited_members.rst new file mode 100644 index 0000000000..9e3b9339c9 --- /dev/null +++ b/docs/_templates/autosummary/class_no_inherited_members.rst @@ -0,0 +1,52 @@ +{# This is identical to class.rst, except for the filtering of the inherited_members. -#} + +{% if referencefile %} +.. include:: {{ referencefile }} +{% endif %} + +{{ objname }} +{{ underline }} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :no-members: + :no-inherited-members: + :no-special-members: + +{% block attributes_summary %} + + {% set wanted_attributes = [] %} + {% for item in attributes%} + {%- if not item.startswith('_') %} + {% set _ = wanted_attributes.append(item)%} + {%- endif -%} + {%- endfor %} + + {% if wanted_attributes%} + .. rubric:: Attributes + {% for item in wanted_attributes %} + .. autoattribute:: {{ name }}.{{ item }} + {%- endfor %} + {% endif %} +{% endblock %} + +{% block methods_summary %} + + {% set wanted_methods = [] %} + {% for item in all_methods %} + {%- if item not in inherited_members %} + {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} + {% set _ = wanted_methods.append(item)%} + {%- endif -%} + {%- endif -%} + {%- endfor %} + + {% if wanted_methods %} + .. rubric:: Methods + {% for item in wanted_methods %} + .. automethod:: {{ name }}.{{ item }} + {%- endfor %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/docs/_templates/autosummary/drawer.rst b/docs/_templates/autosummary/drawer.rst index a17d57f6da..03501a5cd0 100644 --- a/docs/_templates/autosummary/drawer.rst +++ b/docs/_templates/autosummary/drawer.rst @@ -17,11 +17,9 @@ .. rubric:: Attributes - .. autosummary:: - :toctree: ../stubs/ {% for item in all_attributes %} {%- if not item.startswith('_') %} - {{ name }}.{{ item }} + .. autoattribute:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% endif %} @@ -32,16 +30,14 @@ .. rubric:: Methods - .. autosummary:: - :toctree: ../stubs/ {% for item in all_methods %} {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% for item in inherited_members %} {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} diff --git a/docs/_templates/autosummary/experiment.rst b/docs/_templates/autosummary/experiment.rst index 01800ea10b..0de1a1c3c3 100644 --- a/docs/_templates/autosummary/experiment.rst +++ b/docs/_templates/autosummary/experiment.rst @@ -17,11 +17,9 @@ .. rubric:: Attributes - .. autosummary:: - :toctree: ../stubs/ {% for item in all_attributes %} {%- if not item.startswith('_') %} - {{ name }}.{{ item }} + .. autoattribute:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% endif %} @@ -32,16 +30,14 @@ .. rubric:: Methods - .. autosummary:: - :toctree: ../stubs/ {% for item in all_methods %} {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% for item in inherited_members %} {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} diff --git a/docs/_templates/autosummary/plotter.rst b/docs/_templates/autosummary/plotter.rst index 83e19addbe..0b5cdeefaa 100644 --- a/docs/_templates/autosummary/plotter.rst +++ b/docs/_templates/autosummary/plotter.rst @@ -17,11 +17,9 @@ .. rubric:: Attributes - .. autosummary:: - :toctree: ../stubs/ {% for item in all_attributes %} {%- if not item.startswith('_') %} - {{ name }}.{{ item }} + .. autoattribute:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% endif %} @@ -32,17 +30,14 @@ .. rubric:: Methods - .. autosummary:: - :toctree: ../stubs/ - {% for item in all_methods %} {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% for item in inherited_members %} {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %} - {{ name }}.{{ item }} + .. automethod:: {{ name }}.{{ item }} {%- endif -%} {%- endfor %} diff --git a/docs/conf.py b/docs/conf.py index 527c1b7da0..b2165afb6b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -174,6 +174,7 @@ "qiskit_ibm_provider": ("https://qiskit.org/ecosystem/ibm-provider/", None), "qiskit_aer": ("https://qiskit.org/ecosystem/aer", None), "qiskit_dynamics": ("https://qiskit.org/documentation/dynamics", None), + "qiskit_ibm_runtime": ("https://qiskit.org/ecosystem/ibm-runtime/", None), } diff --git a/docs/howtos/cloud_service.rst b/docs/howtos/cloud_service.rst index 5f06517eff..fb3aebad53 100644 --- a/docs/howtos/cloud_service.rst +++ b/docs/howtos/cloud_service.rst @@ -189,11 +189,4 @@ Web interface You can also view experiment results as well as change the tags and share level at the `IBM Quantum Experiments pane `__ -on the cloud. The documentation below explains how to view, search, and share experiment -data entries. - -See also --------- - -* `Experiments web interface documentation `__ - +on the cloud. diff --git a/docs/howtos/runtime_sessions.rst b/docs/howtos/runtime_sessions.rst new file mode 100644 index 0000000000..9b07705e76 --- /dev/null +++ b/docs/howtos/runtime_sessions.rst @@ -0,0 +1,41 @@ +Use Experiments with Runtime sessions +===================================== + +Problem +------- + +You want to run experiments in a `Runtime session +`_ so that jobs can run in close temporal proximity. + +Solution +-------- + +Use the :class:`~qiskit_ibm_provider.IBMBackend` object in ``qiskit-ibm-provider``, which supports sessions. + +In this example, we will set the ``max_circuits`` property to an artificially low value so that the experiment will be +split into multiple jobs that run sequentially in a single session. When running real experiments with a +large number of circuits that can't fit in a single job, it may be helpful to follow this usage pattern: + +.. jupyter-input:: + + from qiskit_ibm_provider import IBMProvider + from qiskit_experiments.library.tomography import ProcessTomography + from qiskit import QuantumCircuit + + provider = IBMProvider() + backend = provider.get_backend("ibm_nairobi") + qc = QuantumCircuit(1) + qc.x(0) + + with backend.open_session() as session: + exp = ProcessTomography(qc) + exp.set_experiment_options(max_circuits=3) + exp_data = exp.run(backend) + exp_data.block_for_results() + # Calling cancel because session.close() is not available for qiskit-ibm-provider<=0.7.2. + # It is safe to call cancel since block_for_results() ensures there are no outstanding jobs + # still running that would be canceled. + session.cancel() + +Note that runtime primitives are not currently supported natively in Qiskit Experiments, so +the ``backend.run()`` path is required to run experiments. diff --git a/docs/manuals/characterization/tphi.rst b/docs/manuals/characterization/tphi.rst index d3ac38078b..3e0f25503e 100644 --- a/docs/manuals/characterization/tphi.rst +++ b/docs/manuals/characterization/tphi.rst @@ -54,11 +54,11 @@ relaxation time estimate. We can see that the component experiments of the batch .. jupyter-execute:: exp = Tphi(physical_qubits=(0,), delays_t1=delays_t1, delays_t2=delays_t2, num_echoes=1) - exp.component_experiment(0).circuits()[-1].draw("mpl") + exp.component_experiment(0).circuits()[-1].draw(output="mpl", style="iqp") .. jupyter-execute:: - exp.component_experiment(1).circuits()[-1].draw("mpl") + exp.component_experiment(1).circuits()[-1].draw(output="mpl", style="iqp") Run the experiment and print results: @@ -94,7 +94,7 @@ experiment: t2type="ramsey", osc_freq=1e5) - exp.component_experiment(1).circuits()[-1].draw("mpl") + exp.component_experiment(1).circuits()[-1].draw(output="mpl", style="iqp") Run and display results: diff --git a/docs/manuals/measurement/readout_mitigation.rst b/docs/manuals/measurement/readout_mitigation.rst index 2b69142d27..0fed52b920 100644 --- a/docs/manuals/measurement/readout_mitigation.rst +++ b/docs/manuals/measurement/readout_mitigation.rst @@ -180,4 +180,4 @@ See also * API documentation: :mod:`~qiskit_experiments.library.characterization.LocalReadoutError`, :mod:`~qiskit_experiments.library.characterization.CorrelatedReadoutError` -* Qiskit Textbook: `Measurement Error Mitigation `__ +* Qiskit Textbook: `Measurement Error Mitigation `__ diff --git a/docs/manuals/verification/quantum_volume.rst b/docs/manuals/verification/quantum_volume.rst index a4ea74e771..2b01b515fa 100644 --- a/docs/manuals/verification/quantum_volume.rst +++ b/docs/manuals/verification/quantum_volume.rst @@ -8,7 +8,7 @@ that the computer successfully implements. Quantum computing systems with high-fidelity operations, high connectivity, large calibrated gate sets, and circuit rewriting toolchains are expected to have higher quantum volumes. See the `Qiskit -Textbook `__ +Textbook `__ for an explanation on the QV method, which is described in Refs. [1]_ [2]_. The Quantum Volume is determined by the largest successful circuit depth @@ -20,8 +20,8 @@ a random permutation on the :math:`d` qubit. Then these circuits run on the quantum backend and on an ideal simulator (either :class:`qiskit_aer.AerSimulator` or :class:`qiskit.quantum_info.Statevector`). -A depth :math:`d` QV circuit is successful if it has ‘mean heavy-output -probability’ > 2/3 with confidence level > 0.977 (corresponding to +A depth :math:`d` QV circuit is successful if it has `mean heavy-output +probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. .. note:: @@ -68,7 +68,7 @@ more trials may reduce the error bars to allow passing the threshold. The analysis results of the QV Experiment are: -- The mean heavy output probabilities (HOP) and standard deviation +- The mean heavy-output probabilities (HOP) and standard deviation - The calculated quantum volume, which will be None if the experiment does not pass the threshold @@ -190,5 +190,5 @@ See also -------- * API documentation: :mod:`~qiskit_experiments.library.quantum_volume` -* Qiskit Textbook: `Measuring Quantum Volume `__ +* Qiskit Textbook: `Measuring Quantum Volume `__ diff --git a/docs/manuals/verification/randomized_benchmarking.rst b/docs/manuals/verification/randomized_benchmarking.rst index 16fbd07f20..111e3f5ddc 100644 --- a/docs/manuals/verification/randomized_benchmarking.rst +++ b/docs/manuals/verification/randomized_benchmarking.rst @@ -8,7 +8,7 @@ identity. After running the circuits, the number of shots resulting in an error output different from the ground state) are counted, and from this data one can infer error estimates for the quantum device, by calculating the Error Per Clifford. See the `Qiskit Textbook -`__ for an +`__ for an explanation on the RB method, which is based on Refs. [1]_ [2]_. .. jupyter-execute:: @@ -215,20 +215,20 @@ The default RB circuit output shows Clifford blocks: # Run an RB experiment on qubit 0 exp = StandardRB(physical_qubits=(0,), lengths=[2], num_samples=1, seed=seed) c = exp.circuits()[0] - c.draw("mpl") + c.draw(output="mpl", style="iqp") You can decompose the circuit into underlying gates: .. jupyter-execute:: - c.decompose().draw("mpl") + c.decompose().draw(output="mpl", style="iqp") And see the transpiled circuit using the basis gate set of the backend: .. jupyter-execute:: from qiskit import transpile - transpile(c, backend, **vars(exp.transpile_options)).draw("mpl", idle_wires=False) + transpile(c, backend, **vars(exp.transpile_options)).draw(output="mpl", style="iqp", idle_wires=False) .. note:: In 0.5.0, the default value of ``optimization_level`` in ``transpile_options`` changed @@ -309,4 +309,4 @@ See also -------- * API documentation: :mod:`~qiskit_experiments.library.randomized_benchmarking` -* Qiskit Textbook: `Randomized Benchmarking `__ +* Qiskit Textbook: `Randomized Benchmarking `__ diff --git a/docs/tutorials/calibrations.rst b/docs/tutorials/calibrations.rst index de1cff5e6a..53aef3d3d9 100644 --- a/docs/tutorials/calibrations.rst +++ b/docs/tutorials/calibrations.rst @@ -172,7 +172,7 @@ Instantiate the experiment and draw the first circuit in the sweep: .. jupyter-execute:: circuit = spec.circuits()[0] - circuit.draw(output="mpl") + circuit.draw(output="mpl", style="iqp") We can also visualize the pulse schedule for the circuit: @@ -225,7 +225,7 @@ with different amplitudes. .. jupyter-execute:: - rabi.circuits()[0].draw("mpl") + rabi.circuits()[0].draw(output="mpl", style="iqp") After the experiment completes the value of the amplitudes in the calibrations will automatically be updated. This behaviour can be controlled using the ``auto_update`` @@ -316,7 +316,7 @@ negative amplitude. from qiskit_experiments.library import RoughDragCal cal_drag = RoughDragCal([qubit], cals, backend=backend, betas=np.linspace(-20, 20, 25)) cal_drag.set_experiment_options(reps=[3, 5, 7]) - cal_drag.circuits()[5].draw(output='mpl') + cal_drag.circuits()[5].draw(output="mpl", style="iqp") .. jupyter-execute:: @@ -393,7 +393,7 @@ over/under rotations is the highest. overamp_exp = FineXAmplitude((qubit,), backend=backend) overamp_exp.set_transpile_options(inst_map=inst_map) - overamp_exp.circuits()[4].draw(output='mpl') + overamp_exp.circuits()[4].draw(output="mpl", style="iqp") .. jupyter-execute:: @@ -460,7 +460,7 @@ error which we want to correct. from qiskit_experiments.library import FineSXAmplitudeCal amp_cal = FineSXAmplitudeCal((qubit,), cals, backend=backend, schedule_name="sx") - amp_cal.circuits()[4].draw(output="mpl") + amp_cal.circuits()[4].draw(output="mpl", style="iqp") Let's run the calibration experiment: @@ -491,7 +491,7 @@ See also -------- * API documentation: :mod:`~qiskit_experiments.calibration_management` and :mod:`~qiskit_experiments.library.calibration` -* Qiskit Textbook: `Calibrating Qubits with Qiskit Pulse `__ +* Qiskit Textbook: `Calibrating Qubits with Qiskit Pulse `__ diff --git a/docs/tutorials/custom_experiment.rst b/docs/tutorials/custom_experiment.rst index 1173cba12a..c1ccf36546 100644 --- a/docs/tutorials/custom_experiment.rst +++ b/docs/tutorials/custom_experiment.rst @@ -573,7 +573,7 @@ Let's use a GHZ circuit as the input: for i in range(1, nq): qc.cx(i-1, i) - qc.draw("mpl") + qc.draw(output="mpl", style="iqp") Check that the experiment is appending a random Pauli and measurements as expected: @@ -586,7 +586,7 @@ Check that the experiment is appending a random Pauli and measurements as expect # Run ideal randomized meas experiment exp = RandomizedMeasurement(qc, num_samples=num_samples) - exp.circuits()[0].draw("mpl") + exp.circuits()[0].draw(output="mpl", style="iqp") We now run the experiment with a GHZ circuit on an ideal backend, which produces nearly perfect symmetrical results between :math:`|0000\rangle` and :math:`|1111\rangle`: @@ -640,4 +640,4 @@ unaffected by the added randomized measurements, which use its own classical reg qc.cx(i-1, i) exp = RandomizedMeasurement(qc, num_samples=num_samples) - exp.circuits()[0].draw("mpl") \ No newline at end of file + exp.circuits()[0].draw(output="mpl", style="iqp") \ No newline at end of file diff --git a/docs/tutorials/getting_started.rst b/docs/tutorials/getting_started.rst index d82b153fd7..339b81d3b5 100644 --- a/docs/tutorials/getting_started.rst +++ b/docs/tutorials/getting_started.rst @@ -101,11 +101,11 @@ first and last circuits for our :math:`T_1` experiment: .. jupyter-execute:: print(delays) - exp.circuits()[0].draw(output='mpl') + exp.circuits()[0].draw(output="mpl", style="iqp") .. jupyter-execute:: - exp.circuits()[-1].draw(output='mpl') + exp.circuits()[-1].draw(output="mpl", style="iqp") As expected, the delay block spans the full range of time values that we specified. @@ -331,11 +331,11 @@ child experiments can be accessed via the .. jupyter-execute:: - parallel_exp.component_experiment(0).circuits()[0].draw(output='mpl') + parallel_exp.component_experiment(0).circuits()[0].draw(output="mpl", style="iqp") .. jupyter-execute:: - parallel_exp.component_experiment(1).circuits()[0].draw(output='mpl') + parallel_exp.component_experiment(1).circuits()[0].draw(output="mpl", style="iqp") Similarly, the child analyses can be accessed via :meth:`.CompositeAnalysis.component_analysis` or via the analysis of the child experiment class: @@ -353,7 +353,7 @@ circuits are composed together and then reassigned virtual qubit indices: .. jupyter-execute:: - parallel_exp.circuits()[0].draw(output='mpl') + parallel_exp.circuits()[0].draw(output="mpl", style="iqp") During experiment transpilation, a mapping is performed to place these circuits on the physical layout. We can see its effects by looking at the transpiled @@ -363,7 +363,7 @@ and the :class:`.StandardRB` experiment's gates are on physical qubits 3 and 1. .. jupyter-execute:: - parallel_exp._transpiled_circuits()[0].draw(output='mpl') + parallel_exp._transpiled_circuits()[0].draw(output="mpl", style="iqp") :class:`.ParallelExperiment` and :class:`.BatchExperiment` classes can also be nested arbitrarily to make complex composite experiments. diff --git a/qiskit_experiments/data_processing/data_processor.py b/qiskit_experiments/data_processing/data_processor.py index 80a639b123..6be3761650 100644 --- a/qiskit_experiments/data_processing/data_processor.py +++ b/qiskit_experiments/data_processing/data_processor.py @@ -48,14 +48,14 @@ class DataProcessor: A DataProcessor defines a sequence of operations to perform on experimental data. Calling an instance of DataProcessor applies this sequence on the input argument. A DataProcessor is created with a list of DataAction instances. Each DataAction - applies its _process method on the data and returns the processed data. The nodes + applies its ``_process`` method on the data and returns the processed data. The nodes in the DataProcessor may also perform data validation and some minor formatting. The output of one data action serves as input for the next data action. - DataProcessor.__call__(datum) usually takes in an entry from the data property of + ``DataProcessor.__call__(datum)`` usually takes in an entry from the data property of an ExperimentData object (i.e. a dict containing metadata and memory keys and possibly counts, like the Result.data property) and produces the formatted data. - DataProcessor.__call__(datum) extracts the data from the given datum under - DataProcessor._input_key (which is specified at initialization) of the given datum. + ``DataProcessor.__call__(datum)`` extracts the data from the given datum under + ``DataProcessor._input_key`` (which is specified at initialization) of the given datum. """ def __init__( diff --git a/qiskit_experiments/framework/base_experiment.py b/qiskit_experiments/framework/base_experiment.py index 882fc20a3f..41240df41c 100644 --- a/qiskit_experiments/framework/base_experiment.py +++ b/qiskit_experiments/framework/base_experiment.py @@ -53,7 +53,7 @@ def __init__( QiskitError: If qubits contains duplicates. """ # Experiment identification metadata - self._type = experiment_type if experiment_type else type(self).__name__ + self.experiment_type = experiment_type # Circuit parameters self._num_qubits = len(physical_qubits) @@ -90,6 +90,14 @@ def experiment_type(self) -> str: """Return experiment type.""" return self._type + @experiment_type.setter + def experiment_type(self, exp_type: str) -> None: + """Set the type for the experiment.""" + if exp_type is None: + self._type = type(self).__name__ + else: + self._type = exp_type + @property def physical_qubits(self) -> Tuple[int, ...]: """Return the device qubits for the experiment.""" @@ -293,8 +301,8 @@ def job_info(self, backend: Backend = None): Args: backend: Optional, the backend for which to get job distribution - information. If not specified, the experiment must already have a - set backend. + information. If not specified, the experiment must already have a + set backend. Returns: dict: A dictionary containing information about job distribution. diff --git a/qiskit_experiments/framework/composite/batch_experiment.py b/qiskit_experiments/framework/composite/batch_experiment.py index c7f66cdca3..7ef50ac2a0 100644 --- a/qiskit_experiments/framework/composite/batch_experiment.py +++ b/qiskit_experiments/framework/composite/batch_experiment.py @@ -49,6 +49,7 @@ def __init__( backend: Optional[Backend] = None, flatten_results: bool = None, analysis: Optional[CompositeAnalysis] = None, + experiment_type: Optional[str] = None, ): """Initialize a batch experiment. @@ -86,7 +87,12 @@ def __init__( logical_qubit += 1 qubits = tuple(self._qubit_map.keys()) super().__init__( - experiments, qubits, backend=backend, analysis=analysis, flatten_results=flatten_results + experiments, + qubits, + backend=backend, + analysis=analysis, + flatten_results=flatten_results, + experiment_type=experiment_type, ) def circuits(self): diff --git a/qiskit_experiments/framework/composite/parallel_experiment.py b/qiskit_experiments/framework/composite/parallel_experiment.py index 91031051ff..24f901a292 100644 --- a/qiskit_experiments/framework/composite/parallel_experiment.py +++ b/qiskit_experiments/framework/composite/parallel_experiment.py @@ -49,6 +49,7 @@ def __init__( backend: Optional[Backend] = None, flatten_results: bool = None, analysis: Optional[CompositeAnalysis] = None, + experiment_type: Optional[str] = None, ): """Initialize the analysis object. @@ -79,7 +80,12 @@ def __init__( for exp in experiments: qubits += exp.physical_qubits super().__init__( - experiments, qubits, backend=backend, analysis=analysis, flatten_results=flatten_results + experiments, + qubits, + backend=backend, + analysis=analysis, + flatten_results=flatten_results, + experiment_type=experiment_type, ) def circuits(self): diff --git a/qiskit_experiments/library/calibration/fine_drag_cal.py b/qiskit_experiments/library/calibration/fine_drag_cal.py index 7c52e2d6bd..c79bad7562 100644 --- a/qiskit_experiments/library/calibration/fine_drag_cal.py +++ b/qiskit_experiments/library/calibration/fine_drag_cal.py @@ -30,7 +30,7 @@ class FineDragCal(BaseCalibrationExperiment, FineDrag): - """A calibration version of the fine drag experiment.""" + """A calibration version of the fine DRAG experiment.""" def __init__( self, @@ -41,7 +41,7 @@ def __init__( cal_parameter_name: Optional[str] = "β", auto_update: bool = True, ): - r"""See class :class:`FineDrag` for details. + r"""See class :class:`.FineDrag` for details. Note that this class implicitly assumes that the target angle of the gate is :math:`\pi` as seen from the default experiment options. @@ -148,7 +148,7 @@ def update_calibrations(self, experiment_data: ExperimentData): class FineXDragCal(FineDragCal): - """Fine drag calibration of X gate.""" + """Fine DRAG calibration of X gate.""" def __init__( self, @@ -158,7 +158,7 @@ def __init__( cal_parameter_name: Optional[str] = "β", auto_update: bool = True, ): - r"""see class :class:`FineDrag` for details. + r"""see class :class:`.FineDrag` for details. Args: physical_qubits: Sequence containing the qubit for which to run the @@ -180,7 +180,7 @@ def __init__( class FineSXDragCal(FineDragCal): - """Fine drag calibration of X gate.""" + """Fine DRAG calibration of X gate.""" def __init__( self, @@ -190,7 +190,7 @@ def __init__( cal_parameter_name: Optional[str] = "β", auto_update: bool = True, ): - r"""see class :class:`FineDrag` for details. + r"""see class :class:`.FineDrag` for details. Args: physical_qubits: Sequence containing the qubit for which to run the diff --git a/qiskit_experiments/library/calibration/rough_drag_cal.py b/qiskit_experiments/library/calibration/rough_drag_cal.py index 53c0efcb63..21e055f8c4 100644 --- a/qiskit_experiments/library/calibration/rough_drag_cal.py +++ b/qiskit_experiments/library/calibration/rough_drag_cal.py @@ -102,7 +102,7 @@ def _attach_calibrations(self, circuit: QuantumCircuit): def update_calibrations(self, experiment_data: ExperimentData): """Update the beta using the value directly reported from the fit. - See :class:`DragCalAnalysis` for details on the fit. + See :class:`.DragCalAnalysis` for details on the fit. """ new_beta = BaseUpdater.get_value( diff --git a/qiskit_experiments/library/characterization/cr_hamiltonian.py b/qiskit_experiments/library/characterization/cr_hamiltonian.py index e655fd2fc5..97378dd2b7 100644 --- a/qiskit_experiments/library/characterization/cr_hamiltonian.py +++ b/qiskit_experiments/library/characterization/cr_hamiltonian.py @@ -122,7 +122,7 @@ class CrossResonanceHamiltonian(BaseExperiment): # section: manual .. ref_website:: Qiskit Textbook 6.7, - https://qiskit.org/textbook/ch-quantum-hardware/hamiltonian-tomography.html + https://github.com/Qiskit/textbook/blob/main/notebooks/quantum-hardware-pulses/hamiltonian-tomography.ipynb """ # Number of CR pulses. The flat top duration per pulse is divided by this number. diff --git a/qiskit_experiments/library/characterization/multi_state_discrimination.py b/qiskit_experiments/library/characterization/multi_state_discrimination.py index 14078af813..ffea405a52 100644 --- a/qiskit_experiments/library/characterization/multi_state_discrimination.py +++ b/qiskit_experiments/library/characterization/multi_state_discrimination.py @@ -55,7 +55,7 @@ class MultiStateDiscrimination(BaseExperiment): # section: reference `Qiskit Textbook\ - `_ + `_ """ diff --git a/qiskit_experiments/library/characterization/rabi.py b/qiskit_experiments/library/characterization/rabi.py index 4f102f84d3..724354f53d 100644 --- a/qiskit_experiments/library/characterization/rabi.py +++ b/qiskit_experiments/library/characterization/rabi.py @@ -50,8 +50,8 @@ class Rabi(BaseExperiment, RestlessMixin): # section: manual :ref:`Rabi Calibration` - See also `Qiskit Textbook `_ + See also the `Qiskit Textbook + `_ for the pulse level programming of a Rabi experiment. # section: analysis_ref diff --git a/qiskit_experiments/library/quantum_volume/qv_analysis.py b/qiskit_experiments/library/quantum_volume/qv_analysis.py index d1575309d8..730b4bb020 100644 --- a/qiskit_experiments/library/quantum_volume/qv_analysis.py +++ b/qiskit_experiments/library/quantum_volume/qv_analysis.py @@ -34,7 +34,7 @@ class QuantumVolumeAnalysis(BaseAnalysis): # section: overview Calculate the quantum volume of the analysed system. The quantum volume is determined by the largest successful circuit depth. - A depth is successful if it has 'mean heavy-output probability' > 2/3 with confidence + A depth is successful if it has `mean heavy-output probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. we assume the error (standard deviation) of the heavy output probability is due to a binomial distribution. The standard deviation for binomial distribution is @@ -175,7 +175,7 @@ def _calc_quantum_volume(self, heavy_output_prob_exp, depth, trials): """ Calc the quantum volume of the analysed system. quantum volume is determined by the largest successful depth. - A depth is successful if it has 'mean heavy-output probability' > 2/3 with confidence + A depth is successful if it has `mean heavy-output probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. we assume the error (standard deviation) of the heavy output probability is due to a binomial distribution. standard deviation for binomial distribution is sqrt(np(1-p)), @@ -187,7 +187,7 @@ def _calc_quantum_volume(self, heavy_output_prob_exp, depth, trials): whether the results passed the threshold, the confidence of the result, the heavy output probability for each trial, - the mean heavy output probability, + the mean heavy-output probability, the error of the heavy output probability, the depth of the circuit, the number of trials ran diff --git a/qiskit_experiments/library/quantum_volume/qv_experiment.py b/qiskit_experiments/library/quantum_volume/qv_experiment.py index 8f1528f157..9e8bea0012 100644 --- a/qiskit_experiments/library/quantum_volume/qv_experiment.py +++ b/qiskit_experiments/library/quantum_volume/qv_experiment.py @@ -40,8 +40,8 @@ class QuantumVolume(BaseExperiment): The Quantum Volume is determined by the largest circuit depth :math:`d_{max}`, and equals to :math:`2^{d_{max}}`. - See `Qiskit Textbook - `_ + See the `Qiskit Textbook + `_ for an explanation on the QV protocol. In the QV experiment we generate :class:`~qiskit.circuit.library.QuantumVolume` circuits on @@ -50,7 +50,7 @@ class QuantumVolume(BaseExperiment): Then these circuits run on the quantum backend and on an ideal simulator (either :class:`~qiskit_aer.AerSimulator` or :class:`~qiskit.quantum_info.Statevector`). - A depth :math:`d` QV circuit is successful if it has 'mean heavy-output probability' > 2/3 with + A depth :math:`d` QV circuit is successful if it has `mean heavy-output probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. See :class:`QuantumVolumeAnalysis` documentation for additional diff --git a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py index 97e8e71ea2..07ec769b16 100644 --- a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py +++ b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py @@ -60,7 +60,7 @@ class StandardRB(BaseExperiment, RestlessMixin): Randomized Benchmarking (RB) is an efficient and robust method for estimating the average error rate of a set of quantum gate operations. See `Qiskit Textbook - `_ + `_ for an explanation on the RB method. A standard RB experiment generates sequences of random Cliffords diff --git a/releasenotes/notes/setter-methods-for-experiment-099074e59faffb49.yaml b/releasenotes/notes/setter-methods-for-experiment-099074e59faffb49.yaml new file mode 100644 index 0000000000..157002c964 --- /dev/null +++ b/releasenotes/notes/setter-methods-for-experiment-099074e59faffb49.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Added ``experiment_type`` as optional ``__init__`` kwarg in :class:`.BatchExperiment` + and :class:`.ParallelExperiment`. + - | + :math:'experiment_type' can now be easily set and retrieved from the experiment + object post-construction using the 'experiment_type' property and setter. diff --git a/requirements-dev.txt b/requirements-dev.txt index e601c014d1..f4f802a434 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ +qiskit-terra>=0.45.0 black~=22.0 stestr pylint~=3.0.2 diff --git a/requirements-extras.txt b/requirements-extras.txt index a1fc84cc89..25689a0858 100644 --- a/requirements-extras.txt +++ b/requirements-extras.txt @@ -1,5 +1,5 @@ qiskit-ibm-provider>=0.6.1 # for submitting experiments to backends through the IBM provider cvxpy>=1.3.2 # for tomography scikit-learn # for discriminators -qiskit-aer>=0.11.0 +qiskit-aer>=0.11.0,<=0.12.2 # temporary version pin until 0.13.1 release qiskit_dynamics>=0.4.0 # for the PulseBackend diff --git a/test/framework/test_composite.py b/test/framework/test_composite.py index 9c2555c0c0..b006d9f7c0 100644 --- a/test/framework/test_composite.py +++ b/test/framework/test_composite.py @@ -151,6 +151,21 @@ def test_roundtrip_serializable(self): self.assertRoundTripSerializable(exp) + def test_experiment_type(self): + """Test experiment_type setter.""" + + exp1 = FakeExperiment([0]) + + par_exp1 = ParallelExperiment([exp1], flatten_results=False) + batch_exp1 = BatchExperiment([exp1], flatten_results=False) + self.assertEqual(par_exp1.experiment_type, "ParallelExperiment") + self.assertEqual(batch_exp1.experiment_type, "BatchExperiment") + + par_exp2 = ParallelExperiment([exp1], flatten_results=False, experiment_type="yooo") + batch_exp2 = BatchExperiment([exp1], flatten_results=False, experiment_type="blaaa") + self.assertEqual(par_exp2.experiment_type, "yooo") + self.assertEqual(batch_exp2.experiment_type, "blaaa") + @ddt class TestCompositeExperimentData(QiskitExperimentsTestCase): diff --git a/test/framework/test_framework.py b/test/framework/test_framework.py index 674b54f850..16488a38f7 100644 --- a/test/framework/test_framework.py +++ b/test/framework/test_framework.py @@ -340,3 +340,22 @@ def circuits(self): } self.assertEqual(exp.job_info(backend=backend), job_info) + + def test_experiment_type(self): + """Test the experiment_type setter for the experiment.""" + + class MyExp(BaseExperiment): + """Some arbitrary experiment""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def circuits(self): + pass + + exp1 = MyExp(physical_qubits=[0], experiment_type="blaaa") + self.assertEqual(exp1.experiment_type, "blaaa") + exp2 = MyExp(physical_qubits=[0]) + self.assertEqual(exp2.experiment_type, "MyExp") + exp2.experiment_type = "suieee" + self.assertEqual(exp2.experiment_type, "suieee") diff --git a/tox.ini b/tox.ini index d89db5a70c..6e89cda43a 100644 --- a/tox.ini +++ b/tox.ini @@ -101,6 +101,20 @@ setenv = commands = sphinx-build -T -W --keep-going -b html {posargs} docs/ docs/_build/html +[testenv:docs-terra-main] +usedevelop = True +passenv = + EXPERIMENTS_DEV_DOCS + PROD_BUILD + RELEASE_STRING + VERSION_STRING +deps = + git+https://github.com/Qiskit/qiskit-terra + -r{toxinidir}/requirements-dev.txt + -r{toxinidir}/requirements-extras.txt +commands = + sphinx-build -j auto -T -W --keep-going -b html {posargs} docs/ docs/_build/html + [testenv:docs-clean] skip_install = true deps =