diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c0e28d98..0144b207 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,6 +31,7 @@ concurrency: jobs: Checks: + if: github.repository_owner == 'qiskit-community' runs-on: ${{ matrix.os }} strategy: matrix: @@ -66,6 +67,8 @@ jobs: run: | sudo apt-get -y install pandoc graphviz python3-enchant hunspell-en-us pip install pyenchant + # append to reno config + echo "earliest_version: 0.1.0" >> releasenotes/config.yaml shell: bash - run: pip check if: ${{ !cancelled() }} @@ -75,6 +78,9 @@ jobs: python tools/check_copyright.py -check if: ${{ !cancelled() }} shell: bash + - run: make spell + if: ${{ !cancelled() }} + shell: bash - name: Style Check run: | make style @@ -99,6 +105,7 @@ jobs: if: ${{ !cancelled() }} shell: bash Algorithms: + if: github.repository_owner == 'qiskit-community' runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -140,10 +147,14 @@ jobs: run-slow: ${{ contains(github.event.pull_request.labels.*.name, 'run_slow') }} python-version: ${{ matrix.python-version }} if: ${{ !cancelled() }} + - name: Deprecation Messages + run: | + mkdir ./ci-artifact-data + python tools/extract_deprecation.py -file out.txt -output ./ci-artifact-data/alg.dep + shell: bash - name: Coverage combine run: | coverage3 combine - mkdir ./ci-artifact-data mv .coverage ./ci-artifact-data/alg.dat if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.8 }} shell: bash @@ -152,12 +163,13 @@ jobs: name: ${{ matrix.os }}-${{ matrix.python-version }} path: ./ci-artifact-data/* Tutorials: + if: github.repository_owner == 'qiskit-community' runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ ubuntu-latest ] - python-version: [ 3.8, 3.11 ] + os: [ubuntu-latest] + python-version: [3.8, 3.11] steps: - uses: actions/checkout@v3 with: @@ -176,19 +188,17 @@ jobs: python-version: ${{ matrix.python-version }} if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }} - uses: ./.github/actions/install-algorithms - with: - os: ${{ matrix.os }} - name: Install Dependencies run: | pip install jupyter qiskit-terra[visualization] sudo apt-get install -y pandoc graphviz + echo "earliest_version: 0.1.0" >> releasenotes/config.yaml shell: bash - name: Run Qiskit Algorithms Tutorials env: QISKIT_PARALLEL: False QISKIT_DOCS_BUILD_TUTORIALS: 'always' run: | - tools/ignore_untagged_notes.sh make html cd docs/_build/html mkdir artifacts @@ -200,7 +210,8 @@ jobs: name: tutorials${{ matrix.python-version }} path: docs/_build/html/artifacts/tutorials.tar.gz Deprecation_Messages_and_Coverage: - needs: [Checks, Algorithms] + if: github.repository_owner == 'qiskit-community' + needs: [Checks, Algorithms, Tutorials] runs-on: ubuntu-latest strategy: matrix: @@ -214,9 +225,41 @@ jobs: with: name: ubuntu-latest-3.8 path: /tmp/u38 + - uses: actions/download-artifact@v3 + with: + name: ubuntu-latest-3.9 + path: /tmp/u39 + - uses: actions/download-artifact@v3 + with: + name: ubuntu-latest-3.10 + path: /tmp/u310 + - uses: actions/download-artifact@v3 + with: + name: ubuntu-latest-3.11 + path: /tmp/u311 + - uses: actions/download-artifact@v3 + with: + name: macos-latest-3.8 + path: /tmp/m38 + - uses: actions/download-artifact@v3 + with: + name: macos-latest-3.11 + path: /tmp/m311 + - uses: actions/download-artifact@v3 + with: + name: windows-2019-3.8 + path: /tmp/w38 + - uses: actions/download-artifact@v3 + with: + name: windows-2019-3.11 + path: /tmp/w311 - name: Install Dependencies run: pip install -U coverage coveralls diff-cover shell: bash + - name: Combined Deprecation Messages + run: | + sort -f -u /tmp/u38/alg.dep /tmp/u39/alg.dep /tmp/u310/alg.dep /tmp/u311/alg.dep /tmp/m38/alg.dep /tmp/m311/alg.dep /tmp/w38/alg.dep /tmp/w311/alg.dep || true + shell: bash - name: Coverage combine run: coverage3 combine /tmp/u38/alg.dat shell: bash diff --git a/.pylintdict b/.pylintdict index e69de29b..86e8d542 100644 --- a/.pylintdict +++ b/.pylintdict @@ -0,0 +1,370 @@ +adam +ae +aer +al +algo +algorithmerror +allclose +amsgrad +ansatz +ansatzes +ansatz's +ap +apl +arg +argmax +args +armijo +arxiv +autosummary +ba +backend +backends +barison +barkoutsos +batchsize +bergholm +bfgs +bielza +bitstring +bitstrings +boltzmann +bool +boyer +brassard +broyden +callables +cambridge +cancelled +carleo +carlo +chernoff +chuang +clopper +cobyla +codebase +coeffs +combinatorial +concha +configs +confint +córcoles +crs +currentmodule +customizable +cvar +dataclass +dataframe +deriv +dicts +diederik +dimensionality +disp +dobsicek +docstring +doi +dp +dt +dω +eda +egger +eigen +eigenphase +eigenproblem +eigensolver +eigensolvers +eigenstate +eigenstates +entangler +enum +eps +et +euler +eval +evals +evolutions +evolutionsynthesis +evolver +evolvers +excitations +exponentiated +exponentiating +fae +failsafe +farhi +fi +fidelities +filippo +fletcher +fm +fmin +formatter +fourier +frac +f'spsa +ftol +fujii +func +functools +gacon +gambetta +gaussian +generalised +getter +getters +giuseppe +globals +gogolin +goldfarb +goldstone +gqt +grinko +grover +gsls +gtol +gutmann +hadamard +hadfield +hamiltonian +hamiltonians +havlíček +hessians +hilbert +hopkins +hoyer +https +hyperparameters +idx +im +imag +initializer +inplace +interatomic +ints +iprint +iqft +ising +iteratively +iz +izaac +izz +jac +jacobian +jones +july +kandala +killoran +kingma +kitagawa +kraft +kth +kumar +kwarg +kwargs +langle +larrañaga +lcu +len +leq +lin +linalg +loglik +loglikelihood +lr +lsb +lse +marecek +mathcal +mathrm +maxcut +maxfail +maxfev +maxfun +maxiter +maxiters +maxmp +mcz +michael +minimised +minimizer +minwidth +mitarai +mle +modelspace +monte +mosca +mprev +nabla +nakaji +nakanishi +nan +nannicini +nathan +ndarray +negoro +nelder +nevals +newtons's +nfev +nfevs +nft +nielsen +njev +nlopt +nones +nosignatures +np +num +numpy +objval +observables +oct +onodera +optimizer's +optimizers +otimes +o'brien +param +parameterizations +parametrization +parametrizations +parametrize +parametrized +params +pauli +paulis +pearson +pedro +peruzzo +polyfit +postprocess +powell +pre +preconditioner +preprint +preprocess +preprocesses +priori +proj +pvqd +pxd +py +qae +qaoa +qasm +qdrift +qfi +qfis +qfi's +qgt +qgts +qgt's +qiskit +qiskit's +qn +qnspsa +qpe +quadratically +quant +quantile +qubit +qubits +rangle +raymond +reddi +representable +resamplings +rescale +rescaled +rescaling +retval +rhobeg +rightarrow +robert +rosen +runarsson +rz +sanjiv +sashank +satyen +schrödinger +schroediger +schroedinger +schuld +scikit +scipy +sdg +seealso +shanno +skquant +sle +slsqp +soloviev +spall +spedalieri +spsa +sqrt +statefn +statevector +statevectors +stddev +stdout +stefano +steppable +stepsize +str +subcircuits +subclasses +subcomponents +subspaces +suzuki +swappable +tanaka +tapp +tarasinski +tavernelli +temme +terhal +terra +th +timestep +timesteps +tnc +toctree +todo +tol +trainability +transpile +transpiled +trotterization +trotterized +uncompute +unitaries +univariate +uno +unscaled +utils +variational +vec +vectorized +vicentini +ville +vqd +vqe +wavefunction +woerner +wrt +xatol +xopt +xtol +yamamoto +yao +york +yy +yz +zi +zoufal +zz +θ +ψ +ω diff --git a/Makefile b/Makefile index 06d21f82..2ace92c1 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ test_ci: stestr run --concurrency $(CONCURRENCY) spell: - pylint -rn --disable=all --enable=spelling --spelling-dict=en_US --spelling-private-dict-file=.pylintdict qiskit_algorithms test tools + pylint -rn --disable=all --enable=spelling --spelling-dict=en_US qiskit_algorithms test tools sphinx-build -M spelling docs docs/_build -W -T --keep-going $(SPHINXOPTS) copyright: @@ -79,4 +79,4 @@ coverage: coverage_erase: coverage erase -clean: coverage_erase; +clean: clean_sphinx coverage_erase; diff --git a/docs/Makefile b/docs/Makefile index e87c8596..b2fb2705 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -15,14 +15,22 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build +STUBSDIR = stubs # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +spell: + @$(SPHINXBUILD) -M spelling "$(SOURCEDIR)" "$(BUILDDIR)" -W -T --keep-going $(SPHINXOPTS) $(O) + +clean: + rm -rf $(BUILDDIR) + rm -rf $(STUBSDIR) + +.PHONY: help spell clean Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" -W -T --keep-going $(SPHINXOPTS) $(O) diff --git a/docs/apidocs/qiskit_algorithms.utils.algorithm_globals.rst b/docs/apidocs/qiskit_algorithms.utils.algorithm_globals.rst new file mode 100644 index 00000000..ec663402 --- /dev/null +++ b/docs/apidocs/qiskit_algorithms.utils.algorithm_globals.rst @@ -0,0 +1,6 @@ +.. _qiskit_algorithms.utils.algorithm_globals: + +.. automodule:: qiskit_algorithms.utils.algorithm_globals + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/docs/conf.py b/docs/conf.py index b7bb39b8..10cde338 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -91,6 +91,9 @@ "**": "_static/images/logo.png", } +spelling_word_list_filename = "../.pylintdict" +spelling_filters = ["lowercase_filter.LowercaseFilter"] + templates_path = ["_templates"] # Number figures, tables and code-blocks if they have a caption. diff --git a/docs/lowercase_filter.py b/docs/lowercase_filter.py new file mode 100644 index 00000000..e7b3d2f2 --- /dev/null +++ b/docs/lowercase_filter.py @@ -0,0 +1,29 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2021, 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. + +""" Implements a Lower Case Filter for Sphinx spelling """ + +from enchant import tokenize + + +class LowercaseFilter(tokenize.Filter): + """Lower Case Filter""" + + def _split(self, word): + """Filter method for sub-tokenization of tokens. + + This method must be a tokenization function that will split the + given word into sub-tokens according to the needs of the filter. + The default behavior is not to split any words. + """ + # Don't split, just lower case to test against lowercase dict + return super()._split(word.lower()) diff --git a/pyproject.toml b/pyproject.toml index d1ffad6c..2114abe3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,4 +73,5 @@ enable = [ ] [tool.pylint.spelling] -spelling-private-dict-file = ".local-spellings" +spelling-private-dict-file = ".pylintdict" +spelling-store-unknown-words = "n" diff --git a/qiskit_algorithms/__init__.py b/qiskit_algorithms/__init__.py index 99f8ff2b..adce55b4 100644 --- a/qiskit_algorithms/__init__.py +++ b/qiskit_algorithms/__init__.py @@ -240,7 +240,7 @@ Utility classes --------------- -Utility classes used by algorithms (mainly for type-hinting purposes). +Utility classes and function used by algorithms. .. autosummary:: :toctree: ../stubs/ @@ -248,6 +248,11 @@ AlgorithmJob +.. autosummary:: + :toctree: + + utils.algorithm_globals + """ from .algorithm_job import AlgorithmJob from .algorithm_result import AlgorithmResult diff --git a/qiskit_algorithms/amplitude_amplifiers/grover.py b/qiskit_algorithms/amplitude_amplifiers/grover.py index 4eb458f2..ce2db8a9 100644 --- a/qiskit_algorithms/amplitude_amplifiers/grover.py +++ b/qiskit_algorithms/amplitude_amplifiers/grover.py @@ -22,9 +22,9 @@ from qiskit import ClassicalRegister, QuantumCircuit from qiskit.primitives import BaseSampler from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals from qiskit_algorithms.exceptions import AlgorithmError +from qiskit_algorithms.utils import algorithm_globals from .amplification_problem import AmplificationProblem from .amplitude_amplifier import AmplitudeAmplifier, AmplitudeAmplifierResult diff --git a/qiskit_algorithms/amplitude_estimators/ae.py b/qiskit_algorithms/amplitude_estimators/ae.py index e6f96207..836ae753 100644 --- a/qiskit_algorithms/amplitude_estimators/ae.py +++ b/qiskit_algorithms/amplitude_estimators/ae.py @@ -48,7 +48,7 @@ class AmplitudeEstimation(AmplitudeEstimator): .. note:: This class does not support the :attr:`.EstimationProblem.is_good_state` property, - as for phase estimation-based QAE, the oracle that identifes the good states + as for phase estimation-based QAE, the oracle that identifies the good states must be encoded in the Grover operator. To set custom oracles, the :attr:`.EstimationProblem.grover_operator` attribute can be set directly. @@ -159,7 +159,7 @@ def evaluate_measurements( """Evaluate the results from the circuit simulation. Given the probabilities from statevector simulation of the QAE circuit, compute the - probabilities that the measurements y/gridpoints a are the best estimate. + probabilities that the measurements y/grid-points a are the best estimate. Args: circuit_results: The circuit result from the QAE circuit. Can be either a counts dict @@ -167,7 +167,7 @@ def evaluate_measurements( threshold: Measurements with probabilities below the threshold are discarded. Returns: - Dictionaries containing the a gridpoints with respective probabilities and + Dictionaries containing the a grid-points with respective probabilities and y measurements with respective probabilities, in this order. """ # compute grid sample and measurement dicts @@ -276,7 +276,7 @@ def loglikelihood(a): right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 bubbles = [left_of_qae, qae, right_of_qae] - # Find global maximum amongst the two local maxima + # Find global maximum among the two local maxima a_opt = qae loglik_opt = loglikelihood(a_opt) for a, b in zip(bubbles[:-1], bubbles[1:]): diff --git a/qiskit_algorithms/amplitude_estimators/estimation_problem.py b/qiskit_algorithms/amplitude_estimators/estimation_problem.py index ecf0f001..7b0c3d76 100644 --- a/qiskit_algorithms/amplitude_estimators/estimation_problem.py +++ b/qiskit_algorithms/amplitude_estimators/estimation_problem.py @@ -171,8 +171,8 @@ def grover_operator(self) -> QuantumCircuit | None: return self._grover_operator # build the reflection about the bad state: a MCZ with open controls (thus X gates - # around the controls) and X gates around the target to change from a phaseflip on - # |1> to a phaseflip on |0> + # around the controls) and X gates around the target to change from a phase flip on + # |1> to a phase flip on |0> num_state_qubits = self.state_preparation.num_qubits - self.state_preparation.num_ancillas oracle = QuantumCircuit(num_state_qubits) diff --git a/qiskit_algorithms/amplitude_estimators/mlae.py b/qiskit_algorithms/amplitude_estimators/mlae.py index 4fb9fd18..a49a8508 100644 --- a/qiskit_algorithms/amplitude_estimators/mlae.py +++ b/qiskit_algorithms/amplitude_estimators/mlae.py @@ -225,7 +225,7 @@ def compute_mle( ) -> float | tuple[float, list[float]]: """Compute the MLE via a grid-search. - This is a stable approach if sufficient gridpoints are used. + This is a stable approach if sufficient grid-points are used. Args: circuit_results: A list of circuit outcomes. Can be counts or statevectors. diff --git a/qiskit_algorithms/eigensolvers/numpy_eigensolver.py b/qiskit_algorithms/eigensolvers/numpy_eigensolver.py index a7811302..0f089839 100644 --- a/qiskit_algorithms/eigensolvers/numpy_eigensolver.py +++ b/qiskit_algorithms/eigensolvers/numpy_eigensolver.py @@ -21,8 +21,8 @@ from qiskit.quantum_info import SparsePauliOp, Statevector from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.utils.validation import validate_min +from qiskit_algorithms.utils.validation import validate_min from .eigensolver import Eigensolver, EigensolverResult from ..exceptions import AlgorithmError from ..list_or_dict import ListOrDict diff --git a/qiskit_algorithms/eigensolvers/vqd.py b/qiskit_algorithms/eigensolvers/vqd.py index 6339dd29..c4db44dd 100644 --- a/qiskit_algorithms/eigensolvers/vqd.py +++ b/qiskit_algorithms/eigensolvers/vqd.py @@ -53,12 +53,12 @@ class VQD(VariationalAlgorithm, Eigensolver): the k eigenvalues of the Hamiltonian :math:`H` of a given system. The algorithm computes excited state energies of generalised hamiltonians - by optimising over a modified cost function where each succesive eigenvalue + by optimizing over a modified cost function where each successive eigenvalue is calculated iteratively by introducing an overlap term with all the previously computed eigenstates that must be minimised, thus ensuring higher energy eigenstates are found. - An instance of VQD requires defining three algorithmic sub-components: + An instance of VQD requires defining three algorithmic subcomponents: an integer k denoting the number of eigenstates to calculate, a trial state (a.k.a. ansatz) which is a :class:`QuantumCircuit`, and one instance (or list of) classical :mod:`~qiskit_algorithms.optimizers`. diff --git a/qiskit_algorithms/gradients/reverse/reverse_qgt.py b/qiskit_algorithms/gradients/reverse/reverse_qgt.py index 78bd8342..46a2e2d0 100644 --- a/qiskit_algorithms/gradients/reverse/reverse_qgt.py +++ b/qiskit_algorithms/gradients/reverse/reverse_qgt.py @@ -157,7 +157,7 @@ def _run_unique( grad_coeffs = [coeff for coeff, _ in deriv] grad_states = [phi.evolve(gate) for _, gate in deriv] - # compute the digaonal element L_{j, j} + # compute the diagonal element L_{j, j} metric[j, j] += _l_term(grad_coeffs, grad_states, grad_coeffs, grad_states) # compute the off diagonal elements L_{i, j} diff --git a/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py b/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py index 26cbd1d6..b1951d8f 100644 --- a/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py +++ b/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py @@ -22,11 +22,12 @@ import numpy as np -from qiskit import QiskitError from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.circuit.library import EvolvedOperatorAnsatz from qiskit.utils.deprecation import deprecate_arg, deprecate_func -from qiskit.utils.validation import validate_min + +from qiskit_algorithms.utils.validation import validate_min +from qiskit_algorithms.exceptions import AlgorithmError from qiskit_algorithms.list_or_dict import ListOrDict @@ -239,8 +240,8 @@ def compute_minimum_eigenvalue( Raises: TypeError: If an ansatz other than :class:`~.EvolvedOperatorAnsatz` is provided. - QiskitError: If all evaluated gradients lie below the convergence threshold in the first - iteration of the algorithm. + AlgorithmError: If all evaluated gradients lie below the convergence threshold in + the first iteration of the algorithm. Returns: An :class:`~.AdaptVQEResult` which is a :class:`~.VQEResult` but also but also @@ -281,7 +282,7 @@ def compute_minimum_eigenvalue( # log gradients if np.abs(max_grad[0]) < self.gradient_threshold: if iteration == 1: - raise QiskitError( + raise AlgorithmError( "All gradients have been evaluated to lie below the convergence threshold " "during the first iteration of the algorithm. Try to either tighten the " "convergence threshold or pick a different ansatz." diff --git a/qiskit_algorithms/minimum_eigensolvers/qaoa.py b/qiskit_algorithms/minimum_eigensolvers/qaoa.py index 2a4dabc1..6faeb0d5 100644 --- a/qiskit_algorithms/minimum_eigensolvers/qaoa.py +++ b/qiskit_algorithms/minimum_eigensolvers/qaoa.py @@ -21,8 +21,8 @@ from qiskit.circuit.library.n_local.qaoa_ansatz import QAOAAnsatz from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.primitives import BaseSampler -from qiskit.utils.validation import validate_min +from qiskit_algorithms.utils.validation import validate_min from qiskit_algorithms.optimizers import Minimizer, Optimizer from .sampling_vqe import SamplingVQE diff --git a/qiskit_algorithms/optimizers/adam_amsgrad.py b/qiskit_algorithms/optimizers/adam_amsgrad.py index 9a986712..4132b5a0 100644 --- a/qiskit_algorithms/optimizers/adam_amsgrad.py +++ b/qiskit_algorithms/optimizers/adam_amsgrad.py @@ -43,13 +43,6 @@ class ADAM(Optimizer): [2]: Sashank J. Reddi and Satyen Kale and Sanjiv Kumar (2018), On the Convergence of Adam and Beyond. `arXiv:1904.09237 `_ - - .. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). - """ _OPTIONS = [ diff --git a/qiskit_algorithms/optimizers/aqgd.py b/qiskit_algorithms/optimizers/aqgd.py index 060345eb..f74bbda4 100644 --- a/qiskit_algorithms/optimizers/aqgd.py +++ b/qiskit_algorithms/optimizers/aqgd.py @@ -18,7 +18,8 @@ from typing import Any import numpy as np -from qiskit.utils.validation import validate_range_exclusive_max + +from qiskit_algorithms.utils.validation import validate_range_exclusive_max from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT from ..exceptions import AlgorithmError diff --git a/qiskit_algorithms/optimizers/gradient_descent.py b/qiskit_algorithms/optimizers/gradient_descent.py index 8ac10fe9..17426a3f 100644 --- a/qiskit_algorithms/optimizers/gradient_descent.py +++ b/qiskit_algorithms/optimizers/gradient_descent.py @@ -98,13 +98,13 @@ def f(x): def learning_rate(): power = 0.6 constant_coeff = 0.1 - def powerlaw(): + def power_law(): n = 0 while True: yield constant_coeff * (n ** power) n += 1 - return powerlaw() + return power_law() def f(x): return (np.linalg.norm(x) - 1) ** 2 @@ -157,7 +157,7 @@ def grad(x): evaluated_gradient = grad(ask_data.x_center) optimizer.state.njev += 1 - optmizer.state.nit += 1 + optimizer.state.nit += 1 tell_data = TellData(eval_jac=evaluated_gradient) optimizer.tell(ask_data=ask_data, tell_data=tell_data) @@ -197,7 +197,7 @@ def __init__( perturbation in both directions (defaults to 1e-2 if required). Ignored when we have an explicit function for the gradient. Raises: - ValueError: If ``learning_rate`` is an array and its lenght is less than ``maxiter``. + ValueError: If ``learning_rate`` is an array and its length is less than ``maxiter``. """ super().__init__(maxiter=maxiter) self.callback = callback @@ -250,7 +250,7 @@ def perturbation(self, perturbation: float | None) -> None: def _callback_wrapper(self) -> None: """ - Wraps the callback function to accomodate GradientDescent. + Wraps the callback function to accommodate GradientDescent. Will call :attr:`~.callback` and pass the following arguments: current number of function values, current parameters, current function value, @@ -295,7 +295,7 @@ def ask(self) -> AskData: def tell(self, ask_data: AskData, tell_data: TellData) -> None: """ - Updates :attr:`.~GradientDescentState.x` by an ammount proportional to the learning + Updates :attr:`.~GradientDescentState.x` by an amount proportional to the learning rate and value of the gradient at that point. Args: diff --git a/qiskit_algorithms/optimizers/gsls.py b/qiskit_algorithms/optimizers/gsls.py index c5832c4d..34144ae2 100644 --- a/qiskit_algorithms/optimizers/gsls.py +++ b/qiskit_algorithms/optimizers/gsls.py @@ -16,9 +16,10 @@ from collections.abc import Callable from typing import Any, SupportsFloat + import numpy as np -from qiskit.utils import algorithm_globals +from qiskit_algorithms.utils import algorithm_globals from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT @@ -33,7 +34,7 @@ class GSLS(Optimizer): This component has some function that is normally random. If you want to reproduce behavior then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). """ _OPTIONS = [ diff --git a/qiskit_algorithms/optimizers/p_bfgs.py b/qiskit_algorithms/optimizers/p_bfgs.py index 93a10a37..e1a98bd0 100644 --- a/qiskit_algorithms/optimizers/p_bfgs.py +++ b/qiskit_algorithms/optimizers/p_bfgs.py @@ -21,8 +21,8 @@ import numpy as np -from qiskit.utils import algorithm_globals -from qiskit.utils.validation import validate_min +from qiskit_algorithms.utils import algorithm_globals +from qiskit_algorithms.utils.validation import validate_min from .optimizer import OptimizerResult, POINT from .scipy_optimizer import SciPyOptimizer @@ -42,6 +42,12 @@ class P_BFGS(SciPyOptimizer): # pylint: disable=invalid-name Uses scipy.optimize.fmin_l_bfgs_b. For further detail, please refer to https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html + + .. note:: + + This component has some function that is normally random. If you want to reproduce behavior + then you should set the random number generator seed in the algorithm_globals + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). """ _OPTIONS = ["maxfun", "ftol", "iprint"] diff --git a/qiskit_algorithms/optimizers/qnspsa.py b/qiskit_algorithms/optimizers/qnspsa.py index 54711bae..cc9c0d63 100644 --- a/qiskit_algorithms/optimizers/qnspsa.py +++ b/qiskit_algorithms/optimizers/qnspsa.py @@ -51,7 +51,7 @@ class QNSPSA(SPSA): This component has some function that is normally random. If you want to reproduce behavior then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). Examples: diff --git a/qiskit_algorithms/optimizers/scipy_optimizer.py b/qiskit_algorithms/optimizers/scipy_optimizer.py index e2de3960..a5661993 100644 --- a/qiskit_algorithms/optimizers/scipy_optimizer.py +++ b/qiskit_algorithms/optimizers/scipy_optimizer.py @@ -19,8 +19,7 @@ import numpy as np from scipy.optimize import minimize -from qiskit.utils.validation import validate_min - +from qiskit_algorithms.utils.validation import validate_min from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT @@ -117,7 +116,7 @@ def minimize( jac: Callable[[POINT], POINT] | None = None, bounds: list[tuple[float, float]] | None = None, ) -> OptimizerResult: - # Remove ignored parameters to supress the warning of scipy.optimize.minimize + # Remove ignored parameters to suppress the warning of scipy.optimize.minimize if self.is_bounds_ignored: bounds = None if self.is_gradient_ignored: diff --git a/qiskit_algorithms/optimizers/snobfit.py b/qiskit_algorithms/optimizers/snobfit.py index faf54a99..9269df5f 100644 --- a/qiskit_algorithms/optimizers/snobfit.py +++ b/qiskit_algorithms/optimizers/snobfit.py @@ -17,7 +17,7 @@ from typing import Any import numpy as np -from qiskit.exceptions import QiskitError +from qiskit_algorithms.exceptions import AlgorithmError from qiskit_algorithms.utils import optionals as _optionals from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT @@ -53,13 +53,13 @@ def __init__( Raises: MissingOptionalLibraryError: scikit-quant or SQSnobFit not installed - QiskitError: If NumPy 1.24.0 or above is installed. + AlgorithmError: If NumPy 1.24.0 or above is installed. See https://github.com/scikit-quant/scikit-quant/issues/24 for more details. """ # check version version = tuple(map(int, np.__version__.split("."))) if version >= (1, 24, 0): - raise QiskitError( + raise AlgorithmError( "SnobFit is incompatible with NumPy 1.24.0 or above, please " "install a previous version. See also scikit-quant/scikit-quant#24." ) diff --git a/qiskit_algorithms/optimizers/spsa.py b/qiskit_algorithms/optimizers/spsa.py index 9ae0adde..7e1092b1 100644 --- a/qiskit_algorithms/optimizers/spsa.py +++ b/qiskit_algorithms/optimizers/spsa.py @@ -26,7 +26,7 @@ import scipy import numpy as np -from qiskit.utils import algorithm_globals +from qiskit_algorithms.utils import algorithm_globals from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT @@ -77,7 +77,7 @@ class SPSA(Optimizer): This component has some function that is normally random. If you want to reproduce behavior then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). Examples: @@ -291,9 +291,9 @@ def calibrate( modelspace: bool = False, max_evals_grouped: int = 1, ) -> tuple[Callable, Callable]: - r"""Calibrate SPSA parameters with a powerseries as learning rate and perturbation coeffs. + r"""Calibrate SPSA parameters with a power series as learning rate and perturbation coeffs. - The powerseries are: + The power series are: .. math:: @@ -306,15 +306,15 @@ def calibrate( stability_constant: The value of `A`. target_magnitude: The target magnitude for the first update step, defaults to :math:`2\pi / 10`. - alpha: The exponent of the learning rate powerseries. - gamma: The exponent of the perturbation powerseries. + alpha: The exponent of the learning rate power series. + gamma: The exponent of the perturbation power series. modelspace: Whether the target magnitude is the difference of parameter values or function values (= model space). max_evals_grouped: The number of grouped evaluations supported by the loss function. Defaults to 1, i.e. no grouping. Returns: - tuple(generator, generator): A tuple of powerseries generators, the first one for the + tuple(generator, generator): A tuple of power series generators, the first one for the learning rate and the second one for the perturbation. """ logger.info("SPSA: Starting calibration of learning rate and perturbation.") @@ -327,7 +327,7 @@ def calibrate( steps = 25 points = [] for _ in range(steps): - # compute the random directon + # compute the random direction pert = bernoulli_perturbation(dim) points += [initial_point + c * pert, initial_point - c * pert] @@ -359,7 +359,7 @@ def calibrate( ) logger.info(" -- Perturbation: c / (n ^ gamma) with c = %s, gamma = %s", c, gamma) - # set up the powerseries + # set up the power series def learning_rate(): return powerseries(a, alpha, stability_constant) @@ -664,7 +664,7 @@ def bernoulli_perturbation(dim, perturbation_dims=None): def powerseries(eta=0.01, power=2, offset=0): - """Yield a series decreasing by a powerlaw.""" + """Yield a series decreasing by a power law.""" n = 1 while True: diff --git a/qiskit_algorithms/optimizers/steppable_optimizer.py b/qiskit_algorithms/optimizers/steppable_optimizer.py index 26c9331b..e9ffda9b 100644 --- a/qiskit_algorithms/optimizers/steppable_optimizer.py +++ b/qiskit_algorithms/optimizers/steppable_optimizer.py @@ -69,9 +69,9 @@ class OptimizerState: nfev: int | None """Number of function evaluations so far in the optimization.""" njev: int | None - """Number of jacobian evaluations so far in the opimization.""" + """Number of jacobian evaluations so far in the optimization.""" nit: int | None - """Number of optmization steps performed so far in the optimization.""" + """Number of optimization steps performed so far in the optimization.""" class SteppableOptimizer(Optimizer): @@ -81,7 +81,7 @@ class SteppableOptimizer(Optimizer): This family of optimizers uses the `ask and tell interface `_. When using this interface the user has to call :meth:`~.ask` to get information about - how to evaluate the fucntion (we are asking the optimizer about how to do the evaluation). + how to evaluate the function (we are asking the optimizer about how to do the evaluation). This information is typically the next points at which the function is evaluated, but depending on the optimizer it can also determine whether to evaluate the function or its gradient. Once the function has been evaluated, the user calls the method :meth:`~..tell` @@ -130,7 +130,7 @@ def grad(x): evaluated_gradient = grad(ask_data.x_center) optimizer.state.njev += 1 - optmizer.state.nit += 1 + optimizer.state.nit += 1 cf = TellData(eval_jac=evaluated_gradient) optimizer.tell(ask_data=ask_data, tell_data=tell_data) @@ -180,7 +180,7 @@ def ask(self) -> AskData: It is the first method inside of a :meth:`~.step` in the optimization process. Returns: - An object containing the data needed to make the funciton evaluation to advance the + An object containing the data needed to make the function evaluation to advance the optimization process. """ @@ -217,7 +217,7 @@ def evaluate(self, ask_data: AskData) -> TellData: def _callback_wrapper(self) -> None: """ - Wraps the callback function to accomodate each optimizer. + Wraps the callback function to accommodate each optimizer. """ pass diff --git a/qiskit_algorithms/optimizers/umda.py b/qiskit_algorithms/optimizers/umda.py index f47c0947..e7eba0c6 100644 --- a/qiskit_algorithms/optimizers/umda.py +++ b/qiskit_algorithms/optimizers/umda.py @@ -16,10 +16,11 @@ from collections.abc import Callable from typing import Any + import numpy as np from scipy.stats import norm -from qiskit.utils import algorithm_globals +from qiskit_algorithms.utils import algorithm_globals from .optimizer import OptimizerResult, POINT from .scipy_optimizer import Optimizer, OptimizerSupportLevel @@ -47,7 +48,7 @@ class UMDA(Optimizer): have been obtained [1]. UMDA seems to provide very good solutions for those circuits in which the number of layers is not big. - The optimization process can be personalized depending on the paremeters chosen in the + The optimization process can be personalized depending on the parameters chosen in the initialization. The main parameter is the population size. The bigger it is, the final result will be better. However, this increases the complexity of the algorithm and the runtime will be much heavier. In the work [1] different experiments have been performed where population @@ -72,7 +73,6 @@ class UMDA(Optimizer): from qiskit_algorithms.optimizers import UMDA from qiskit_algorithms import QAOA - from qiskit.utils import QuantumInstance from qiskit.quantum_info import Pauli from qiskit.primitives import Sampler @@ -102,6 +102,11 @@ class UMDA(Optimizer): qaoa = QAOA(Sampler(), opt,reps=p) result = qaoa.compute_minimum_eigenvalue(operator=H2_op) + .. note:: + + This component has some function that is normally random. If you want to reproduce behavior + then you should set the random number generator seed in the algorithm_globals + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). References: diff --git a/qiskit_algorithms/phase_estimators/phase_estimation.py b/qiskit_algorithms/phase_estimators/phase_estimation.py index 5285dada..bf84b736 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimation.py +++ b/qiskit_algorithms/phase_estimators/phase_estimation.py @@ -183,7 +183,7 @@ def estimate_from_pe_circuit(self, pe_circuit: QuantumCircuit) -> PhaseEstimatio pe_circuit: The phase estimation circuit. Returns: - An instance of qiskit_algorithms.phase_estimator_result.PhaseEstimationResult. + A phase estimation result. Raises: AlgorithmError: Primitive job failed. @@ -223,7 +223,7 @@ def estimate( computational basis. Returns: - An instance of qiskit_algorithms.phase_estimator_result.PhaseEstimationResult. + A phase estimation result. """ pe_circuit = self.construct_circuit(unitary, state_preparation) diff --git a/qiskit_algorithms/phase_estimators/phase_estimation_result.py b/qiskit_algorithms/phase_estimators/phase_estimation_result.py index 5712c273..25d54760 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimation_result.py +++ b/qiskit_algorithms/phase_estimators/phase_estimation_result.py @@ -70,7 +70,7 @@ def phase(self) -> float: r"""Return the most likely phase as a number in :math:`[0.0, 1.0)`. 1.0 corresponds to a phase of :math:`2\pi`. This selects the phase corresponding - to the bit string with the highesest probability. This is the most likely phase. + to the bit string with the highest probability. This is the most likely phase. """ if isinstance(self.phases, dict): binary_phase_string = max(self.phases, key=self.phases.get) diff --git a/qiskit_algorithms/state_fidelities/base_state_fidelity.py b/qiskit_algorithms/state_fidelities/base_state_fidelity.py index fc98febb..834b42dd 100644 --- a/qiskit_algorithms/state_fidelities/base_state_fidelity.py +++ b/qiskit_algorithms/state_fidelities/base_state_fidelity.py @@ -179,7 +179,7 @@ def _construct_circuits( self._check_qubits_match(circuit_1, circuit_2) # re-parametrize input circuits - # TODO: make smarter checks to avoid unnecesary reparametrizations + # TODO: make smarter checks to avoid unnecessary re-parametrizations parameters_1 = ParameterVector("x", circuit_1.num_parameters) parametrized_circuit_1 = circuit_1.assign_parameters(parameters_1) parameters_2 = ParameterVector("y", circuit_2.num_parameters) diff --git a/qiskit_algorithms/time_evolvers/pvqd/pvqd.py b/qiskit_algorithms/time_evolvers/pvqd/pvqd.py index ab4969c4..0585f547 100644 --- a/qiskit_algorithms/time_evolvers/pvqd/pvqd.py +++ b/qiskit_algorithms/time_evolvers/pvqd/pvqd.py @@ -23,9 +23,9 @@ from qiskit.primitives import BaseEstimator from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.synthesis import EvolutionSynthesis, LieTrotter -from qiskit.utils import algorithm_globals +from qiskit_algorithms.utils import algorithm_globals -from ...exceptions import AlgorithmError, QiskitError +from ...exceptions import AlgorithmError from ...optimizers import Minimizer, Optimizer from ...state_fidelities.base_state_fidelity import BaseStateFidelity from ..real_time_evolver import RealTimeEvolver @@ -283,7 +283,7 @@ def evaluate_loss(displacement: np.ndarray | list[np.ndarray]) -> float | np.nda def evaluate_gradient(displacement: np.ndarray) -> np.ndarray: """Evaluate the gradient with the parameter-shift rule. - This is hardcoded here since the gradient framework does not support computing + This is hard-coded here since the gradient framework does not support computing gradients for overlaps. Args: @@ -420,12 +420,12 @@ def _validate_setup(self, skip=None): ) if self.ansatz.num_parameters == 0: - raise QiskitError( + raise AlgorithmError( "The ansatz cannot have 0 parameters, otherwise it cannot be trained." ) if len(self.initial_parameters) != self.ansatz.num_parameters: - raise QiskitError( + raise AlgorithmError( f"Mismatching number of parameters in the ansatz ({self.ansatz.num_parameters}) " f"and the initial parameters ({len(self.initial_parameters)})." ) diff --git a/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py b/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py index 549a9651..a36ddff6 100644 --- a/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py +++ b/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py @@ -65,7 +65,7 @@ def __init__( should be 1 to obtain a number of time-steps equal to ``num_timesteps`` and an evaluation of :attr:`.TimeEvolutionProblem.aux_operators` at every time-step. If ``reps`` is larger than 1, the true number of time-steps will be ``num_timesteps * reps``. - num_timesteps: The number of time-steps the full evolution time is devided into + num_timesteps: The number of time-steps the full evolution time is divided into (repetitions of ``product_formula``) estimator: An estimator primitive used for calculating expectation values of ``TimeEvolutionProblem.aux_operators``. diff --git a/qiskit_algorithms/utils/__init__.py b/qiskit_algorithms/utils/__init__.py index ef185f8f..1e9e5544 100644 --- a/qiskit_algorithms/utils/__init__.py +++ b/qiskit_algorithms/utils/__init__.py @@ -12,10 +12,12 @@ """Common qiskit_algorithms utility functions.""" +from .algorithm_globals import algorithm_globals from .validate_initial_point import validate_initial_point from .validate_bounds import validate_bounds __all__ = [ + "algorithm_globals", "validate_initial_point", "validate_bounds", ] diff --git a/qiskit_algorithms/utils/algorithm_globals.py b/qiskit_algorithms/utils/algorithm_globals.py new file mode 100644 index 00000000..75d01733 --- /dev/null +++ b/qiskit_algorithms/utils/algorithm_globals.py @@ -0,0 +1,122 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2019, 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. + +""" +utils.algorithm_globals +======================= +Common (global) properties used across qiskit_algorithms. + +.. currentmodule:: qiskit_algorithms.utils.algorithm_globals + +Includes: + + * Random number generator and random seed. + + Algorithms can use the generator for random values, as needed, and it + can be seeded here for reproducible results when using such an algorithm. + This is often important, for example in unit tests, where the same + outcome is desired each time (reproducible) and not have it be variable + due to randomness. + +Attributes: + random_seed (int | None): Random generator seed (read/write). + random (np.random.Generator): Random generator (read-only) +""" + +from __future__ import annotations + +import warnings + +import numpy as np + + +class QiskitAlgorithmGlobals: + """Global properties for algorithms.""" + + # The code is done to work even after some future removal of algorithm_globals + # from Qiskit (qiskit.utils). All that is needed in the future, after that, if + # this is updated, is just the logic in the except blocks. + # + # If the Qiskit version exists this acts a redirect to that (it delegates the + # calls off to it). In the future when that does not exist this has similar code + # in the except blocks here, as noted above, that will take over. By delegating + # to the Qiskit instance it means that any existing code that uses that continues + # to work. Logic here in qiskit_algorithms though uses this instance and the + # random check here has logic to warn if the seed here is not the same as the Qiskit + # version so we can detect direct usage of the Qiskit version and alert the user to + # change their code to use this. So simply changing from: + # from qiskit.utils import algorithm_globals + # to + # from qiskit_algorithm.utils import algorithm_globals + + def __init__(self) -> None: + self._random_seed: int | None = None + self._random = None + + @property + def random_seed(self) -> int | None: + """Random seed property (getter/setter).""" + try: + from qiskit.utils import algorithm_globals as qiskit_globals + + return qiskit_globals.random_seed + + except ImportError: + return self._random_seed + + @random_seed.setter + def random_seed(self, seed: int | None) -> None: + """Set the random generator seed. + + Args: + seed: If ``None`` then internally a random value is used as a seed + """ + try: + from qiskit.utils import algorithm_globals as qiskit_globals + + qiskit_globals.random_seed = seed + # Mirror the seed here when set via this random_seed. If the seed is + # set on the qiskit.utils instance then we can detect it's different + self._random_seed = seed + + except ImportError: + self._random_seed = seed + self._random = None + + @property + def random(self) -> np.random.Generator: + """Return a numpy np.random.Generator (default_rng) using random_seed.""" + try: + from qiskit.utils import algorithm_globals as qiskit_globals + + if self._random_seed != qiskit_globals.random_seed: + # If the seeds are different - likely this local is None and the qiskit.utils + # algorithms global was seeded directly then we will warn to use this here as + # the Qiskit version is planned to be removed in a future version of Qiskit. + warnings.warn( + "Using random that is seeded via qiskit.utils algorithm_globals is deprecated " + "since version 0.2.0. Instead set random_seed directly to " + "qiskit_algorithms.utils algorithm_globals.", + category=DeprecationWarning, + stacklevel=2, + ) + + return qiskit_globals.random + + except ImportError: + if self._random is None: + self._random = np.random.default_rng(self._random_seed) + return self._random + + +# Global instance to be used as the entry point for globals. +algorithm_globals = QiskitAlgorithmGlobals() diff --git a/qiskit_algorithms/utils/validate_initial_point.py b/qiskit_algorithms/utils/validate_initial_point.py index c486f9f4..fde01f02 100644 --- a/qiskit_algorithms/utils/validate_initial_point.py +++ b/qiskit_algorithms/utils/validate_initial_point.py @@ -19,7 +19,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.utils import algorithm_globals +from qiskit_algorithms.utils import algorithm_globals def validate_initial_point( diff --git a/qiskit_algorithms/utils/validation.py b/qiskit_algorithms/utils/validation.py new file mode 100644 index 00000000..ae838d8d --- /dev/null +++ b/qiskit_algorithms/utils/validation.py @@ -0,0 +1,138 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2019, 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. + +""" +Validation module +""" + +from typing import Set + + +def validate_in_set(name: str, value: object, values: Set[object]) -> None: + """ + Args: + name: value name. + value: value to check. + values: set that should contain value. + Raises: + ValueError: invalid value + """ + if value not in values: + raise ValueError(f"{name} must be one of '{values}', was '{value}'.") + + +def validate_min(name: str, value: float, minimum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + Raises: + ValueError: invalid value + """ + if value < minimum: + raise ValueError(f"{name} must have value >= {minimum}, was {value}") + + +def validate_min_exclusive(name: str, value: float, minimum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + Raises: + ValueError: invalid value + """ + if value <= minimum: + raise ValueError(f"{name} must have value > {minimum}, was {value}") + + +def validate_max(name: str, value: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value > maximum: + raise ValueError(f"{name} must have value <= {maximum}, was {value}") + + +def validate_max_exclusive(name: str, value: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value >= maximum: + raise ValueError(f"{name} must have value < {maximum}, was {value}") + + +def validate_range(name: str, value: float, minimum: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value < minimum or value > maximum: + raise ValueError(f"{name} must have value >= {minimum} and <= {maximum}, was {value}") + + +def validate_range_exclusive(name: str, value: float, minimum: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value <= minimum or value >= maximum: + raise ValueError(f"{name} must have value > {minimum} and < {maximum}, was {value}") + + +def validate_range_exclusive_min(name: str, value: float, minimum: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value <= minimum or value > maximum: + raise ValueError(f"{name} must have value > {minimum} and <= {maximum}, was {value}") + + +def validate_range_exclusive_max(name: str, value: float, minimum: float, maximum: float) -> None: + """ + Args: + name: value name. + value: value to check. + minimum: minimum value allowed. + maximum: maximum value allowed. + Raises: + ValueError: invalid value + """ + if value < minimum or value >= maximum: + raise ValueError(f"{name} must have value >= {minimum} and < {maximum}, was {value}") diff --git a/qiskit_algorithms/variational_algorithm.py b/qiskit_algorithms/variational_algorithm.py index 88b19c7e..aa295616 100644 --- a/qiskit_algorithms/variational_algorithm.py +++ b/qiskit_algorithms/variational_algorithm.py @@ -23,7 +23,7 @@ This component has some function that is normally random. If you want to reproduce behavior then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). + (``qiskit_algorithms.utils.algorithm_globals.random_seed = seed``). """ from __future__ import annotations diff --git a/releasenotes/notes/algorithm_globals-9a8afe956f974c86.yaml b/releasenotes/notes/algorithm_globals-9a8afe956f974c86.yaml new file mode 100644 index 00000000..0386aab9 --- /dev/null +++ b/releasenotes/notes/algorithm_globals-9a8afe956f974c86.yaml @@ -0,0 +1,39 @@ +--- +upgrade: + - | + The ``qiskit-algorithms`` code uses a common random number generator, + which can be seeded for reproducibility. The algorithms code here, having + originated in Qiskit and having been moved here, used random function from + ``qiskit.utils`` which was seeded as follows:: + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = 101 + + Now this will continue to work in ``qiskit-algorithms``, until such time + as the ``algorithm_globals`` are removed from ``Qiskit``, however you are + highly recommended to already change to import/seed the ``algorithm_globals`` + that is now supplied by ``qiskit-algorithms`` thus:: + + from qiskit_algorithms.utils import algorithm_globals + algorithm_globals.random_seed = 101 + + As can be seen it's simply a change to the import statement, so as to import + the ``qiskit_algorithms`` instance rather than the one from ``qiskit``. + + This has been done to afford a transition and not break end-users code by supporting + seeding the random generator as it was done before. How does it work - well, + while the ``qiskit.utils`` version exists, the ``qiskit-algorithms`` version + simply delegates its function to that instance. However the main codebase of + ``qiskit-algorithms`` has already been altered to use the new instance and + the delegation function, that accesses the random generator, will warn with + a message to switch to seeding the ``qiskit-algorithms`` version if it detects + a difference in the seed. A difference could exist if the ``random_seed`` + was set direct to the ``qiskit.utils`` instance. + + At such a time when the ``qiskit.utils`` ``algorithm_globals`` version no + longer exists, rather than delegating the functionality to that, it will use + identical logic which it already has, so no further user change will be + required if you already transitioned to seeding the ``qiskit_algorithms.utils`` + ``algorithms_globals``. + + diff --git a/releasenotes/notes/change_exception-85f9f8d86ac16720.yaml b/releasenotes/notes/change_exception-85f9f8d86ac16720.yaml new file mode 100644 index 00000000..51bbbbf9 --- /dev/null +++ b/releasenotes/notes/change_exception-85f9f8d86ac16720.yaml @@ -0,0 +1,12 @@ +--- +upgrade: + - | + A couple of algorithms here, :class:`.PVQD`, :class:`.AdaptVQE` and optimizer + :class:`.SNOBFIT`, directly raised a :class:`~qiskit.exceptions.QiskitError`. + These have been changed to raise an :class:`.AlgorithmError` instead. Algorithms + have now been moved out of ``Qiskit`` and this better distinguishes the exception to the + algorithms when raised. Now ``AlgorithmError`` was already raised elsewhere by the algorithms + here so this makes things more consistent too. Note, that as ``AlgorithmError`` + internally extends ``QiskitError``, any code that might have caught that specifically + will continue to work. However we do recommend you update your code accordingly for + ``AlgorithmError``. diff --git a/test/algorithms_test_case.py b/test/algorithms_test_case.py index 4c8a425d..c4445c9d 100644 --- a/test/algorithms_test_case.py +++ b/test/algorithms_test_case.py @@ -21,6 +21,8 @@ import unittest import time +from qiskit_algorithms.utils import algorithm_globals + # disable deprecation warnings that can cause log output overflow # pylint: disable=unused-argument @@ -45,6 +47,7 @@ def setUp(self) -> None: self._class_location = __file__ def tearDown(self) -> None: + algorithm_globals.random_seed = None elapsed = time.time() - self._started_at if elapsed > 5.0: print(f"({round(elapsed, 2):.2f}s)", flush=True) diff --git a/test/eigensolvers/test_vqd.py b/test/eigensolvers/test_vqd.py index 3ec49f09..46b22a31 100644 --- a/test/eigensolvers/test_vqd.py +++ b/test/eigensolvers/test_vqd.py @@ -23,12 +23,12 @@ from qiskit.primitives import Sampler, Estimator from qiskit.quantum_info import SparsePauliOp from qiskit.quantum_info.operators import Operator -from qiskit.utils import algorithm_globals from qiskit_algorithms.eigensolvers import VQD, VQDResult from qiskit_algorithms import AlgorithmError from qiskit_algorithms.optimizers import COBYLA, L_BFGS_B, SLSQP, SPSA from qiskit_algorithms.state_fidelities import ComputeUncompute +from qiskit_algorithms.utils import algorithm_globals H2_SPARSE_PAULI = SparsePauliOp.from_list( [ @@ -115,7 +115,7 @@ def test_full_spectrum(self): @data(H2_SPARSE_PAULI) def test_beta_autoeval(self, op): - """Test beta autoevaluation for different operator types.""" + """Test beta auto-evaluation for different operator types.""" with self.assertLogs(level="INFO") as logs: vqd = VQD( diff --git a/test/gradients/test_estimator_gradient.py b/test/gradients/test_estimator_gradient.py index cc05a741..975cb9bf 100644 --- a/test/gradients/test_estimator_gradient.py +++ b/test/gradients/test_estimator_gradient.py @@ -14,6 +14,7 @@ """Test Estimator Gradients""" import unittest +from test import QiskitAlgorithmsTestCase import numpy as np from ddt import ddt, data, unpack @@ -25,7 +26,6 @@ from qiskit.primitives import Estimator from qiskit.quantum_info import Operator, SparsePauliOp, Pauli from qiskit.quantum_info.random import random_pauli_list -from qiskit.test import QiskitTestCase from qiskit_algorithms.gradients import ( FiniteDiffEstimatorGradient, @@ -49,7 +49,7 @@ @ddt -class TestEstimatorGradient(QiskitTestCase): +class TestEstimatorGradient(QiskitAlgorithmsTestCase): """Test Estimator Gradient""" @data(*gradient_factories) diff --git a/test/gradients/test_qfi.py b/test/gradients/test_qfi.py index 70946a8e..ad4f5cdb 100644 --- a/test/gradients/test_qfi.py +++ b/test/gradients/test_qfi.py @@ -14,21 +14,21 @@ """Test QFI.""" import unittest -from ddt import ddt, data +from test import QiskitAlgorithmsTestCase +from ddt import ddt, data import numpy as np from qiskit import QuantumCircuit from qiskit.circuit import Parameter from qiskit.circuit.parametervector import ParameterVector from qiskit.primitives import Estimator -from qiskit.test import QiskitTestCase from qiskit_algorithms.gradients import LinCombQGT, ReverseQGT, QFI, DerivativeType @ddt -class TestQFI(QiskitTestCase): +class TestQFI(QiskitAlgorithmsTestCase): """Test QFI""" def setUp(self): diff --git a/test/gradients/test_qgt.py b/test/gradients/test_qgt.py index 91fc818a..905f4142 100644 --- a/test/gradients/test_qgt.py +++ b/test/gradients/test_qgt.py @@ -14,15 +14,15 @@ """Test QGT.""" import unittest -from ddt import ddt, data +from test import QiskitAlgorithmsTestCase +from ddt import ddt, data import numpy as np from qiskit import QuantumCircuit from qiskit.circuit import Parameter from qiskit.circuit.library import RealAmplitudes from qiskit.primitives import Estimator -from qiskit.test import QiskitTestCase from qiskit_algorithms.gradients import DerivativeType, LinCombQGT, ReverseQGT @@ -30,7 +30,7 @@ @ddt -class TestQGT(QiskitTestCase): +class TestQGT(QiskitAlgorithmsTestCase): """Test QGT""" def setUp(self): diff --git a/test/gradients/test_sampler_gradient.py b/test/gradients/test_sampler_gradient.py index 154e04cd..3bace4d6 100644 --- a/test/gradients/test_sampler_gradient.py +++ b/test/gradients/test_sampler_gradient.py @@ -14,6 +14,7 @@ """Test Sampler Gradients""" import unittest +from test import QiskitAlgorithmsTestCase from typing import List import numpy as np @@ -25,7 +26,6 @@ from qiskit.circuit.library.standard_gates import RXXGate from qiskit.primitives import Sampler from qiskit.result import QuasiDistribution -from qiskit.test import QiskitTestCase from qiskit_algorithms.gradients import ( FiniteDiffSamplerGradient, @@ -46,7 +46,7 @@ @ddt -class TestSamplerGradient(QiskitTestCase): +class TestSamplerGradient(QiskitAlgorithmsTestCase): """Test Sampler Gradient""" @data(*gradient_factories) diff --git a/test/minimum_eigensolvers/test_adapt_vqe.py b/test/minimum_eigensolvers/test_adapt_vqe.py index fd3010cf..1b72377d 100644 --- a/test/minimum_eigensolvers/test_adapt_vqe.py +++ b/test/minimum_eigensolvers/test_adapt_vqe.py @@ -23,11 +23,11 @@ from qiskit.circuit.library import EvolvedOperatorAnsatz from qiskit.primitives import Estimator from qiskit.quantum_info import SparsePauliOp -from qiskit.utils import algorithm_globals from qiskit_algorithms.minimum_eigensolvers import VQE from qiskit_algorithms.minimum_eigensolvers.adapt_vqe import AdaptVQE, TerminationCriterion from qiskit_algorithms.optimizers import SLSQP +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/minimum_eigensolvers/test_qaoa.py b/test/minimum_eigensolvers/test_qaoa.py index de15a239..17a15773 100644 --- a/test/minimum_eigensolvers/test_qaoa.py +++ b/test/minimum_eigensolvers/test_qaoa.py @@ -26,10 +26,10 @@ from qiskit.primitives import Sampler from qiskit.quantum_info import Pauli, SparsePauliOp from qiskit.result import QuasiDistribution -from qiskit.utils import algorithm_globals from qiskit_algorithms.minimum_eigensolvers import QAOA from qiskit_algorithms.optimizers import COBYLA, NELDER_MEAD +from qiskit_algorithms.utils import algorithm_globals W1 = np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) P1 = 1 diff --git a/test/minimum_eigensolvers/test_sampling_vqe.py b/test/minimum_eigensolvers/test_sampling_vqe.py index 820ab798..3ae889fc 100644 --- a/test/minimum_eigensolvers/test_sampling_vqe.py +++ b/test/minimum_eigensolvers/test_sampling_vqe.py @@ -25,12 +25,13 @@ from qiskit.circuit.library import RealAmplitudes, TwoLocal from qiskit.primitives import Sampler from qiskit.quantum_info import Operator, Pauli, SparsePauliOp -from qiskit.utils import algorithm_globals from qiskit_algorithms import AlgorithmError from qiskit_algorithms.minimum_eigensolvers import SamplingVQE from qiskit_algorithms.optimizers import L_BFGS_B, QNSPSA, SLSQP, OptimizerResult from qiskit_algorithms.state_fidelities import ComputeUncompute +from qiskit_algorithms.utils import algorithm_globals + # pylint: disable=invalid-name def _mock_optimizer(fun, x0, jac=None, bounds=None, inputs=None): @@ -258,7 +259,7 @@ def store_intermediate_result(eval_count, parameters, mean, metadata): def test_aggregation(self): """Test the aggregation works.""" - # test a custom aggregration that just uses the best measurement + # test a custom aggregation that just uses the best measurement def best_measurement(measurements): res = min(measurements, key=lambda meas: meas[1])[1] return res diff --git a/test/minimum_eigensolvers/test_vqe.py b/test/minimum_eigensolvers/test_vqe.py index c159424b..8d5f48b1 100644 --- a/test/minimum_eigensolvers/test_vqe.py +++ b/test/minimum_eigensolvers/test_vqe.py @@ -24,7 +24,6 @@ from qiskit.circuit.library import RealAmplitudes, TwoLocal from qiskit.quantum_info import SparsePauliOp, Operator, Pauli from qiskit.primitives import Estimator, Sampler -from qiskit.utils import algorithm_globals from qiskit_algorithms import AlgorithmError from qiskit_algorithms.gradients import ParamShiftEstimatorGradient @@ -42,6 +41,8 @@ TNC, ) from qiskit_algorithms.state_fidelities import ComputeUncompute +from qiskit_algorithms.utils import algorithm_globals + # pylint: disable=invalid-name def _mock_optimizer(fun, x0, jac=None, bounds=None, inputs=None) -> OptimizerResult: diff --git a/test/optimizers/test_gradient_descent.py b/test/optimizers/test_gradient_descent.py index e43b112c..d8ca30d2 100644 --- a/test/optimizers/test_gradient_descent.py +++ b/test/optimizers/test_gradient_descent.py @@ -89,7 +89,7 @@ def callback(*args): self.assertIsInstance(history[0][3], float) # norm of the gradient def test_minimize(self): - """Test setting the learning rate as iterator and minimizing the funciton.""" + """Test setting the learning rate as iterator and minimizing the function.""" def learning_rate(): power = 0.6 diff --git a/test/optimizers/test_optimizer_aqgd.py b/test/optimizers/test_optimizer_aqgd.py index 6c78e76d..c2136a95 100644 --- a/test/optimizers/test_optimizer_aqgd.py +++ b/test/optimizers/test_optimizer_aqgd.py @@ -15,7 +15,6 @@ import unittest from test import QiskitAlgorithmsTestCase from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import algorithm_globals from qiskit.primitives import Estimator from qiskit.quantum_info import SparsePauliOp from qiskit.test import slow_test @@ -24,6 +23,7 @@ from qiskit_algorithms.gradients import LinCombEstimatorGradient from qiskit_algorithms.optimizers import AQGD from qiskit_algorithms.minimum_eigensolvers import VQE +from qiskit_algorithms.utils import algorithm_globals class TestOptimizerAQGD(QiskitAlgorithmsTestCase): diff --git a/test/optimizers/test_optimizers.py b/test/optimizers/test_optimizers.py index cf05eff4..05867cb0 100644 --- a/test/optimizers/test_optimizers.py +++ b/test/optimizers/test_optimizers.py @@ -22,7 +22,7 @@ from qiskit.circuit.library import RealAmplitudes from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.utils import algorithm_globals, optionals +from qiskit.utils import optionals from qiskit.primitives import Sampler from qiskit_algorithms.optimizers import ( @@ -48,6 +48,7 @@ TNC, SciPyOptimizer, ) +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/optimizers/test_optimizers_scikitquant.py b/test/optimizers/test_optimizers_scikitquant.py index 4d08c0fa..a8f8f191 100644 --- a/test/optimizers/test_optimizers_scikitquant.py +++ b/test/optimizers/test_optimizers_scikitquant.py @@ -19,13 +19,13 @@ import numpy from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import algorithm_globals from qiskit.exceptions import MissingOptionalLibraryError from qiskit.primitives import Estimator from qiskit.quantum_info import SparsePauliOp from qiskit_algorithms.minimum_eigensolvers import VQE from qiskit_algorithms.optimizers import BOBYQA, SNOBFIT, IMFIL +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/optimizers/test_spsa.py b/test/optimizers/test_spsa.py index 67ee3f05..51a9e503 100644 --- a/test/optimizers/test_spsa.py +++ b/test/optimizers/test_spsa.py @@ -20,9 +20,9 @@ from qiskit.circuit.library import PauliTwoDesign from qiskit.primitives import Estimator, Sampler from qiskit.quantum_info import SparsePauliOp, Statevector -from qiskit.utils import algorithm_globals from qiskit_algorithms.optimizers import SPSA, QNSPSA +from qiskit_algorithms.utils import algorithm_globals @ddt @@ -80,7 +80,7 @@ def objective(x): self.assertEqual(result.nfev, expected_nfev) # function evaluations def test_recalibrate_at_optimize(self): - """Test SPSA calibrates anew upon each optimization run, if no autocalibration is set.""" + """Test SPSA calibrates anew upon each optimization run, if no auto-calibration is set.""" def objective(x): return -(x**2) diff --git a/test/optimizers/test_umda.py b/test/optimizers/test_umda.py index 8722db08..86e0454d 100644 --- a/test/optimizers/test_umda.py +++ b/test/optimizers/test_umda.py @@ -17,9 +17,8 @@ import numpy as np from scipy.optimize import rosen -from qiskit.utils import algorithm_globals - from qiskit_algorithms.optimizers.umda import UMDA +from qiskit_algorithms.utils import algorithm_globals class TestUMDA(QiskitAlgorithmsTestCase): diff --git a/test/state_fidelities/test_compute_uncompute.py b/test/state_fidelities/test_compute_uncompute.py index 94124cf6..a8cfe8f3 100644 --- a/test/state_fidelities/test_compute_uncompute.py +++ b/test/state_fidelities/test_compute_uncompute.py @@ -13,18 +13,18 @@ """Tests for Fidelity.""" import unittest +from test import QiskitAlgorithmsTestCase import numpy as np from qiskit.circuit import QuantumCircuit, ParameterVector from qiskit.circuit.library import RealAmplitudes from qiskit.primitives import Sampler -from qiskit.test import QiskitTestCase from qiskit_algorithms.state_fidelities import ComputeUncompute -class TestComputeUncompute(QiskitTestCase): +class TestComputeUncompute(QiskitAlgorithmsTestCase): """Test Compute-Uncompute Fidelity class""" def setUp(self): diff --git a/test/test_validation.py b/test/test_validation.py index 23053c94..2d9ae22d 100644 --- a/test/test_validation.py +++ b/test/test_validation.py @@ -15,7 +15,8 @@ import unittest from test import QiskitAlgorithmsTestCase -from qiskit.utils.validation import ( + +from qiskit_algorithms.utils.validation import ( validate_in_set, validate_min, validate_min_exclusive, diff --git a/test/time_evolvers/test_pvqd.py b/test/time_evolvers/test_pvqd.py index 23055297..0509e8d9 100644 --- a/test/time_evolvers/test_pvqd.py +++ b/test/time_evolvers/test_pvqd.py @@ -18,18 +18,18 @@ import numpy as np from ddt import data, ddt, unpack -from qiskit import QiskitError from qiskit.circuit import Gate, Parameter, QuantumCircuit from qiskit.circuit.library import EfficientSU2 from qiskit.primitives import Estimator, Sampler from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.test import QiskitTestCase -from qiskit.utils import algorithm_globals +from qiskit_algorithms import AlgorithmError from qiskit_algorithms.time_evolvers import TimeEvolutionProblem from qiskit_algorithms.optimizers import L_BFGS_B, SPSA, GradientDescent, OptimizerResult from qiskit_algorithms.state_fidelities import ComputeUncompute from qiskit_algorithms.time_evolvers.pvqd import PVQD +from qiskit_algorithms.utils import algorithm_globals + # pylint: disable=unused-argument, invalid-name def gradient_supplied(fun, x0, jac, info): @@ -222,7 +222,7 @@ def test_zero_parameters(self): optimizer=SPSA(maxiter=10, learning_rate=0.1, perturbation=0.01), ) - with self.assertRaises(QiskitError): + with self.assertRaises(AlgorithmError): _ = pvqd.evolve(problem) def test_initial_state_raises(self): @@ -270,7 +270,7 @@ def test_aux_ops_raises(self): _ = pvqd.evolve(problem) -class TestPVQDUtils(QiskitTestCase): +class TestPVQDUtils(QiskitAlgorithmsTestCase): """Test some utility functions for PVQD.""" def setUp(self): diff --git a/test/time_evolvers/test_trotter_qrte.py b/test/time_evolvers/test_trotter_qrte.py index bf08bd44..3a1b5670 100644 --- a/test/time_evolvers/test_trotter_qrte.py +++ b/test/time_evolvers/test_trotter_qrte.py @@ -22,12 +22,12 @@ from qiskit import QuantumCircuit from qiskit.circuit.library import ZGate from qiskit.quantum_info import Statevector, Pauli, SparsePauliOp -from qiskit.utils import algorithm_globals from qiskit.circuit import Parameter from qiskit.primitives import Estimator from qiskit.synthesis import SuzukiTrotter, QDrift from qiskit_algorithms.time_evolvers import TimeEvolutionProblem, TrotterQRTE +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/time_evolvers/variational/test_var_qite.py b/test/time_evolvers/variational/test_var_qite.py index 83381b6e..e25b3743 100644 --- a/test/time_evolvers/variational/test_var_qite.py +++ b/test/time_evolvers/variational/test_var_qite.py @@ -21,7 +21,6 @@ from qiskit.circuit import Parameter from qiskit.primitives import Estimator from qiskit.quantum_info import SparsePauliOp, Pauli -from qiskit.utils import algorithm_globals from qiskit.circuit.library import EfficientSU2 from qiskit.quantum_info import Statevector @@ -30,6 +29,7 @@ from qiskit_algorithms.time_evolvers.variational import ( ImaginaryMcLachlanPrinciple, ) +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/time_evolvers/variational/test_var_qrte.py b/test/time_evolvers/variational/test_var_qrte.py index 18d6abe1..bed4c48f 100644 --- a/test/time_evolvers/variational/test_var_qrte.py +++ b/test/time_evolvers/variational/test_var_qrte.py @@ -22,7 +22,6 @@ from qiskit.circuit import Parameter, ParameterVector from qiskit.circuit.library import EfficientSU2 from qiskit.primitives import Estimator -from qiskit.utils import algorithm_globals from qiskit.quantum_info import SparsePauliOp, Pauli, Statevector from qiskit_algorithms.gradients import LinCombQGT, DerivativeType, LinCombEstimatorGradient @@ -30,6 +29,7 @@ from qiskit_algorithms.time_evolvers.variational import ( RealMcLachlanPrinciple, ) +from qiskit_algorithms.utils import algorithm_globals @ddt diff --git a/test/utils/test_validate_bounds.py b/test/utils/test_validate_bounds.py index 83799d8d..63f16604 100644 --- a/test/utils/test_validate_bounds.py +++ b/test/utils/test_validate_bounds.py @@ -18,9 +18,7 @@ import numpy as np -from qiskit.utils import algorithm_globals - -from qiskit_algorithms.utils import validate_bounds +from qiskit_algorithms.utils import algorithm_globals, validate_bounds class TestValidateBounds(QiskitAlgorithmsTestCase): diff --git a/test/utils/test_validate_initial_point.py b/test/utils/test_validate_initial_point.py index 6badcb7b..9a8f1549 100644 --- a/test/utils/test_validate_initial_point.py +++ b/test/utils/test_validate_initial_point.py @@ -18,9 +18,7 @@ import numpy as np -from qiskit.utils import algorithm_globals - -from qiskit_algorithms.utils import validate_initial_point +from qiskit_algorithms.utils import algorithm_globals, validate_initial_point class TestValidateInitialPoint(QiskitAlgorithmsTestCase): @@ -45,7 +43,7 @@ def test_with_no_initial_point(self): np.testing.assert_array_almost_equal(initial_point, [0.430278]) def test_with_mismatched_params(self): - """Test with mistmatched parameters and bounds..""" + """Test with mismatched parameters and bounds..""" self.ansatz.parameter_bounds = None with self.assertRaises(ValueError): _ = validate_initial_point([1.0, 2.0], self.ansatz) diff --git a/tools/extract_deprecation.py b/tools/extract_deprecation.py new file mode 100644 index 00000000..5a61d982 --- /dev/null +++ b/tools/extract_deprecation.py @@ -0,0 +1,98 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2021, 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. + +""" Extract deprecation messages from input """ + +from typing import List +import sys +import os +import argparse + + +class DeprecationExtractor: + """Extract deprecation messages""" + + def __init__(self, in_file: str, out_file: str) -> None: + self._input_filename = in_file + self._output_filename = out_file + self._messages = None # type: List[str] + + def extract_messages(self) -> bool: + """ + extract deprecation + Returns: + bool: if messages were found + """ + + self._messages = None + messages = set() + with open(self._input_filename, "rt", encoding="utf8", errors="ignore") as file: + for line in file: + if line.find("DeprecationWarning:") > 0: + messages.add(line.strip()) + + if messages: + self._messages = sorted(messages) + return True + + return False + + def save_to_output(self, force_create: bool) -> bool: + """ + save messages to file if they exist + Args: + force_create: create file even if it is empty + Returns: + bool: if messages were saved + """ + if self._output_filename: + # create file even if it is empty + if self._messages or force_create: + with open(self._output_filename, "w", encoding="utf8") as file: + if self._messages: + file.write("\n".join(self._messages)) + return True + + return False + + def print_messages(self) -> None: + """print messages""" + if self._messages: + print("---------------------") + print("Deprecation Messages:") + print("---------------------") + for line in self._messages: + print(line) + + +def _check_file(path) -> str: + if not os.path.isfile(path): + raise argparse.ArgumentTypeError(f"file: '{path}' doesn't exist.") + + return path + + +if __name__ == "__main__": + PARSER = argparse.ArgumentParser(description="Qiskit Extract Deprecation Messages Tool") + PARSER.add_argument( + "-file", type=_check_file, required=True, metavar="file", help="Input file." + ) + PARSER.add_argument("-output", metavar="output", help="Output file.") + + ARGS = PARSER.parse_args() + + OBJ = DeprecationExtractor(ARGS.file, ARGS.output) + OBJ.extract_messages() + OBJ.save_to_output(True) + OBJ.print_messages() + + sys.exit(0)