From e9abdc45d52411091c707006ec6cd0d26f6f3f23 Mon Sep 17 00:00:00 2001 From: Yael Ben-Haim Date: Wed, 6 Sep 2023 16:29:41 +0300 Subject: [PATCH 1/7] Removed test_t1_parallel_different_analysis_options (#1264) The test sets a bad initial guess and expects the fitter to fail. It has to be removed because the underlying assumption - that the fitter must fail when given a bad guess - is incorrect. --- test/library/characterization/test_t1.py | 31 ------------------------ 1 file changed, 31 deletions(-) diff --git a/test/library/characterization/test_t1.py b/test/library/characterization/test_t1.py index 7a275d3c31..05a95969b4 100644 --- a/test/library/characterization/test_t1.py +++ b/test/library/characterization/test_t1.py @@ -190,37 +190,6 @@ def test_t1_parallel_measurement_level_1(self): self.assertEqual(sub_res.quality, "good") self.assertAlmostEqual(sub_res.value.n, t1[qb], delta=3) - def test_t1_parallel_different_analysis_options(self): - """ - Test parallel experiments of T1 using a simulator, for the case where - the sub-experiments have different analysis options - """ - - t1 = [25, 25] - t2 = [value / 2 for value in t1] - - backend = NoisyDelayAerBackend(t1, t2) - - delays = list(range(1, 40, 3)) - - exp0 = T1([0], delays) - exp0.analysis.set_options(p0={"tau": 30}) - - exp1 = T1([1], delays) - exp1.analysis.set_options(p0={"tau": 1000000}) - - par_exp = ParallelExperiment([exp0, exp1], flatten_results=False) - res = par_exp.run(backend=backend, seed_simulator=4) - self.assertExperimentDone(res) - - sub_res = [] - for i in range(2): - sub_res.append(res.child_data(i).analysis_results("T1")) - - self.assertEqual(sub_res[0].quality, "good") - self.assertAlmostEqual(sub_res[0].value.n, t1[0], delta=3) - self.assertEqual(sub_res[1].quality, "bad") - def test_t1_analysis(self): """ Test T1Analysis From c294aa1cc4816285d875be11968fa49e8b403df8 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Wed, 6 Sep 2023 09:59:02 -0400 Subject: [PATCH 2/7] Use only sx, x, and rz in half angle experiment (#1259) Previously, `HalfAngle` used a y gate which is not a standard basis gate for IBM devices and is also not the gate that the experiment is testing. Now, the experiment uses x and sx whenever possible, which are the gates under test. --------- Co-authored-by: Helena Zhang --- .../library/characterization/half_angle.py | 61 ++++++++++++++----- .../notes/half-angle-x-600debac368ce2c6.yaml | 10 +++ .../characterization/test_half_angle.py | 8 +-- 3 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/half-angle-x-600debac368ce2c6.yaml diff --git a/qiskit_experiments/library/characterization/half_angle.py b/qiskit_experiments/library/characterization/half_angle.py index 2b17e68949..4743149512 100644 --- a/qiskit_experiments/library/characterization/half_angle.py +++ b/qiskit_experiments/library/characterization/half_angle.py @@ -43,9 +43,48 @@ class HalfAngle(BaseExperiment): This sequence measures angle errors where the axis of the :code:`sx` and :code:`x` rotation are not parallel. A similar experiment is described in Ref.~[1] where the gate sequence :code:`x - y` is repeated to amplify errors caused by non-orthogonal - :code:`x` and :code:`y` rotation axes. Such errors can occur due to phase errors. - For example, the non-linearities in the mixer's skew for :math:`\pi/2` pulses may - be different from the :math:`\pi` pulse. + :code:`x` and :code:`y` rotation axes. + + One cause of such errors is non-linearity in the microwave mixer used + to produce the pulses for the ``x`` and ``sx`` gates. Typically, these + gates are calibrated to have the same duration and so have different + pulse amplitudes. Non-linearities in the mixer's skew can cause the + angle to differ for these different pulse amplitudes. + + The way the experiment works is that the initial ``Ry(π/2)`` puts the + qubit close to the :math:`+X` state, with a deviation :math:`δθ`, due + to the misalignment between ``sx`` and ``x`` (``Ry(π/2)`` is + implemented with ``sx`` as described below). The first ``sx - sx`` do + nothing as they should be rotations about the axis the qubit is + pointing along. The first ``y`` then mirrors the qubit about the + :math:`y` axis in the :math:`xy` plane of the Bloch sphere, so the + :math:`δθ` deviation from :math:`+X` becomes a :math:`-δθ` from + :math:`-X`. The next ``sx - sx`` sequence rotates about the axis that + is :math:`+δθ` rotated in the :math:`xy` plane from :math:`+X`, which + takes the deviation from :math:`-X` from :math:`-δθ` to :math:`+3 δθ`. + Then the next ``y`` mirrors this across the :math:`y` axis, taking the + state to :math:`-3 δθ` from :math:`+X`. This pattern continues with + each iteration, with the angular deviation in units of :math:`δθ` + following the sequence 1, 3, 5, 7, 9, etc. from :math:`+X` and + :math:`-X`. The final ``sx`` rotation serves mainly to rotate these + deviations from :math:`+X` and :math:`-X` in the :math:`xy` plane into + deviations out of the :math:`xy` plane, so that they appear as a signal + in the :math:`Z` basis. Because ``sx`` has a :math:`δθ` deviation from + ``x``, the final ``sx`` adds an extra :math:`δθ` to the deviations, so + the pattern ends up as 2, 4, 6, 8, etc., meaning that each iteration + adds :math:`2 δθ` to the deviation from the equator of the Bloch sphere + (with the sign alternating due to the ``y`` gates, so the deviations + are really -2, 4, -6, 8, etc.). + + For the implementation of the circuits, the experiment uses ``Rz(π/2) - + sx - Rz(-π/2)`` to implement the ``Ry(π/2)`` and ``Rz(π/2) - x - + Rz(-π/2)`` to implement the ``y``. So the experiment makes use of only + ``sx``, ``x``, ``Rz(π/2)``, and ``Rz(-π/2)`` gates. For the + experiment's analysis to be valid, it is important that the ``sx`` and + ``x`` gates are not replaced (such as by a transpiler pass that + replaces ``x`` with ``sx - sx``), as it is the angle between them which + is being inferred. It is assumed that the angle between ``x`` and + ``Rz`` is exactly :math:`π/2`. # section: analysis_ref :class:`.ErrorAmplificationAnalysis` @@ -66,18 +105,6 @@ def _default_experiment_options(cls) -> Options: options.repetitions = list(range(15)) return options - @classmethod - def _default_transpile_options(cls) -> Options: - """Default transpile options. - - The basis gates option should not be changed since it will affect the gates and - the pulses that are run on the hardware. - """ - options = super()._default_transpile_options() - options.basis_gates = ["sx", "rz", "y"] - options.inst_map = None - return options - def __init__(self, physical_qubits: Sequence[int], backend: Optional[Backend] = None): """Setup a half angle experiment on the given qubit. @@ -126,7 +153,9 @@ def circuits(self) -> List[QuantumCircuit]: for _ in range(repetition): circuit.sx(0) circuit.sx(0) - circuit.y(0) + circuit.rz(np.pi / 2, 0) + circuit.x(0) + circuit.rz(-np.pi / 2, 0) circuit.sx(0) circuit.measure_all() diff --git a/releasenotes/notes/half-angle-x-600debac368ce2c6.yaml b/releasenotes/notes/half-angle-x-600debac368ce2c6.yaml new file mode 100644 index 0000000000..d1af5944f2 --- /dev/null +++ b/releasenotes/notes/half-angle-x-600debac368ce2c6.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + The :class:`.HalfAngle` experiment's circuits were changed so that they use + combinations of ``rz`` and ``x`` instead of the less standard ``y`` gate. + This change allows :class:`~HalfAngle` to be run on IBM backends directly. + Previously, it could only be run through the :class:`~HalfAngleCal` + subclass in combination with a :class:`~Calibrations` instance containing a + custom calibration for the ``y`` gate. + Fixes issue `#1233 `_. diff --git a/test/library/characterization/test_half_angle.py b/test/library/characterization/test_half_angle.py index 3f2a75e47b..e90c6b6d96 100644 --- a/test/library/characterization/test_half_angle.py +++ b/test/library/characterization/test_half_angle.py @@ -44,12 +44,12 @@ def test_end_to_end(self): self.assertTrue(abs(d_theta - error) < tol) def test_circuits(self): - """Test that transpiling works and that we can have a y gate with a calibration.""" + """Test that transpiling works.""" qubit = 1 inst_map = InstructionScheduleMap() - for inst in ["sx", "y"]: + for inst in ["sx", "x"]: inst_map.add(inst, (qubit,), pulse.Schedule(name=inst)) hac = HalfAngle([qubit]) @@ -64,8 +64,8 @@ def test_circuits(self): self.assertEqual(circ.count_ops()["sx"], idx * 2 + 2) self.assertEqual(circ.calibrations["sx"][((qubit,), ())], pulse.Schedule(name="sx")) if idx > 0: - self.assertEqual(circ.count_ops()["y"], idx) - self.assertEqual(circ.calibrations["y"][((qubit,), ())], pulse.Schedule(name="y")) + self.assertEqual(circ.count_ops()["x"], idx) + self.assertEqual(circ.calibrations["x"][((qubit,), ())], pulse.Schedule(name="x")) def test_experiment_config(self): """Test converting to and from config works""" From 60655bb7aa78fd8f27a3a0be3c19f1e89e483305 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 6 Sep 2023 17:39:15 +0200 Subject: [PATCH 3/7] Improve performance of RB circuit generation (#1263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Summary The circuit generation for RB experiments is slow due to the use of indexing on sparse matrices (`_CLIFFORD_COMPOSE_2Q` from `qiskit_experiments.library.randomized_benchmarking.clifford_utils`). Since indexing is the only operation used on `_CLIFFORD_COMPOSE_2Q` we can improve performance by using the `lil_matrix` format. ### Details and comments Improve performance of RB circuit generation by changing the format of `_CLIFFORD_COMPOSE_2Q` to `lil_matrix`. Micro benchmark: ``` import scipy.sparse from qiskit_experiments.library.randomized_benchmarking.clifford_utils import _CLIFFORD_COMPOSE_2Q _CLIFFORD_COMPOSE_2Q_lil = scipy.sparse.lil_matrix(_CLIFFORD_COMPOSE_2Q) %timeit _CLIFFORD_COMPOSE_2Q[2,3] %timeit _CLIFFORD_COMPOSE_2Q_lil[2,3] ``` Result: ``` 29.4 µs ± 4.01 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each) 2.64 µs ± 777 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each) ``` Application benchmark: ``` import time import qiskit from qiskit_experiments.library import InterleavedRB gate=qiskit.circuit.library.CXGate() rb=InterleavedRB(gate, physical_qubits= (0,1),lengths= [1,10,50,100,200], num_samples= 10) t0=time.time() c=rb.circuits() dt=time.time()-t0 ``` results in a speedup from 2.1419544219970703 to 1.154689073562622 seconds. ### PR checklist (delete when all criteria are met) - [x] I have read the contributing guide `CONTRIBUTING.md`. - [x] I have added the tests to cover my changes. - [x] I have updated the documentation accordingly. - [x] I have added a release note file using `reno` if this change needs to be documented in the release notes. (no release note required) --------- Co-authored-by: Helena Zhang --- .../library/randomized_benchmarking/clifford_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py index fb5759ac8d..d7584be28b 100644 --- a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py +++ b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py @@ -36,7 +36,9 @@ _CLIFFORD_COMPOSE_1Q = np.load(f"{_DATA_FOLDER}/clifford_compose_1q.npz")["table"] _CLIFFORD_INVERSE_1Q = np.load(f"{_DATA_FOLDER}/clifford_inverse_1q.npz")["table"] -_CLIFFORD_COMPOSE_2Q = scipy.sparse.load_npz(f"{_DATA_FOLDER}/clifford_compose_2q_sparse.npz") +_CLIFFORD_COMPOSE_2Q = scipy.sparse.lil_matrix( + scipy.sparse.load_npz(f"{_DATA_FOLDER}/clifford_compose_2q_sparse.npz") +) _CLIFFORD_INVERSE_2Q = np.load(f"{_DATA_FOLDER}/clifford_inverse_2q.npz")["table"] From 4f08d6facc8f4eb5361bc05296d5e6e0b03e0a35 Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Thu, 7 Sep 2023 17:18:08 -0400 Subject: [PATCH 4/7] Address timing dependence in ExperimentData.add_jobs test (#1267) Previously, the test `test_add_data_job` called `add_jobs` with two jobs and then tested that the results from `ExperimentData.data()` matched the results of the first job concatenated with the second. `ExperimentData` uses threads to handle tracking jobs. Even though these jobs are dummy jobs that take no time, `ExperimentData` would occasionally add the results from the second job before the first and then fail the results comparison. With this change, a label is attached to each result and then the results are sorted based on the label before testing against the expected results. Closes #1044 --- .../test_db_experiment_data.py | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/database_service/test_db_experiment_data.py b/test/database_service/test_db_experiment_data.py index 6d6713248e..3cbfa52928 100644 --- a/test/database_service/test_db_experiment_data.py +++ b/test/database_service/test_db_experiment_data.py @@ -107,8 +107,8 @@ def test_add_data_result(self): def test_add_data_result_metadata(self): """Test add result metadata.""" exp_data = ExperimentData(backend=self.backend, experiment_type="qiskit_test") - result1 = self._get_job_result(1, has_metadata=False) - result2 = self._get_job_result(1, has_metadata=True) + result1 = self._get_job_result(1, no_metadata=True) + result2 = self._get_job_result(1) exp_data.add_data(result1) exp_data.add_data(result2) @@ -119,12 +119,14 @@ def test_add_data_job(self): """Test add job data.""" a_job = mock.create_autospec(Job, instance=True) a_job.result.return_value = self._get_job_result(3) + num_circs = 3 jobs = [] for _ in range(2): job = mock.create_autospec(Job, instance=True) - job.result.return_value = self._get_job_result(2) + job.result.return_value = self._get_job_result(2, label_from=num_circs) job.status.return_value = JobStatus.DONE jobs.append(job) + num_circs = num_circs + 2 expected = a_job.result().get_counts() for job in jobs: @@ -135,7 +137,13 @@ def test_add_data_job(self): self.assertExperimentDone(exp_data) exp_data.add_jobs(jobs) self.assertExperimentDone(exp_data) - self.assertEqual(expected, [sdata["counts"] for sdata in exp_data.data()]) + self.assertEqual( + expected, + [ + sdata["counts"] + for sdata in sorted(exp_data.data(), key=lambda x: x["metadata"]["label"]) + ], + ) self.assertIn(a_job.job_id(), exp_data.job_ids) def test_add_data_job_callback(self): @@ -1073,7 +1081,7 @@ def _job2_result(): exp_data.data(0)["counts"], [copied.data(0)["counts"], copied.data(1)["counts"]] ) - def _get_job_result(self, circ_count, has_metadata=False): + def _get_job_result(self, circ_count, label_from=0, no_metadata=False): """Return a job result with random counts.""" job_result = { "backend_name": BackendData(self.backend).name, @@ -1085,12 +1093,12 @@ def _get_job_result(self, circ_count, has_metadata=False): } circ_result_template = {"shots": 1024, "success": True, "data": {}} - for _ in range(circ_count): + for i_circ in range(circ_count): counts = randrange(1024) circ_result = copy.copy(circ_result_template) circ_result["data"] = {"counts": {"0x0": counts, "0x3": 1024 - counts}} - if has_metadata: - circ_result["header"] = {"metadata": {"meas_basis": "pauli"}} + if not no_metadata: + circ_result["header"] = {"metadata": {"label": label_from + i_circ}} job_result["results"].append(circ_result) return Result.from_dict(job_result) From f2e5d8b72b8e4a98fe3ea58fd9f32c43ddaf8057 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Fri, 8 Sep 2023 17:39:54 -0400 Subject: [PATCH 5/7] Make CI tests faster (#1246) ### Summary This PR does a few things to make tests run faster: - [x] Only test on the lowest and highest supported python versions - [x] Set MacOS build options to be the same as the other OSes (and remove coverage) - [x] Add `.stestr` to the cache as suggested by @mtreinish. This doesn't seem to improve the Ubuntu and Windows runtimes but significantly improves MacOS's. - [x] Group tests in stestr by class name. This might avoid large parallelized tests being run on multiple workers simultaneously and slowing each test down. - [x] Fail a test automatically if it takes longer than 60 seconds (hopefully this can be shortened in the future, but for now it mostly prevents a very long test from being added) - [x] Shorten long tests by decreasing shots and generating smaller circuits where the size isn't relevant (such as the roundtrip serialization tests) - [x] Also fixes a bug in the DRAG experiment where integer `beta` values caused a serialization error. Test are currently 20-40 minutes for Windows/Ubuntu and 50+ minutes for MacOS. With this PR, all tests go down to ~10 minutes. --------- Co-authored-by: Will Shanks --- .github/workflows/main.yml | 31 ++++------ .stestr.conf | 1 + CONTRIBUTING.md | 2 + README.md | 1 - .../library/characterization/drag.py | 2 +- .../library/tomography/mit_qpt_experiment.py | 2 +- .../library/tomography/qpt_experiment.py | 2 +- .../rabi-and-qv-bugfix-34636baee6651af1.yaml | 4 +- test/base.py | 9 +++ test/library/calibration/test_drag.py | 61 +++++++------------ test/library/calibration/test_rabi.py | 3 +- .../calibration/test_rough_amplitude.py | 8 ++- .../test_cross_resonance_hamiltonian.py | 2 +- .../test_qubit_spectroscopy.py | 8 ++- .../characterization/test_readout_angle.py | 4 +- .../test_resonator_spectroscopy.py | 12 ++-- test/library/characterization/test_t1.py | 2 +- test/library/characterization/test_t2hahn.py | 4 +- .../library/characterization/test_t2ramsey.py | 4 +- test/library/quantum_volume/test_qv.py | 2 +- .../test_interleaved_rb.py | 5 +- .../test_standard_rb.py | 4 +- .../tomography/test_process_tomography.py | 2 +- tox.ini | 2 +- 24 files changed, 88 insertions(+), 89 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0649ab9d55..4f45bb6170 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, "3.11"] os: ["ubuntu-latest", "macOS-latest", "windows-latest"] steps: - name: Print Concurrency Group @@ -39,25 +39,20 @@ jobs: ${{ runner.os }}-${{ matrix.python-version }}-pip-tests- ${{ runner.os }}-${{ matrix.python-version }}-pip- ${{ runner.os }}-${{ matrix.python-version }} + - name: Stestr cache + uses: actions/cache@v3 + with: + path: .stestr + key: stestr-${{ runner.os }}-${{ matrix.python-version }} + restore-keys: | + stestr-${{ runner.os }}- + stestr- - name: Install Deps - run: python -m pip install -U "tox==3.27.1" setuptools virtualenv wheel - - name: Install and Run Tests (Windows and Linux) + run: python -m pip install -U tox setuptools virtualenv wheel stestr + - name: Install and Run Tests run: tox -e py - if: runner.os != 'macOS' - - name: Install and Run Tests (Macs only) - run: tox -e cover - if: runner.os == 'macOS' - env: - OMP_NUM_THREADS: 1 - - name: Report coverage to coveralls.io (Macs only) - if: runner.os == 'macOS' - uses: coverallsapp/github-action@v2 - env: - ACTIONS_RUNNER_DEBUG: 1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: unit-tests_python${{ matrix.python-version }}-${{ matrix.os }} - path-to-lcov: coverage.lcov + - name: Clean up stestr cache + run: stestr history remove all lint: name: lint diff --git a/.stestr.conf b/.stestr.conf index 55302a1601..460ee43afa 100644 --- a/.stestr.conf +++ b/.stestr.conf @@ -1,2 +1,3 @@ [DEFAULT] test_path=./test +parallel_class=True \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20412c851b..7f4b45191f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,6 +121,8 @@ To run a method: tox -- -n test.python.test_examples.TestPythonExamples.test_all_examples ``` +Note that tests will fail automatically if they do not finish execution within 60 seconds. + #### STDOUT/STDERR and logging capture When running tests in parallel using `stestr` either via tox, the Makefile (`make diff --git a/README.md b/README.md index f165a1c865..b9a3e8020c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![License](https://img.shields.io/github/license/Qiskit-Extensions/qiskit-experiments.svg)](https://opensource.org/licenses/Apache-2.0) [![Release](https://img.shields.io/github/release/Qiskit-Extensions/qiskit-experiments.svg)](https://github.com/Qiskit-Extensions/qiskit-experiments/releases) ![Python](https://img.shields.io/pypi/pyversions/qiskit-experiments.svg) -[![Coverage Status](https://coveralls.io/repos/github/Qiskit-Extensions/qiskit-experiments/badge.svg?branch=main)](https://coveralls.io/github/Qiskit-Extensions/qiskit-experiments?branch=main) [![DOI](https://joss.theoj.org/papers/10.21105/joss.05329/status.svg)](https://doi.org/10.21105/joss.05329) **Qiskit Experiments** is a repository that builds tools for building, running, diff --git a/qiskit_experiments/library/characterization/drag.py b/qiskit_experiments/library/characterization/drag.py index 7e7639fe3b..fb48add906 100644 --- a/qiskit_experiments/library/characterization/drag.py +++ b/qiskit_experiments/library/characterization/drag.py @@ -166,7 +166,7 @@ def circuits(self) -> List[QuantumCircuit]: ) for beta_val in self.experiment_options.betas: - beta_val = np.round(beta_val, decimals=6) + beta_val = float(np.round(beta_val, decimals=6)) assigned_circuit = circuit.assign_parameters({beta: beta_val}, inplace=False) diff --git a/qiskit_experiments/library/tomography/mit_qpt_experiment.py b/qiskit_experiments/library/tomography/mit_qpt_experiment.py index bc7d859137..bdebf34bd0 100644 --- a/qiskit_experiments/library/tomography/mit_qpt_experiment.py +++ b/qiskit_experiments/library/tomography/mit_qpt_experiment.py @@ -78,7 +78,7 @@ def __init__( basis_indices: Optional, a list of basis indices for generating partial tomography measurement data. Each item should be given as a pair of lists of preparation and measurement basis configurations - ``([p[0], p[1], ..], m[0], m[1], ...])``, where ``p[i]`` is the + ``([p[0], p[1], ...], [m[0], m[1], ...])``, where ``p[i]`` is the preparation basis index, and ``m[i]`` is the measurement basis index for qubit-i. If not specified full tomography for all indices of the preparation and measurement bases will be performed. diff --git a/qiskit_experiments/library/tomography/qpt_experiment.py b/qiskit_experiments/library/tomography/qpt_experiment.py index fb253a8a06..17d7d7347b 100644 --- a/qiskit_experiments/library/tomography/qpt_experiment.py +++ b/qiskit_experiments/library/tomography/qpt_experiment.py @@ -81,7 +81,7 @@ def __init__( basis_indices: Optional, a list of basis indices for generating partial tomography measurement data. Each item should be given as a pair of lists of preparation and measurement basis configurations - ``([p[0], p[1], ..], m[0], m[1], ...])``, where ``p[i]`` is the + ``([p[0], p[1], ...], [m[0], m[1], ...])``, where ``p[i]`` is the preparation basis index, and ``m[i]`` is the measurement basis index for qubit-i. If not specified full tomography for all indices of the preparation and measurement bases will be performed. diff --git a/releasenotes/notes/rabi-and-qv-bugfix-34636baee6651af1.yaml b/releasenotes/notes/rabi-and-qv-bugfix-34636baee6651af1.yaml index 8c06aff21d..b6119d299c 100644 --- a/releasenotes/notes/rabi-and-qv-bugfix-34636baee6651af1.yaml +++ b/releasenotes/notes/rabi-and-qv-bugfix-34636baee6651af1.yaml @@ -5,4 +5,6 @@ fixes: `qiskit-ibm-provider` using custom amplitudes provided as a numpy array. - | Resolved an issue that caused QV experiments to fail when executed via `qiskit-ibm-provider` using - Qiskit Terra for calculating ideal probabilities, instead of Aer. \ No newline at end of file + Qiskit Terra for calculating ideal probabilities, instead of Aer. + - | + Resolved a serialization issue that affected DRAG experiments with integral beta values specified. \ No newline at end of file diff --git a/test/base.py b/test/base.py index b0a4aa15d0..d1ad6e8ed6 100644 --- a/test/base.py +++ b/test/base.py @@ -13,11 +13,13 @@ Qiskit Experiments test case class """ +import os import json import pickle import warnings from typing import Any, Callable, Optional +import fixtures import uncertainties from qiskit.test import QiskitTestCase from qiskit.utils.deprecation import deprecate_func @@ -30,10 +32,17 @@ from qiskit_experiments.framework.experiment_data import ExperimentStatus from .extended_equality import is_equivalent +# Fail tests that take longer than this +TEST_TIMEOUT = os.environ.get("TEST_TIMEOUT", 60) + class QiskitExperimentsTestCase(QiskitTestCase): """Qiskit Experiments specific extra functionality for test cases.""" + def setUp(self): + super().setUp() + self.useFixture(fixtures.Timeout(TEST_TIMEOUT, gentle=True)) + @classmethod def setUpClass(cls): """Set-up test class.""" diff --git a/test/library/calibration/test_drag.py b/test/library/calibration/test_drag.py index 8a095b1e46..89354a8602 100644 --- a/test/library/calibration/test_drag.py +++ b/test/library/calibration/test_drag.py @@ -48,58 +48,42 @@ def setUp(self): self.x_plus = xp self.test_tol = 0.1 - # pylint: disable=no-member - def test_end_to_end(self): + @data( + (None, None, None), + (0.0044, None, None), + (0.04, np.linspace(-4, 4, 31), {"beta": 1.8, "freq": 0.08}), + ) + @unpack + def test_end_to_end(self, freq, betas, p0_opt): """Test the drag experiment end to end.""" drag_experiment_helper = DragHelper(gate_name="Drag(xp)") + if freq: + drag_experiment_helper.frequency = freq backend = MockIQBackend(drag_experiment_helper) drag = RoughDrag([1], self.x_plus) + drag.set_run_options(shots=200) + + if betas is not None: + drag.set_experiment_options(betas=betas) + if p0_opt: + drag.analysis.set_options(p0=p0_opt) expdata = drag.run(backend) self.assertExperimentDone(expdata) result = expdata.analysis_results(1) - # pylint: disable=no-member - self.assertTrue(abs(result.value.n - backend.experiment_helper.ideal_beta) < self.test_tol) - self.assertEqual(result.quality, "good") - - # Small leakage will make the curves very flat, in this case one should - # rather increase beta. - drag_experiment_helper.frequency = 0.0044 - - drag = RoughDrag([0], self.x_plus) - exp_data = drag.run(backend) - self.assertExperimentDone(exp_data) - result = exp_data.analysis_results(1) - - # pylint: disable=no-member - self.assertTrue(abs(result.value.n - backend.experiment_helper.ideal_beta) < self.test_tol) - self.assertEqual(result.quality, "good") - - # Large leakage will make the curves oscillate quickly. - drag_experiment_helper.frequency = 0.04 - drag = RoughDrag([1], self.x_plus, betas=np.linspace(-4, 4, 31)) - # pylint: disable=no-member - drag.set_run_options(shots=200) - drag.analysis.set_options(p0={"beta": 1.8, "freq": 0.08}) - exp_data = drag.run(backend) - self.assertExperimentDone(exp_data) - result = exp_data.analysis_results(1) - - meas_level = exp_data.metadata["meas_level"] - - self.assertEqual(meas_level, MeasLevel.CLASSIFIED) self.assertTrue(abs(result.value.n - backend.experiment_helper.ideal_beta) < self.test_tol) self.assertEqual(result.quality, "good") + self.assertEqual(expdata.metadata["meas_level"], MeasLevel.CLASSIFIED) @data( - (0.0040, 1.0, 0.00, [1, 3, 5], None, 0.1), # partial oscillation. - (0.0020, 0.5, 0.00, [1, 3, 5], None, 0.5), # even slower oscillation with amp < 1 - (0.0040, 0.8, 0.05, [3, 5, 7], None, 0.1), # constant offset, i.e. lower SNR. - (0.0800, 0.9, 0.05, [1, 3, 5], np.linspace(-1, 1, 51), 0.1), # Beta not in range - (0.2000, 0.5, 0.10, [1, 3, 5], np.linspace(-2.5, 2.5, 51), 0.1), # Max closer to zero + (0.0040, 1.0, 0.00, [1, 3, 5], None, 0.2), # partial oscillation. + (0.0020, 0.5, 0.00, [1, 3, 5], None, 1.0), # even slower oscillation with amp < 1 + (0.0040, 0.8, 0.05, [3, 5, 7], None, 0.2), # constant offset, i.e. lower SNR. + (0.0800, 0.9, 0.05, [1, 3, 5], np.linspace(-1, 1, 51), 0.2), # Beta not in range + (0.2000, 0.5, 0.10, [1, 3, 5], np.linspace(-2.5, 2.5, 51), 0.2), # Max closer to zero ) @unpack def test_nasty_data(self, freq, amp, offset, reps, betas, tol): @@ -113,6 +97,7 @@ def test_nasty_data(self, freq, amp, offset, reps, betas, tol): drag = RoughDrag([0], self.x_plus, betas=betas) drag.set_experiment_options(reps=reps) + drag.set_run_options(shots=500) exp_data = drag.run(backend) self.assertExperimentDone(exp_data) @@ -190,7 +175,7 @@ def test_default_circuits(self): def test_circuit_roundtrip_serializable(self): """Test circuit serializations for drag experiment.""" drag = RoughDrag([0], self.x_plus) - drag.set_experiment_options(reps=[2, 4, 8]) + drag.set_experiment_options(reps=[2, 4], betas=[-5, 5]) drag.backend = FakeWashingtonV2() self.assertRoundTripSerializable(drag._transpiled_circuits()) diff --git a/test/library/calibration/test_rabi.py b/test/library/calibration/test_rabi.py index cc73299803..7708e64cfd 100644 --- a/test/library/calibration/test_rabi.py +++ b/test/library/calibration/test_rabi.py @@ -50,9 +50,10 @@ def setUp(self): def test_rabi_end_to_end(self): """Test the Rabi experiment end to end.""" - test_tol = 0.015 + test_tol = 0.15 rabi = Rabi([self.qubit], self.sched, backend=self.backend) + rabi.set_run_options(shots=200) rabi.set_experiment_options(amplitudes=np.linspace(-0.1, 0.1, 21)) expdata = rabi.run() self.assertExperimentDone(expdata) diff --git a/test/library/calibration/test_rough_amplitude.py b/test/library/calibration/test_rough_amplitude.py index df19472d81..c2cf284f09 100644 --- a/test/library/calibration/test_rough_amplitude.py +++ b/test/library/calibration/test_rough_amplitude.py @@ -84,7 +84,7 @@ def test_update(self): def test_circuit_roundtrip_serializable(self): """Test round trip JSON serialization""" - test_amps = [-0.5, 0, 0.5] + test_amps = [-0.5, 0] rabi = RoughXSXAmplitudeCal([0], self.cals, amplitudes=test_amps, backend=self.backend) self.assertRoundTripSerializable(rabi._transpiled_circuits()) @@ -151,14 +151,15 @@ def test_ef_circuits(self): self.assertEqual(circ.calibrations["Rabi"][((0,), (amp,))], expected_x12) def test_ef_update(self): - """Tes that we properly update the pulses on the 1<->2 transition.""" + """Test that we properly update the pulses on the 1<->2 transition.""" - tol = 0.01 + tol = 0.05 default_amp = 0.5 / self.backend.rabi_rate_12 rabi_ef = EFRoughXSXAmplitudeCal( [0], self.cals, amplitudes=np.linspace(-0.1, 0.1, 11), backend=self.backend ) + rabi_ef.set_run_options(shots=200) expdata = rabi_ef.run() self.assertExperimentDone(expdata) @@ -172,6 +173,7 @@ def test_ef_update(self): self.cals.add_parameter_value(int(4 * 160 / 5), "duration", 0, "x12") self.cals.add_parameter_value(int(4 * 160 / 5), "duration", 0, "sx12") rabi_ef = EFRoughXSXAmplitudeCal([0], self.cals, amplitudes=np.linspace(-0.1, 0.1, 11)) + rabi_ef.set_run_options(shots=200) expdata = rabi_ef.run(self.backend) self.assertExperimentDone(expdata) diff --git a/test/library/characterization/test_cross_resonance_hamiltonian.py b/test/library/characterization/test_cross_resonance_hamiltonian.py index 34a2637330..1df5c80aae 100644 --- a/test/library/characterization/test_cross_resonance_hamiltonian.py +++ b/test/library/characterization/test_cross_resonance_hamiltonian.py @@ -192,7 +192,7 @@ def test_integration(self, ix, iy, iz, zx, zy, zz): expr = cr_hamiltonian.CrossResonanceHamiltonian( physical_qubits=(0, 1), sigma=sigma, - # A hack to avoild local function in pickle, i.e. in transpile. + # A hack to avoid local function in pickle, i.e. in transpile. cr_gate=functools.partial( SimulatableCRGate, hamiltonian=hamiltonian, sigma=sigma, dt=dt ), diff --git a/test/library/characterization/test_qubit_spectroscopy.py b/test/library/characterization/test_qubit_spectroscopy.py index d3522c4a7f..12ae08a4a5 100644 --- a/test/library/characterization/test_qubit_spectroscopy.py +++ b/test/library/characterization/test_qubit_spectroscopy.py @@ -160,7 +160,7 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - exp = QubitSpectroscopy([1], np.linspace(int(100e6), int(150e6), int(20e6))) + exp = QubitSpectroscopy([1], np.linspace(int(100e6), int(150e6), 4)) # Checking serialization of the experiment self.assertRoundTripSerializable(exp) @@ -270,7 +270,9 @@ def test_parallel_experiment(self): par_experiment = ParallelExperiment( exp_list, flatten_results=False, backend=parallel_backend ) - par_experiment.set_run_options(meas_level=MeasLevel.KERNELED, meas_return="single") + par_experiment.set_run_options( + meas_level=MeasLevel.KERNELED, meas_return="single", shots=20 + ) par_data = par_experiment.run() self.assertExperimentDone(par_data) @@ -288,7 +290,7 @@ def test_circuit_roundtrip_serializable(self): backend = FakeWashingtonV2() qubit = 1 freq01 = BackendData(backend).drive_freqs[qubit] - frequencies = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 21) + frequencies = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 3) exp = QubitSpectroscopy([1], frequencies, backend=backend) # Checking serialization of the experiment self.assertRoundTripSerializable(exp._transpiled_circuits()) diff --git a/test/library/characterization/test_readout_angle.py b/test/library/characterization/test_readout_angle.py index 1413006e13..a26ffd4e2b 100644 --- a/test/library/characterization/test_readout_angle.py +++ b/test/library/characterization/test_readout_angle.py @@ -36,7 +36,7 @@ def test_readout_angle_end2end(self): MockIQReadoutAngleHelper(iq_cluster_centers=[((-3.0, 3.0), (5.0, 5.0))]), ) exp = ReadoutAngle([0]) - expdata = exp.run(backend, shots=100000) + expdata = exp.run(backend, shots=10000) self.assertExperimentDone(expdata) res = expdata.analysis_results(0) self.assertAlmostEqual(res.value % (2 * np.pi), np.pi / 2, places=2) @@ -45,7 +45,7 @@ def test_readout_angle_end2end(self): MockIQReadoutAngleHelper(iq_cluster_centers=[((0, -3.0), (5.0, 5.0))]), ) exp = ReadoutAngle([0]) - expdata = exp.run(backend, shots=100000) + expdata = exp.run(backend, shots=10000) self.assertExperimentDone(expdata) res = expdata.analysis_results(0) self.assertAlmostEqual(res.value % (2 * np.pi), 15 * np.pi / 8, places=2) diff --git a/test/library/characterization/test_resonator_spectroscopy.py b/test/library/characterization/test_resonator_spectroscopy.py index ef7d4ca406..cbbb110292 100644 --- a/test/library/characterization/test_resonator_spectroscopy.py +++ b/test/library/characterization/test_resonator_spectroscopy.py @@ -134,14 +134,14 @@ def test_end_to_end(self, freq_shift): def test_experiment_config(self): """Test converting to and from config works""" - exp = ResonatorSpectroscopy([1], frequencies=np.linspace(100, 150, 20) * 1e6) + exp = ResonatorSpectroscopy([1], frequencies=np.linspace(100, 150, 4) * 1e6) loaded_exp = ResonatorSpectroscopy.from_config(exp.config()) self.assertNotEqual(exp, loaded_exp) self.assertEqualExtended(exp, loaded_exp) def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - exp = ResonatorSpectroscopy([1], frequencies=np.linspace(int(100e6), int(150e6), int(20e6))) + exp = ResonatorSpectroscopy([1], frequencies=np.linspace(int(100e6), int(150e6), 4)) self.assertRoundTripSerializable(exp) def test_circuit_roundtrip_serializable(self): @@ -158,7 +158,7 @@ def test_circuit_roundtrip_serializable(self): ), ) res_freq = BackendData(backend).meas_freqs[qubit] - frequencies = np.linspace(res_freq - 20e6, res_freq + 20e6, 51) + frequencies = np.linspace(res_freq - 20e6, res_freq + 20e6, 3) exp = ResonatorSpectroscopy([qubit], backend=backend, frequencies=frequencies) self.assertRoundTripSerializable(exp._transpiled_circuits()) @@ -177,7 +177,7 @@ def test_kerneled_expdata_serialization(self, freq_shift): res_freq = BackendData(backend).meas_freqs[qubit] - frequencies = np.linspace(res_freq - 20e6, res_freq + 20e6, 51) + frequencies = np.linspace(res_freq - 20e6, res_freq + 20e6, 11) exp = ResonatorSpectroscopy([qubit], backend=backend, frequencies=frequencies) expdata = exp.run(backend) @@ -226,8 +226,8 @@ def test_parallel_experiment(self): res_freq1 = backend_data.meas_freqs[qubit1] res_freq2 = backend_data.meas_freqs[qubit2] - frequencies1 = np.linspace(res_freq1 - 20e6, res_freq1 + 20e6, 51) - frequencies2 = np.linspace(res_freq2 - 20e6, res_freq2 + 20e6, 53) + frequencies1 = np.linspace(res_freq1 - 20e6, res_freq1 + 20e6, 11) + frequencies2 = np.linspace(res_freq2 - 20e6, res_freq2 + 20e6, 13) res_spect1 = ResonatorSpectroscopy( [qubit1], backend=parallel_backend, frequencies=frequencies1 diff --git a/test/library/characterization/test_t1.py b/test/library/characterization/test_t1.py index 05a95969b4..7c60e96ddd 100644 --- a/test/library/characterization/test_t1.py +++ b/test/library/characterization/test_t1.py @@ -311,7 +311,7 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - exp = T1([0], [1, 2, 3, 4, 5]) + exp = T1([0], [1, 2]) self.assertRoundTripSerializable(exp) def test_circuit_roundtrip_serializable(self): diff --git a/test/library/characterization/test_t2hahn.py b/test/library/characterization/test_t2hahn.py index 3656689143..8e3d124e48 100644 --- a/test/library/characterization/test_t2hahn.py +++ b/test/library/characterization/test_t2hahn.py @@ -179,7 +179,7 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - delays0 = list(range(1, 60, 2)) + delays0 = list(range(1, 60, 20)) exp = T2Hahn([0], delays0) self.assertRoundTripSerializable(exp) @@ -205,7 +205,7 @@ def test_roundtrip_serializable(self): def test_circuit_roundtrip_serializable(self): """Test round trip JSON serialization""" - delays0 = list(range(1, 60, 2)) + delays0 = list(range(1, 60, 20)) # backend is needed for serialization of the delays in the metadata of the experiment. backend = FakeVigoV2() exp = T2Hahn([0], delays0, backend=backend) diff --git a/test/library/characterization/test_t2ramsey.py b/test/library/characterization/test_t2ramsey.py index e5a90e8a36..599c9a547a 100644 --- a/test/library/characterization/test_t2ramsey.py +++ b/test/library/characterization/test_t2ramsey.py @@ -222,13 +222,13 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - exp = T2Ramsey([0], [1, 2, 3, 4, 5]) + exp = T2Ramsey([0], [1, 2]) self.assertRoundTripSerializable(exp) def test_circuit_roundtrip_serializable(self): """Test round trip JSON serialization""" backend = FakeVigoV2() - exp = T2Ramsey([0], [1, 2, 3, 4, 5], backend=backend) + exp = T2Ramsey([0], [1, 2], backend=backend) self.assertRoundTripSerializable(exp._transpiled_circuits()) def test_analysis_config(self): diff --git a/test/library/quantum_volume/test_qv.py b/test/library/quantum_volume/test_qv.py index b303b742c2..eca6c4ce03 100644 --- a/test/library/quantum_volume/test_qv.py +++ b/test/library/quantum_volume/test_qv.py @@ -275,6 +275,6 @@ def test_roundtrip_serializable(self): def test_circuit_roundtrip_serializable(self): """Test expdata round trip JSON serialization""" - num_of_qubits = 4 + num_of_qubits = 3 qv_exp = QuantumVolume(range(num_of_qubits), seed=SEED) self.assertRoundTripSerializable(qv_exp._transpiled_circuits()) diff --git a/test/library/randomized_benchmarking/test_interleaved_rb.py b/test/library/randomized_benchmarking/test_interleaved_rb.py index a7151495bf..6ed28d0f53 100644 --- a/test/library/randomized_benchmarking/test_interleaved_rb.py +++ b/test/library/randomized_benchmarking/test_interleaved_rb.py @@ -72,14 +72,14 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" exp = rb.InterleavedRB( - interleaved_element=SXGate(), physical_qubits=(0,), lengths=[10, 20, 30], seed=123 + interleaved_element=SXGate(), physical_qubits=(0,), lengths=[1, 3], seed=123 ) self.assertRoundTripSerializable(exp) def test_circuit_roundtrip_serializable(self): """Test circuits round trip JSON serialization""" exp = rb.InterleavedRB( - interleaved_element=SXGate(), physical_qubits=(0,), lengths=[10, 20, 30], seed=123 + interleaved_element=SXGate(), physical_qubits=(0,), lengths=[1, 3], seed=123 ) self.assertRoundTripSerializable(exp._transpiled_circuits()) @@ -276,6 +276,7 @@ def test_interleaving_cnot_gate_with_non_supported_direction(self): lengths=[3], num_samples=4, backend=my_backend, + seed=1234, ) transpiled = exp._transpiled_circuits() for qc in transpiled: diff --git a/test/library/randomized_benchmarking/test_standard_rb.py b/test/library/randomized_benchmarking/test_standard_rb.py index 7990cbaa09..6370321a2e 100644 --- a/test/library/randomized_benchmarking/test_standard_rb.py +++ b/test/library/randomized_benchmarking/test_standard_rb.py @@ -57,12 +57,12 @@ def test_experiment_config(self): def test_roundtrip_serializable(self): """Test round trip JSON serialization""" - exp = rb.StandardRB(physical_qubits=(0,), lengths=[10, 20, 30], seed=123) + exp = rb.StandardRB(physical_qubits=(0,), lengths=[1, 3], seed=123) self.assertRoundTripSerializable(exp) def test_circuit_roundtrip_serializable(self): """Test circuits round trip JSON serialization""" - exp = rb.StandardRB(physical_qubits=(0,), lengths=[10, 20, 30], seed=123) + exp = rb.StandardRB(physical_qubits=(0,), lengths=[1, 3], seed=123) self.assertRoundTripSerializable(exp._transpiled_circuits()) def test_analysis_config(self): diff --git a/test/library/tomography/test_process_tomography.py b/test/library/tomography/test_process_tomography.py index 27593a9fcb..17bed174a5 100644 --- a/test/library/tomography/test_process_tomography.py +++ b/test/library/tomography/test_process_tomography.py @@ -103,7 +103,7 @@ def test_circuit_roundtrip_serializable(self): circ.s(0) circ.cx(0, 1) - exp = ProcessTomography(circ) + exp = ProcessTomography(circ, preparation_indices=[0], measurement_indices=[0]) self.assertRoundTripSerializable(exp._transpiled_circuits()) def test_cvxpy_gaussian_lstsq_cx(self): diff --git a/tox.ini b/tox.ini index be44ae4f6a..70be5b521e 100644 --- a/tox.ini +++ b/tox.ini @@ -104,4 +104,4 @@ commands = skip_install = true deps = allowlist_externals = rm -commands = rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build +commands = rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build \ No newline at end of file From b27f33cc667a157a8f0a6a01f99f5215d1a14404 Mon Sep 17 00:00:00 2001 From: Naoki Kanazawa Date: Tue, 19 Sep 2023 22:32:34 +0900 Subject: [PATCH 6/7] Replace deprecated mpl function in visualization test (#1274) ### Summary All PRs in Qiskit Experiments are currently failing in tests due to `TestPlotterAndMplDrawer.test_series_names_different_types` which includes deprecated functions in the recently updated matplotlib package. ### Details and comments No release note is included because this is update for unittest. --- test/visualization/test_plotter_mpldrawer.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/visualization/test_plotter_mpldrawer.py b/test/visualization/test_plotter_mpldrawer.py index b39f9fcd4d..fc5799535a 100644 --- a/test/visualization/test_plotter_mpldrawer.py +++ b/test/visualization/test_plotter_mpldrawer.py @@ -118,9 +118,10 @@ def test_series_names_different_types(self, series_names: Dict[type, List[Any]]) """ # Create Matplotlib axes that use a PNG backend. The default backend, FigureCanvasSVG, does not - # have `tostring_rgb()` which is needed to compute the difference between two figures in this + # have `buffer_rgba()` which is needed to compute the difference between two figures in this # method. We need to set the axes as MplDrawer will use # `qiskit_experiments.framework.matplotlib.get_non_gui_ax` by default; which uses an SVG backend. + plt.close("all") plt.switch_backend("Agg") axes = {} for key in series_names.keys(): @@ -161,16 +162,12 @@ def test_series_names_different_types(self, series_names: Dict[type, List[Any]]) for plot_type in legend_plot_types: plotter.enable_legend_for(series_name, plot_type) - # Generate figure and save to buffers for comparison. This requires a pixel backend, like AGG, so - # that `tostring_rgb()` is available. + # Generate figure and save to buffers for comparison. figure_data = {} for plotter_type, plotter in plotters.items(): figure = plotter.figure().figure figure.canvas.draw() - figure_data[plotter_type] = np.frombuffer( - figure.canvas.tostring_rgb(), - dtype=np.uint8, - ).reshape(figure.canvas.get_width_height() + (3,)) + figure_data[plotter_type] = np.asarray(figure.canvas.buffer_rgba(), dtype=np.uint8) # Compare root-mean-squared error between two images. for (fig1_type, fig1), (fig2_type, fig2) in combinations(figure_data.items(), 2): From c8362f3e3919220bc7ea1836e148894d1032dda3 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Tue, 19 Sep 2023 20:53:13 -0400 Subject: [PATCH 7/7] Update CI tests (#1273) This PR fixes a deprecation error from Matplotlib since version 3.8: https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.8.0.html#auto-closing-of-figures-when-switching-backend It also removes the intermediate python versions from the daily cron jobs. --------- Co-authored-by: Will Shanks --- .github/workflows/cron-staging.yml | 2 +- test/visualization/test_plotter_mpldrawer.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cron-staging.yml b/.github/workflows/cron-staging.yml index 3176c910cb..27bfad72cd 100644 --- a/.github/workflows/cron-staging.yml +++ b/.github/workflows/cron-staging.yml @@ -10,7 +10,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, "3.11"] os: ["ubuntu-latest", "macOS-latest", "windows-latest"] steps: - name: Print Concurrency Group diff --git a/test/visualization/test_plotter_mpldrawer.py b/test/visualization/test_plotter_mpldrawer.py index fc5799535a..9e795b3265 100644 --- a/test/visualization/test_plotter_mpldrawer.py +++ b/test/visualization/test_plotter_mpldrawer.py @@ -32,6 +32,11 @@ class TestPlotterAndMplDrawer(QiskitExperimentsTestCase): """Test generic plotter with Matplotlib drawer.""" + def tearDown(self): + """Clean up test case state""" + plt.close("all") + super().tearDown() + def test_end_to_end_short(self): """Test whether plotter with MplDrawer returns a figure.""" plotter = MockPlotter(MplDrawer()) @@ -121,7 +126,6 @@ def test_series_names_different_types(self, series_names: Dict[type, List[Any]]) # have `buffer_rgba()` which is needed to compute the difference between two figures in this # method. We need to set the axes as MplDrawer will use # `qiskit_experiments.framework.matplotlib.get_non_gui_ax` by default; which uses an SVG backend. - plt.close("all") plt.switch_backend("Agg") axes = {} for key in series_names.keys():