From 05589845a8bd0139105189330758f9e1acf08860 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Tue, 28 Mar 2023 18:35:48 -0400 Subject: [PATCH 1/6] drop 3.7 support and upgrade numpy to 1.22 also started using Literal types and updated tomography analysis --- .github/workflows/main.yml | 2 +- docs/tutorials/getting_started.rst | 4 ++-- qiskit_experiments/curve_analysis/base_curve_analysis.py | 9 +++++---- qiskit_experiments/library/tomography/qpt_analysis.py | 3 ++- qiskit_experiments/library/tomography/qst_analysis.py | 2 +- .../library/tomography/tomography_analysis.py | 8 +------- .../notes/drop_python_3_7_support-0529a7122e94b004.yaml | 4 ++++ requirements.txt | 2 +- setup.py | 3 +-- 9 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 09a46c01e0..2163a74ac4 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.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11"] os: ["ubuntu-latest", "macOS-latest", "windows-latest"] steps: - name: Print Concurrency Group diff --git a/docs/tutorials/getting_started.rst b/docs/tutorials/getting_started.rst index c5ec7c5eec..f9b9ebb354 100644 --- a/docs/tutorials/getting_started.rst +++ b/docs/tutorials/getting_started.rst @@ -7,8 +7,8 @@ Installation Qiskit Experiments is built on top of Qiskit, so we recommend that you first install Qiskit following its :external+qiskit:doc:`installation guide `. Qiskit -Experiments supports the same platforms and Python versions (currently **3.7+**) as -Qiskit itself. +Experiments supports the same platforms as Qiskit itself and Python versions 3.8, +3.9, 3.10, and 3.11. Qiskit Experiments releases can be installed via the Python package manager ``pip``: diff --git a/qiskit_experiments/curve_analysis/base_curve_analysis.py b/qiskit_experiments/curve_analysis/base_curve_analysis.py index 5dabe47b1d..faf9481b6c 100644 --- a/qiskit_experiments/curve_analysis/base_curve_analysis.py +++ b/qiskit_experiments/curve_analysis/base_curve_analysis.py @@ -162,10 +162,11 @@ def _default_options(cls) -> Options: instance that defines the `self.__call__` method. normalization (bool): Set ``True`` to normalize y values within range [-1, 1]. Default to ``False``. - average_method (str): Method to average the y values when the same x values - appear multiple times. One of "sample", "iwv" (i.e. inverse weighted variance), - "shots_weighted". See :func:`.mean_xy_data` for details. Default to - "shots_weighted". + average_method (Literal["sample", "iwv", "shots_weighted"]): Method + to average the y values when the same x values + appear multiple times. One of "sample", "iwv" (i.e. inverse + weighted variance), "shots_weighted". See :func:`.mean_xy_data` + for details. Default to "shots_weighted". p0 (Dict[str, float]): Initial guesses for the fit parameters. The dictionary is keyed on the fit parameter names. bounds (Dict[str, Tuple[float, float]]): Boundary of fit parameters. diff --git a/qiskit_experiments/library/tomography/qpt_analysis.py b/qiskit_experiments/library/tomography/qpt_analysis.py index 408927d651..7101866c90 100644 --- a/qiskit_experiments/library/tomography/qpt_analysis.py +++ b/qiskit_experiments/library/tomography/qpt_analysis.py @@ -92,7 +92,8 @@ def _default_options(cls) -> Options: preparation_qubits (Sequence[int]): Optional, the physical qubits with tomographic preparations. If not specified will be set to ``[0, ..., N-1]`` for N-qubit tomographic preparations. - This can be a string to select one of the built-in fitters, or a callable to + fitter (str or Callable): The fitter function to use for reconstruction. + This can be a string to select one of the built-in fitters, or a callable to supply a custom fitter function. See the `Fitter Functions` section for additional information. target (str or diff --git a/qiskit_experiments/library/tomography/qst_analysis.py b/qiskit_experiments/library/tomography/qst_analysis.py index f036a47ec1..1e6cf6d000 100644 --- a/qiskit_experiments/library/tomography/qst_analysis.py +++ b/qiskit_experiments/library/tomography/qst_analysis.py @@ -70,7 +70,7 @@ def _default_options(cls) -> Options: :class:`~qiskit_experiments.library.tomography.basis.MeasurementBasis` to use for tomographic state reconstruction. fitter (str or Callable): The fitter function to use for reconstruction. - This can be a string to select one of the built-in fitters, or a callable to + This can be a string to select one of the built-in fitters, or a callable to supply a custom fitter function. See the `Fitter Functions` section for additional information. fitter_options (dict): Any addition kwarg options to be supplied to the fitter diff --git a/qiskit_experiments/library/tomography/tomography_analysis.py b/qiskit_experiments/library/tomography/tomography_analysis.py index 7f8ce930c8..82cdbe2b2c 100644 --- a/qiskit_experiments/library/tomography/tomography_analysis.py +++ b/qiskit_experiments/library/tomography/tomography_analysis.py @@ -326,13 +326,7 @@ def _fidelity_result( prob_data = outcome_data / shot_data[None, :, None] bs_fidelities = [] for _ in range(self.options.target_bootstrap_samples): - # Once python 3.7 support is dropped and minimum NumPy - # version can be set to 1.22 this can be replaced with - # `sampled_data = rng.multinomial(shot_data, probs)` - sampled_data = np.zeros_like(outcome_data) - for i in range(prob_data.shape[0]): - for j in range(prob_data.shape[1]): - sampled_data[i, j] = rng.multinomial(shot_data[j], prob_data[i, j]) + sampled_data = rng.multinomial(shot_data, prob_data) try: state_results = self._fit_state_results( diff --git a/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml b/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml new file mode 100644 index 0000000000..e7f16047f2 --- /dev/null +++ b/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Dropped support for Python 3.7 and upgraded NumPy to 1.22. diff --git a/requirements.txt b/requirements.txt index 32238bc6c1..c70ac444b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy>=1.17 +numpy>=1.22 scipy>=1.4 qiskit-terra>=0.22 qiskit-ibm-experiment>=0.2.5 diff --git a/setup.py b/setup.py index ef9e8465d2..561c211b11 100755 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ "Operating System :: MacOS", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -61,7 +60,7 @@ packages=find_packages(exclude=["test*"]), install_requires=REQUIREMENTS, include_package_data=True, - python_requires=">=3.7", + python_requires=">=3.8", project_urls={ "Bug Tracker": "https://github.com/Qiskit/qiskit-experiments/issues", "Documentation": "https://qiskit.org/documentation/", From fdd16f740173b4c6319ae840fe61d0f2c62e30c0 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Tue, 28 Mar 2023 22:15:34 -0400 Subject: [PATCH 2/6] remove pins and update test, rb --- .../library/randomized_benchmarking/standard_rb.py | 6 +----- requirements-dev.txt | 5 +---- test/visualization/test_utils.py | 6 +++--- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py index 4f1c0c6dea..a81b04fe05 100644 --- a/qiskit_experiments/library/randomized_benchmarking/standard_rb.py +++ b/qiskit_experiments/library/randomized_benchmarking/standard_rb.py @@ -383,11 +383,7 @@ def _transpiled_circuits(self) -> List[QuantumCircuit]: inst_prop = self.backend.target[op_name].get(qargs, None) if inst_prop is None: continue - try: - schedule = inst_prop.calibration - except AttributeError: - # TODO remove after qiskit-terra/#9681 is in stable release. - schedule = None + schedule = inst_prop.calibration if schedule is None: continue publisher = schedule.metadata.get("publisher", CalibrationPublisher.QISKIT) diff --git a/requirements-dev.txt b/requirements-dev.txt index ee5c55fb3e..c3bc3c1ea5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -17,12 +17,9 @@ qiskit-aer>=0.11.0 pandas>=1.1.5 cvxpy>=1.1.15 pylatexenc -# Pin `importlib-metadata` because of a bug relating to version 5.0.0. See #931 for more. -importlib-metadata==4.13.0;python_version<'3.8' scikit-learn sphinx-copybutton # Pin versions below because of build errors ipykernel<=6.21.3 jupyter-client<=8.0.3 -# Pin symengine because there are no wheels for 0.10 for Python 3.7 -symengine<=0.9.2;python_version<'3.8' + diff --git a/test/visualization/test_utils.py b/test/visualization/test_utils.py index d451bdbed8..66b814fc77 100644 --- a/test/visualization/test_utils.py +++ b/test/visualization/test_utils.py @@ -42,14 +42,14 @@ def _dummy_data( ] # Iterate over pairs of adjacent bin edges, which define the maximum and minimum for the region. - # This is done by zipping over shifted subarrays of bin_edges as follows: + # This is done by generating sliding windows of bin_edges as follows: # [[a], [b], [c], [d], [e], [f]], g] # [a, [[b], [c], [d], [e], [f], [g]] # The result is a list of pairs representing a moving window of size 2. - # TODO: Replace this with numpy.[...].sliding_window_view when Qiskit requires numpy>=0.20.0 + dummy_data = [] for (x_min, x_max), (y_min, y_max) in it.product( - *tuple(list(zip(b[0:-1], b[1:])) for b in bin_edges) + *np.lib.stride_tricks.sliding_window_view(bin_edges, 2, 1) ): _dummy_data = np.asarray( [ From 9e700a43783c200d99251338b829c2f7720fb099 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Wed, 29 Mar 2023 22:59:18 -0400 Subject: [PATCH 3/6] reverted numpy upgrade and added version check --- qiskit_experiments/framework/__init__.py | 1 + qiskit_experiments/framework/package_deps.py | 21 +++++++++++ .../library/tomography/tomography_analysis.py | 11 ++++-- ...p_python_3_7_support-0529a7122e94b004.yaml | 2 +- requirements.txt | 2 +- test/visualization/test_utils.py | 35 +++++++++++++------ 6 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 qiskit_experiments/framework/package_deps.py diff --git a/qiskit_experiments/framework/__init__.py b/qiskit_experiments/framework/__init__.py index 05f047b9a9..cb25986815 100644 --- a/qiskit_experiments/framework/__init__.py +++ b/qiskit_experiments/framework/__init__.py @@ -146,3 +146,4 @@ ) from .json import ExperimentEncoder, ExperimentDecoder from .restless_mixin import RestlessMixin +from .package_deps import numpy_version diff --git a/qiskit_experiments/framework/package_deps.py b/qiskit_experiments/framework/package_deps.py new file mode 100644 index 0000000000..a814eb4d8f --- /dev/null +++ b/qiskit_experiments/framework/package_deps.py @@ -0,0 +1,21 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Functions for checking dependency versions. +""" + +from importlib.metadata import version + + +def numpy_version(): + """Returns the current numpy version in (major, minor) form.""" + return tuple(map(int, version("numpy").split(".")[:2])) diff --git a/qiskit_experiments/library/tomography/tomography_analysis.py b/qiskit_experiments/library/tomography/tomography_analysis.py index 82cdbe2b2c..409d7653a3 100644 --- a/qiskit_experiments/library/tomography/tomography_analysis.py +++ b/qiskit_experiments/library/tomography/tomography_analysis.py @@ -26,7 +26,7 @@ from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel from qiskit_experiments.exceptions import AnalysisError -from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData, Options +from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData, Options, numpy_version from .fitters import ( tomography_fitter_data, postprocess_fitter, @@ -326,7 +326,14 @@ def _fidelity_result( prob_data = outcome_data / shot_data[None, :, None] bs_fidelities = [] for _ in range(self.options.target_bootstrap_samples): - sampled_data = rng.multinomial(shot_data, prob_data) + # TODO: remove conditional once numpy is pinned at 1.22 and above + if numpy_version() >= (1, 22): + sampled_data = rng.multinomial(shot_data, prob_data) + else: + sampled_data = np.zeros_like(outcome_data) + for i in range(prob_data.shape[0]): + for j in range(prob_data.shape[1]): + sampled_data[i, j] = rng.multinomial(shot_data[j], prob_data[i, j]) try: state_results = self._fit_state_results( diff --git a/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml b/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml index e7f16047f2..26b6c1bf82 100644 --- a/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml +++ b/releasenotes/notes/drop_python_3_7_support-0529a7122e94b004.yaml @@ -1,4 +1,4 @@ --- upgrade: - | - Dropped support for Python 3.7 and upgraded NumPy to 1.22. + Dropped support for Python 3.7. diff --git a/requirements.txt b/requirements.txt index c70ac444b2..32238bc6c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy>=1.22 +numpy>=1.17 scipy>=1.4 qiskit-terra>=0.22 qiskit-ibm-experiment>=0.2.5 diff --git a/test/visualization/test_utils.py b/test/visualization/test_utils.py index 66b814fc77..9e8a593f52 100644 --- a/test/visualization/test_utils.py +++ b/test/visualization/test_utils.py @@ -22,6 +22,7 @@ from qiskit.exceptions import QiskitError from qiskit_experiments.visualization.utils import DataExtentCalculator +from qiskit_experiments.framework.package_deps import numpy_version @ddt @@ -46,18 +47,30 @@ def _dummy_data( # [[a], [b], [c], [d], [e], [f]], g] # [a, [[b], [c], [d], [e], [f], [g]] # The result is a list of pairs representing a moving window of size 2. - + # TODO: remove the old code once numpy is above 1.20. dummy_data = [] - for (x_min, x_max), (y_min, y_max) in it.product( - *np.lib.stride_tricks.sliding_window_view(bin_edges, 2, 1) - ): - _dummy_data = np.asarray( - [ - np.linspace(x_min, x_max, n_points), - np.linspace(y_min, y_max, n_points), - ] - ) - dummy_data.append(_dummy_data.swapaxes(-1, -2)) + if numpy_version() >= (1, 20): + for (x_min, x_max), (y_min, y_max) in it.product( + *np.lib.stride_tricks.sliding_window_view(bin_edges, 2, 1) + ): + _dummy_data = np.asarray( + [ + np.linspace(x_min, x_max, n_points), + np.linspace(y_min, y_max, n_points), + ] + ) + dummy_data.append(_dummy_data.swapaxes(-1, -2)) + else: + for (x_min, x_max), (y_min, y_max) in it.product( + *tuple(list(zip(b[0:-1], b[1:])) for b in bin_edges) + ): + _dummy_data = np.asarray( + [ + np.linspace(x_min, x_max, n_points), + np.linspace(y_min, y_max, n_points), + ] + ) + dummy_data.append(_dummy_data.swapaxes(-1, -2)) return dummy_data @data(*list(it.product([1.0, 1.1, 2.0], [None, 1.0, np.sqrt(2)]))) From 6d88ef3c5f94c0f497a1d11f132e3b21a2ff91c2 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Thu, 30 Mar 2023 15:54:04 -0400 Subject: [PATCH 4/6] remove 3.7 from cron tests --- .github/workflows/cron-staging.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cron-staging.yml b/.github/workflows/cron-staging.yml index 587439bd7e..45eaecbf5c 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.7, 3.8, 3.9, "3.10", "3.11"] + python-version: [3.8, 3.9, "3.10", "3.11"] os: ["ubuntu-latest", "macOS-latest", "windows-latest"] steps: - name: Print Concurrency Group From 64d94366652ec2706fb2d4778602d05a6f1a3511 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Fri, 31 Mar 2023 22:55:46 -0400 Subject: [PATCH 5/6] missed inclusions of 3.7 --- pyproject.toml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 20c8c3625a..a617450e70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,4 @@ build-backend = "setuptools.build_meta" [tool.black] line-length = 100 -target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] +target-version = ['py38', 'py39', 'py310', 'py311'] diff --git a/tox.ini b/tox.ini index 427fba47d7..bcea06c803 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 3.3.0 -envlist = py311,py310,py39,py38,py37,lint +envlist = py311,py310,py39,py38,lint isolated_build = true [testenv] From 8d7a6632f35d7e5274d01c316c378d3bdfde8592 Mon Sep 17 00:00:00 2001 From: Helena Zhang Date: Tue, 11 Apr 2023 14:20:47 -0400 Subject: [PATCH 6/6] added python version badge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5e546b5855..cf682454ac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Qiskit Experiments [![License](https://img.shields.io/github/license/Qiskit/qiskit-experiments.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0) +[![Release](https://img.shields.io/github/release/Qiskit/qiskit-experiments.svg?style=popout-square)](https://github.com/Qiskit/qiskit-experiments/releases) +![Python](https://img.shields.io/pypi/pyversions/qiskit-experiments.svg?style=popout-square) + **Qiskit Experiments** is a repository that builds tools for building, running, and analyzing experiments on noisy quantum computers using Qiskit.