diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0144b207..cbef0bdd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -139,6 +139,8 @@ jobs: - uses: ./.github/actions/install-algorithms - run: make lint shell: bash + - run: make mypy + shell: bash - name: Algorithms Unit Tests under Python ${{ matrix.python-version }} uses: ./.github/actions/run-tests with: diff --git a/.pylintdict b/.pylintdict index 01062282..4e054f7d 100644 --- a/.pylintdict +++ b/.pylintdict @@ -205,6 +205,7 @@ modelspace monte mosca mprev +mypy nabla nakaji nakanishi diff --git a/Makefile b/Makefile index 2ace92c1..378cf4a4 100644 --- a/Makefile +++ b/Makefile @@ -35,29 +35,32 @@ endif # You can set this variable from the command line. SPHINXOPTS = -.PHONY: lint style black test test_ci spell copyright html doctest clean_sphinx coverage clean +.PHONY: lint mypy style black test test_ci spell copyright html doctest clean_sphinx coverage clean -all_check: spell style lint copyright clean_sphinx html doctest +all_check: spell style lint copyright mypy clean_sphinx html doctest lint: pylint -rn qiskit_algorithms test tools python tools/verify_headers.py qiskit_algorithms test tools +mypy: + python -m mypy qiskit_algorithms test tools + style: - black --check qiskit_algorithms test tools + python -m black --check qiskit_algorithms test tools docs black: - black qiskit_algorithms test tools + python -m black qiskit_algorithms test tools docs test: python -m unittest discover -v test test_ci: echo "Detected $(NPROCS) CPUs running with $(CONCURRENCY) workers" - stestr run --concurrency $(CONCURRENCY) + python -m stestr run --concurrency $(CONCURRENCY) spell: - pylint -rn --disable=all --enable=spelling --spelling-dict=en_US qiskit_algorithms test tools + python -m 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: @@ -73,10 +76,10 @@ clean_sphinx: make -C docs clean coverage: - coverage3 run --source qiskit_algorithms -m unittest discover -s test -q - coverage3 report + python -m coverage3 run --source qiskit_algorithms -m unittest discover -s test -q + python -m coverage3 report coverage_erase: - coverage erase + python -m coverage erase clean: clean_sphinx coverage_erase; diff --git a/docs/conf.py b/docs/conf.py index 10cde338..85d90927 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ sys.path.append(os.path.abspath(".")) project = "Qiskit Algorithms" -copyright = f"2017-{datetime.date.today().year}, Qiskit Algorithms Development Team" # pylint: disable=redefined-builtin +copyright = f"2017-{datetime.date.today().year}, Qiskit Algorithms Development Team" # pylint: disable=redefined-builtin author = "Qiskit Algorithms Development Team" # The short X.Y version @@ -55,7 +55,7 @@ "matplotlib.sphinxext.plot_directive", "sphinx.ext.doctest", "qiskit_sphinx_theme", - "nbsphinx" + "nbsphinx", ] rst_prolog = """ @@ -81,7 +81,9 @@ __""" vers = version.split(".") -link_str = f" https://github.com/qiskit-community/qiskit-algorithms/blob/stable/{vers[0]}.{vers[1]}/docs/" +link_str = ( + f" https://github.com/qiskit-community/qiskit-algorithms/blob/stable/{vers[0]}.{vers[1]}/docs/" +) nbsphinx_prolog += link_str + "{{ docname }}" nbsphinx_timeout = 360 @@ -162,4 +164,3 @@ # >> code # output doctest_test_doctest_blocks = "" - diff --git a/docs/tutorials/01_algorithms_introduction.ipynb b/docs/tutorials/01_algorithms_introduction.ipynb index 5976277f..709eac93 100644 --- a/docs/tutorials/01_algorithms_introduction.ipynb +++ b/docs/tutorials/01_algorithms_introduction.ipynb @@ -141,13 +141,15 @@ "source": [ "from qiskit.quantum_info import SparsePauliOp\n", "\n", - "H2_op = SparsePauliOp.from_list([\n", - " (\"II\", -1.052373245772859),\n", - " (\"IZ\", 0.39793742484318045),\n", - " (\"ZI\", -0.39793742484318045),\n", - " (\"ZZ\", -0.01128010425623538),\n", - " (\"XX\", 0.18093119978423156)\n", - "])" + "H2_op = SparsePauliOp.from_list(\n", + " [\n", + " (\"II\", -1.052373245772859),\n", + " (\"IZ\", 0.39793742484318045),\n", + " (\"ZI\", -0.39793742484318045),\n", + " (\"ZZ\", -0.01128010425623538),\n", + " (\"XX\", 0.18093119978423156),\n", + " ]\n", + ")" ] }, { @@ -305,8 +307,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/02_vqe_advanced_options.ipynb b/docs/tutorials/02_vqe_advanced_options.ipynb index ecf74726..be08ede5 100644 --- a/docs/tutorials/02_vqe_advanced_options.ipynb +++ b/docs/tutorials/02_vqe_advanced_options.ipynb @@ -298,9 +298,7 @@ " values.append(mean)\n", "\n", "\n", - "vqe = VQE(\n", - " estimator, ansatz, optimizer, callback=store_intermediate_result, gradient=gradient\n", - ")\n", + "vqe = VQE(estimator, ansatz, optimizer, callback=store_intermediate_result, gradient=gradient)\n", "\n", "result = vqe.compute_minimum_eigenvalue(operator=H2_op)\n", "print(f\"Value using Gradient: {result.eigenvalue.real:.5f}\")" @@ -435,9 +433,7 @@ "ansatz1 = TwoLocal(rotation_blocks=\"ry\", entanglement_blocks=\"cz\")\n", "optimizer1 = SLSQP(maxiter=1000)\n", "\n", - "vqe1 = VQE(\n", - " estimator1, ansatz1, optimizer1, gradient=gradient1, initial_point=initial_pt\n", - ")\n", + "vqe1 = VQE(estimator1, ansatz1, optimizer1, gradient=gradient1, initial_point=initial_pt)\n", "result1 = vqe1.compute_minimum_eigenvalue(operator=H2_op)\n", "print(result1)\n", "\n", @@ -507,7 +503,7 @@ "import qiskit.tools.jupyter\n", "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/03_vqe_simulation_with_noise.ipynb b/docs/tutorials/03_vqe_simulation_with_noise.ipynb index 7d71cd0c..b91c4ac5 100644 --- a/docs/tutorials/03_vqe_simulation_with_noise.ipynb +++ b/docs/tutorials/03_vqe_simulation_with_noise.ipynb @@ -120,6 +120,7 @@ "counts = []\n", "values = []\n", "\n", + "\n", "def store_intermediate_result(eval_count, parameters, mean, std):\n", " counts.append(eval_count)\n", " values.append(mean)" @@ -162,15 +163,11 @@ "# instantiate and run VQE\n", "from qiskit_algorithms import VQE\n", "\n", - "vqe = VQE(\n", - " noiseless_estimator, ansatz, optimizer=spsa, callback=store_intermediate_result\n", - ")\n", + "vqe = VQE(noiseless_estimator, ansatz, optimizer=spsa, callback=store_intermediate_result)\n", "result = vqe.compute_minimum_eigenvalue(operator=H2_op)\n", "\n", "print(f\"VQE on Aer qasm simulator (no noise): {result.eigenvalue.real:.5f}\")\n", - "print(\n", - " f\"Delta from reference energy value is {(result.eigenvalue.real - ref_value):.5f}\"\n", - ")" + "print(f\"Delta from reference energy value is {(result.eigenvalue.real - ref_value):.5f}\")" ] }, { @@ -324,9 +321,7 @@ "result1 = vqe.compute_minimum_eigenvalue(operator=H2_op)\n", "\n", "print(f\"VQE on Aer qasm simulator (with noise): {result1.eigenvalue.real:.5f}\")\n", - "print(\n", - " f\"Delta from reference energy value is {(result1.eigenvalue.real - ref_value):.5f}\"\n", - ")" + "print(f\"Delta from reference energy value is {(result1.eigenvalue.real - ref_value):.5f}\")" ] }, { @@ -442,7 +437,7 @@ "import qiskit.tools.jupyter\n", "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/04_vqd.ipynb b/docs/tutorials/04_vqd.ipynb index 2443800e..254f7493 100644 --- a/docs/tutorials/04_vqd.ipynb +++ b/docs/tutorials/04_vqd.ipynb @@ -88,7 +88,7 @@ "ansatz = TwoLocal(2, rotation_blocks=[\"ry\", \"rz\"], entanglement_blocks=\"cz\", reps=1)\n", "\n", "optimizer = SLSQP()\n", - "ansatz.decompose().draw('mpl')" + "ansatz.decompose().draw(\"mpl\")" ] }, { @@ -148,6 +148,7 @@ "values = []\n", "steps = []\n", "\n", + "\n", "def callback(eval_count, params, value, meta, step):\n", " counts.append(eval_count)\n", " values.append(value)\n", @@ -170,8 +171,8 @@ "from qiskit_algorithms import VQD\n", "\n", "vqd = VQD(estimator, fidelity, ansatz, optimizer, k=k, betas=betas, callback=callback)\n", - "result = vqd.compute_eigenvalues(operator = H2_op)\n", - "vqd_values = result.eigenvalues\n" + "result = vqd.compute_eigenvalues(operator=H2_op)\n", + "vqd_values = result.eigenvalues" ] }, { @@ -237,7 +238,7 @@ "counts = np.asarray(counts)\n", "values = np.asarray(values)\n", "\n", - "for i in range(1,4):\n", + "for i in range(1, 4):\n", " _counts = counts[np.where(steps == i)]\n", " _values = values[np.where(steps == i)]\n", " pylab.plot(_counts, _values, label=f\"State {i-1}\")\n", @@ -338,8 +339,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/05_qaoa.ipynb b/docs/tutorials/05_qaoa.ipynb index 7a00b6fb..448c56f9 100644 --- a/docs/tutorials/05_qaoa.ipynb +++ b/docs/tutorials/05_qaoa.ipynb @@ -26,10 +26,9 @@ "import networkx as nx\n", "\n", "num_nodes = 4\n", - "w = np.array([[0., 1., 1., 0.],\n", - " [1., 0., 1., 1.],\n", - " [1., 1., 0., 1.],\n", - " [0., 1., 1., 0.]])\n", + "w = np.array(\n", + " [[0.0, 1.0, 1.0, 0.0], [1.0, 0.0, 1.0, 1.0], [1.0, 1.0, 0.0, 1.0], [0.0, 1.0, 1.0, 0.0]]\n", + ")\n", "G = nx.from_numpy_array(w)" ] }, @@ -56,9 +55,9 @@ ], "source": [ "layout = nx.random_layout(G, seed=10)\n", - "colors = ['r', 'g', 'b', 'y']\n", + "colors = [\"r\", \"g\", \"b\", \"y\"]\n", "nx.draw(G, layout, node_color=colors)\n", - "labels = nx.get_edge_attributes(G, 'weight')\n", + "labels = nx.get_edge_attributes(G, \"weight\")\n", "nx.draw_networkx_edge_labels(G, pos=layout, edge_labels=labels);" ] }, @@ -95,10 +94,12 @@ " w_01 = np.where(w != 0, 1, 0)\n", " return np.sum(w_01 * X)\n", "\n", + "\n", "def bitfield(n, L):\n", " result = np.binary_repr(n, L)\n", " return [int(digit) for digit in result] # [2:] to chop off the \"0b\" part\n", "\n", + "\n", "# use the brute-force way to generate the oracle\n", "L = num_nodes\n", "max = 2**L\n", @@ -114,7 +115,7 @@ " if cur_v < sol:\n", " sol = cur_v\n", "\n", - "print(f'Objective value computed by the brute-force method is {sol}')" + "print(f\"Objective value computed by the brute-force method is {sol}\")" ] }, { @@ -132,6 +133,7 @@ "source": [ "from qiskit.quantum_info import Pauli, SparsePauliOp\n", "\n", + "\n", "def get_operator(weight_matrix):\n", " r\"\"\"Generate Hamiltonian for the graph partitioning\n", " Notes:\n", @@ -179,6 +181,7 @@ "\n", " return SparsePauliOp(pauli_list, coeffs=coeffs), shift\n", "\n", + "\n", "qubit_op, offset = get_operator(w)" ] }, @@ -215,6 +218,7 @@ "\n", "sampler = Sampler()\n", "\n", + "\n", "def sample_most_likely(state_vector):\n", " \"\"\"Compute the most likely binary string from state vector.\n", " Args:\n", @@ -233,6 +237,7 @@ " x.reverse()\n", " return np.asarray(x)\n", "\n", + "\n", "algorithm_globals.random_seed = 10598\n", "\n", "optimizer = COBYLA()\n", @@ -243,7 +248,7 @@ "x = sample_most_likely(result.eigenstate)\n", "\n", "print(x)\n", - "print(f'Objective value computed by QAOA is {objective_value(x, w)}')" + "print(f\"Objective value computed by QAOA is {objective_value(x, w)}\")" ] }, { @@ -277,7 +282,7 @@ "x = sample_most_likely(result.eigenstate)\n", "\n", "print(x)\n", - "print(f'Objective value computed by the NumPyMinimumEigensolver is {objective_value(x, w)}')" + "print(f\"Objective value computed by the NumPyMinimumEigensolver is {objective_value(x, w)}\")" ] }, { @@ -318,7 +323,7 @@ "x = sample_most_likely(result.eigenstate)\n", "\n", "print(x)\n", - "print(f\"Objective value computed by SamplingVQE is {objective_value(x, w)}\")\n" + "print(f\"Objective value computed by SamplingVQE is {objective_value(x, w)}\")" ] }, { @@ -357,8 +362,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/06_grover.ipynb b/docs/tutorials/06_grover.ipynb index 5bafbdb3..dfd3259c 100644 --- a/docs/tutorials/06_grover.ipynb +++ b/docs/tutorials/06_grover.ipynb @@ -78,7 +78,7 @@ "from qiskit_algorithms import AmplificationProblem\n", "\n", "# the state we desire to find is '11'\n", - "good_state = ['11']\n", + "good_state = [\"11\"]\n", "\n", "# specify the oracle that marks the state '11' as a good solution\n", "oracle = QuantumCircuit(2)\n", @@ -90,7 +90,7 @@ "# now we can have a look at the Grover operator that is used in running the algorithm\n", "# (Algorithm circuits are wrapped in a gate to appear in composition as a block\n", "# so we have to decompose() the op to see it expanded into its component gates.)\n", - "problem.grover_operator.decompose().draw(output='mpl')" + "problem.grover_operator.decompose().draw(output=\"mpl\")" ] }, { @@ -125,10 +125,10 @@ "\n", "grover = Grover(sampler=Sampler())\n", "result = grover.amplify(problem)\n", - "print('Result type:', type(result))\n", + "print(\"Result type:\", type(result))\n", "print()\n", - "print('Success!' if result.oracle_evaluation else 'Failure!')\n", - "print('Top measurement:', result.top_measurement)" + "print(\"Success!\" if result.oracle_evaluation else \"Failure!\")\n", + "print(\"Top measurement:\", result.top_measurement)" ] }, { @@ -167,15 +167,15 @@ "source": [ "from qiskit.quantum_info import Statevector\n", "\n", - "oracle = Statevector.from_label('11')\n", - "problem = AmplificationProblem(oracle, is_good_state=['11'])\n", + "oracle = Statevector.from_label(\"11\")\n", + "problem = AmplificationProblem(oracle, is_good_state=[\"11\"])\n", "\n", "grover = Grover(sampler=Sampler())\n", "result = grover.amplify(problem)\n", - "print('Result type:', type(result))\n", + "print(\"Result type:\", type(result))\n", "print()\n", - "print('Success!' if result.oracle_evaluation else 'Failure!')\n", - "print('Top measurement:', result.top_measurement)" + "print(\"Success!\" if result.oracle_evaluation else \"Failure!\")\n", + "print(\"Top measurement:\", result.top_measurement)" ] }, { @@ -203,7 +203,7 @@ } ], "source": [ - "problem.grover_operator.oracle.decompose().draw(output='mpl')" + "problem.grover_operator.oracle.decompose().draw(output=\"mpl\")" ] }, { @@ -236,11 +236,11 @@ "from qiskit.exceptions import MissingOptionalLibraryError\n", "\n", "# `Oracle` (`PhaseOracle`) as the `oracle` argument\n", - "expression = '(a & b)'\n", + "expression = \"(a & b)\"\n", "try:\n", " oracle = PhaseOracle(expression)\n", " problem = AmplificationProblem(oracle)\n", - " display(problem.grover_operator.oracle.decompose().draw(output='mpl'))\n", + " display(problem.grover_operator.oracle.decompose().draw(output=\"mpl\"))\n", "except MissingOptionalLibraryError as ex:\n", " print(ex)" ] @@ -310,16 +310,18 @@ "theta = 2 * np.arccos(1 / np.sqrt(3))\n", "state_preparation = QuantumCircuit(3)\n", "state_preparation.ry(theta, 0)\n", - "state_preparation.ch(0,1)\n", + "state_preparation.ch(0, 1)\n", "state_preparation.x(1)\n", "state_preparation.h(2)\n", "\n", "# we only care about the first two bits being in state 1, thus add both possibilities for the last qubit\n", - "problem = AmplificationProblem(oracle, state_preparation=state_preparation, is_good_state=['110', '111'])\n", + "problem = AmplificationProblem(\n", + " oracle, state_preparation=state_preparation, is_good_state=[\"110\", \"111\"]\n", + ")\n", "\n", "# state_preparation\n", - "print('state preparation circuit:')\n", - "problem.grover_operator.state_preparation.draw(output='mpl')" + "print(\"state preparation circuit:\")\n", + "problem.grover_operator.state_preparation.draw(output=\"mpl\")" ] }, { @@ -339,8 +341,8 @@ "source": [ "grover = Grover(sampler=Sampler())\n", "result = grover.amplify(problem)\n", - "print('Success!' if result.oracle_evaluation else 'Failure!')\n", - "print('Top measurement:', result.top_measurement)" + "print(\"Success!\" if result.oracle_evaluation else \"Failure!\")\n", + "print(\"Top measurement:\", result.top_measurement)" ] }, { @@ -386,7 +388,7 @@ "source": [ "oracle = QuantumCircuit(5)\n", "oracle.ccz(0, 1, 2)\n", - "oracle.draw(output='mpl')" + "oracle.draw(output=\"mpl\")" ] }, { @@ -421,7 +423,7 @@ "from qiskit.circuit.library import GroverOperator\n", "\n", "grover_op = GroverOperator(oracle, insert_barriers=True)\n", - "grover_op.decompose().draw(output='mpl')" + "grover_op.decompose().draw(output=\"mpl\")" ] }, { @@ -450,7 +452,7 @@ ], "source": [ "grover_op = GroverOperator(oracle, reflection_qubits=[0, 1, 2], insert_barriers=True)\n", - "grover_op.decompose().draw(output='mpl')" + "grover_op.decompose().draw(output=\"mpl\")" ] }, { @@ -483,9 +485,9 @@ "# a list of binary strings good state\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "good_state = ['11', '00']\n", + "good_state = [\"11\", \"00\"]\n", "problem = AmplificationProblem(oracle, is_good_state=good_state)\n", - "print(problem.is_good_state('11'))" + "print(problem.is_good_state(\"11\"))" ] }, { @@ -507,7 +509,7 @@ "oracle.cz(0, 1)\n", "good_state = [0, 1]\n", "problem = AmplificationProblem(oracle, is_good_state=good_state)\n", - "print(problem.is_good_state('11'))" + "print(problem.is_good_state(\"11\"))" ] }, { @@ -531,9 +533,9 @@ "# `Statevector` good state\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "good_state = Statevector.from_label('11')\n", + "good_state = Statevector.from_label(\"11\")\n", "problem = AmplificationProblem(oracle, is_good_state=good_state)\n", - "print(problem.is_good_state('11'))" + "print(problem.is_good_state(\"11\"))" ] }, { @@ -558,10 +560,11 @@ " return True\n", " return False\n", "\n", + "\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", "problem = AmplificationProblem(oracle, is_good_state=good_state)\n", - "print(problem.is_good_state('11'))" + "print(problem.is_good_state(\"11\"))" ] }, { @@ -592,7 +595,7 @@ "# integer iteration\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "problem = AmplificationProblem(oracle, is_good_state=['11'])\n", + "problem = AmplificationProblem(oracle, is_good_state=[\"11\"])\n", "grover = Grover(iterations=1)" ] }, @@ -605,7 +608,7 @@ "# list iteration\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "problem = AmplificationProblem(oracle, is_good_state=['11'])\n", + "problem = AmplificationProblem(oracle, is_good_state=[\"11\"])\n", "grover = Grover(iterations=[1, 2, 3])" ] }, @@ -618,7 +621,7 @@ "# using sample_from_iterations\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "problem = AmplificationProblem(oracle, is_good_state=['11'])\n", + "problem = AmplificationProblem(oracle, is_good_state=[\"11\"])\n", "grover = Grover(iterations=[1, 2, 3], sample_from_iterations=True)" ] }, @@ -676,11 +679,12 @@ ], "source": [ "def to_DIAMACS_CNF_format(bit_rep):\n", - " return [index+1 if val==1 else -1 * (index + 1) for index, val in enumerate(bit_rep)]\n", + " return [index + 1 if val == 1 else -1 * (index + 1) for index, val in enumerate(bit_rep)]\n", + "\n", "\n", "oracle = QuantumCircuit(2)\n", "oracle.cz(0, 1)\n", - "problem = AmplificationProblem(oracle, is_good_state=['11'], post_processing=to_DIAMACS_CNF_format)\n", + "problem = AmplificationProblem(oracle, is_good_state=[\"11\"], post_processing=to_DIAMACS_CNF_format)\n", "problem.post_processing([1, 0, 1])" ] }, @@ -716,8 +720,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/07_grover_examples.ipynb b/docs/tutorials/07_grover_examples.ipynb index 0262a04e..99c6e663 100644 --- a/docs/tutorials/07_grover_examples.ipynb +++ b/docs/tutorials/07_grover_examples.ipynb @@ -24,7 +24,7 @@ "metadata": {}, "outputs": [], "source": [ - "input_3sat_instance = '''\n", + "input_3sat_instance = \"\"\"\n", "c example DIMACS-CNF 3-SAT\n", "p cnf 3 5\n", "-1 -2 -3 0\n", @@ -32,7 +32,7 @@ "1 2 -3 0\n", "1 -2 -3 0\n", "-1 2 3 0\n", - "'''" + "\"\"\"" ] }, { @@ -65,7 +65,7 @@ "from qiskit.exceptions import MissingOptionalLibraryError\n", "from qiskit.circuit.library.phase_oracle import PhaseOracle\n", "\n", - "fp = tempfile.NamedTemporaryFile(mode='w+t', delete=False)\n", + "fp = tempfile.NamedTemporaryFile(mode=\"w+t\", delete=False)\n", "fp.write(input_3sat_instance)\n", "file_name = fp.name\n", "fp.close()\n", @@ -187,7 +187,7 @@ } ], "source": [ - "expression = '(w ^ x) & ~(y ^ z) & (x & y & z)'\n", + "expression = \"(w ^ x) & ~(y ^ z) & (x & y & z)\"\n", "try:\n", " oracle = PhaseOracle(expression)\n", " problem = AmplificationProblem(oracle, is_good_state=oracle.evaluate_bitstring)\n", @@ -237,8 +237,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/10_pvqd.ipynb b/docs/tutorials/10_pvqd.ipynb index 464cd841..747dd7d8 100644 --- a/docs/tutorials/10_pvqd.ipynb +++ b/docs/tutorials/10_pvqd.ipynb @@ -41,9 +41,14 @@ "from qiskit.quantum_info import SparsePauliOp\n", "\n", "final_time = 1\n", - "hamiltonian = SparsePauliOp.from_sparse_list([\n", - " (\"ZZ\", [0, 1], 0.1), (\"X\", [0], 1), (\"X\", [1], 1),\n", - "], num_qubits=2)\n", + "hamiltonian = SparsePauliOp.from_sparse_list(\n", + " [\n", + " (\"ZZ\", [0, 1], 0.1),\n", + " (\"X\", [0], 1),\n", + " (\"X\", [1], 1),\n", + " ],\n", + " num_qubits=2,\n", + ")\n", "observable = SparsePauliOp([\"ZI\", \"IZ\"])" ] }, @@ -165,12 +170,7 @@ "from qiskit_algorithms import PVQD\n", "\n", "pvqd = PVQD(\n", - " fidelity,\n", - " ansatz,\n", - " initial_parameters,\n", - " estimator=estimator,\n", - " num_timesteps=100,\n", - " optimizer=bfgs\n", + " fidelity, ansatz, initial_parameters, estimator=estimator, num_timesteps=100, optimizer=bfgs\n", ")" ] }, @@ -189,7 +189,9 @@ "source": [ "from qiskit_algorithms import TimeEvolutionProblem\n", "\n", - "problem = TimeEvolutionProblem(hamiltonian, time=final_time, aux_operators=[hamiltonian, observable])" + "problem = TimeEvolutionProblem(\n", + " hamiltonian, time=final_time, aux_operators=[hamiltonian, observable]\n", + ")" ] }, { @@ -698,6 +700,7 @@ "source": [ "import scipy as sc\n", "\n", + "\n", "def exact(final_time, timestep, hamiltonian, initial_state):\n", " \"\"\"Get the exact values for energy and the observable.\"\"\"\n", " O = observable.to_matrix()\n", @@ -729,7 +732,9 @@ "from qiskit.quantum_info import Statevector\n", "\n", "initial_state = Statevector(ansatz.bind_parameters(initial_parameters))\n", - "exact_times, exact_energies, exact_magnetizations = exact(final_time, 0.01, hamiltonian, initial_state)" + "exact_times, exact_energies, exact_magnetizations = exact(\n", + " final_time, 0.01, hamiltonian, initial_state\n", + ")" ] }, { @@ -880,9 +885,9 @@ "source": [ "energies_gd = np.real(result_gd.observables)[:, 0]\n", "\n", - "plt.plot(result.times[:n + 1], energies[:n + 1], \"-\", color=\"royalblue\", label=\"BFGS\")\n", + "plt.plot(result.times[: n + 1], energies[: n + 1], \"-\", color=\"royalblue\", label=\"BFGS\")\n", "plt.plot(result_gd.times, energies_gd, \"--\", color=\"royalblue\", label=\"Gradient descent\")\n", - "plt.plot(exact_times[:n + 1], exact_energies[:n + 1], \":\", color=\"k\", label=\"Exact\")\n", + "plt.plot(exact_times[: n + 1], exact_energies[: n + 1], \":\", color=\"k\", label=\"Exact\")\n", "plt.legend(loc=\"best\")\n", "plt.xlabel(\"time $t$\")\n", "plt.ylabel(\"energy $E$\")\n", @@ -931,9 +936,9 @@ "source": [ "magnetizations_gd = np.real(result_gd.observables)[:, 1]\n", "\n", - "plt.plot(result.times[:n + 1], magnetizations[:n + 1], \"-\", color=\"crimson\", label=\"BFGS\")\n", + "plt.plot(result.times[: n + 1], magnetizations[: n + 1], \"-\", color=\"crimson\", label=\"BFGS\")\n", "plt.plot(result_gd.times, magnetizations_gd, \"--\", color=\"crimson\", label=\"Gradient descent\")\n", - "plt.plot(exact_times[:n + 1], exact_magnetizations[:n + 1], \":\", color=\"k\", label=\"Exact\")\n", + "plt.plot(exact_times[: n + 1], exact_magnetizations[: n + 1], \":\", color=\"k\", label=\"Exact\")\n", "plt.legend(loc=\"best\")\n", "plt.xlabel(\"time $t$\")\n", "plt.ylabel(r\"magnetization $\\langle Z_1 + Z_2 \\rangle$\")\n", @@ -979,8 +984,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/11_VarQTE.ipynb b/docs/tutorials/11_VarQTE.ipynb index 2dc2eb9a..78632154 100644 --- a/docs/tutorials/11_VarQTE.ipynb +++ b/docs/tutorials/11_VarQTE.ipynb @@ -85,10 +85,9 @@ "source": [ "from qiskit.quantum_info import SparsePauliOp\n", "\n", - "hamiltonian = SparsePauliOp(['ZZ', 'IX', 'XI'],\n", - " coeffs=[-0.2 , -1, -1])\n", + "hamiltonian = SparsePauliOp([\"ZZ\", \"IX\", \"XI\"], coeffs=[-0.2, -1, -1])\n", "\n", - "magnetization = SparsePauliOp([ 'IZ', 'ZI'], coeffs=[1, 1])" + "magnetization = SparsePauliOp([\"IZ\", \"ZI\"], coeffs=[1, 1])" ] }, { @@ -130,7 +129,7 @@ "from qiskit.circuit.library import EfficientSU2\n", "\n", "ansatz = EfficientSU2(hamiltonian.num_qubits, reps=1)\n", - "ansatz.decompose().draw('mpl')" + "ansatz.decompose().draw(\"mpl\")" ] }, { @@ -152,9 +151,9 @@ "source": [ "import numpy as np\n", "\n", - "init_param_values={}\n", + "init_param_values = {}\n", "for i in range(len(ansatz.parameters)):\n", - " init_param_values[ansatz.parameters[i]]=np.pi/2" + " init_param_values[ansatz.parameters[i]] = np.pi / 2" ] }, { @@ -274,7 +273,9 @@ "source": [ "from qiskit_algorithms import SciPyImaginaryEvolver\n", "\n", - "evolution_problem = TimeEvolutionProblem(hamiltonian, time, initial_state=init_state, aux_operators=aux_ops)\n", + "evolution_problem = TimeEvolutionProblem(\n", + " hamiltonian, time, initial_state=init_state, aux_operators=aux_ops\n", + ")\n", "exact_evol = SciPyImaginaryEvolver(num_timesteps=501)\n", "sol = exact_evol.evolve(evolution_problem)" ] @@ -323,8 +324,8 @@ "exact_h_exp_val = sol.observables[0][0].real\n", "\n", "times = evolution_result.times\n", - "pylab.plot(times, h_exp_val, label= \"VarQITE\")\n", - "pylab.plot(times, exact_h_exp_val , label= \"Exact\", linestyle='--')\n", + "pylab.plot(times, h_exp_val, label=\"VarQITE\")\n", + "pylab.plot(times, exact_h_exp_val, label=\"Exact\", linestyle=\"--\")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\langle H \\rangle$ (energy)\")\n", "pylab.legend(loc=\"upper right\");" @@ -383,7 +384,7 @@ "source": [ "from qiskit_algorithms.gradients import ReverseEstimatorGradient, ReverseQGT\n", "\n", - "var_principle = ImaginaryMcLachlanPrinciple(qgt=ReverseQGT() , gradient=ReverseEstimatorGradient())\n", + "var_principle = ImaginaryMcLachlanPrinciple(qgt=ReverseQGT(), gradient=ReverseEstimatorGradient())\n", "evolution_problem = TimeEvolutionProblem(hamiltonian, time, aux_operators=aux_ops)\n", "var_qite = VarQITE(ansatz, init_param_values, var_principle, Estimator())\n", "evolution_result_eff = var_qite.evolve(evolution_problem)" @@ -429,8 +430,8 @@ "exact_h_exp_val_eff = sol.observables[0][0].real\n", "\n", "times = evolution_result_eff.times\n", - "pylab.plot(times, h_exp_val_eff, label= r\"VarQITE$_{eff}$\")\n", - "pylab.plot(times, exact_h_exp_val_eff , label= \"Exact\", linestyle='--')\n", + "pylab.plot(times, h_exp_val_eff, label=r\"VarQITE$_{eff}$\")\n", + "pylab.plot(times, exact_h_exp_val_eff, label=\"Exact\", linestyle=\"--\")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\langle H \\rangle$ (energy)\")\n", "pylab.legend(loc=\"upper right\");" @@ -505,8 +506,8 @@ } ], "source": [ - "pylab.plot(times, (h_exp_val-exact_h_exp_val), label=\"VarQITE\")\n", - "pylab.plot(times, (h_exp_val_eff-exact_h_exp_val_eff), label=r\"VarQITE$_{eff}$\", linestyle='--')\n", + "pylab.plot(times, (h_exp_val - exact_h_exp_val), label=\"VarQITE\")\n", + "pylab.plot(times, (h_exp_val_eff - exact_h_exp_val_eff), label=r\"VarQITE$_{eff}$\", linestyle=\"--\")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\Delta \\langle H \\rangle$\")\n", "pylab.legend(loc=\"upper right\");" @@ -569,7 +570,7 @@ ], "source": [ "ansatz = EfficientSU2(hamiltonian.num_qubits, reps=1)\n", - "ansatz.decompose().draw('mpl')" + "ansatz.decompose().draw(\"mpl\")" ] }, { @@ -592,7 +593,9 @@ "init_param_values = {}\n", "\n", "for i in range(len(ansatz.parameters)):\n", - " init_param_values[ansatz.parameters[i]] = np.pi/2 # initialize the parameters which also decide the initial state" + " init_param_values[ansatz.parameters[i]] = (\n", + " np.pi / 2\n", + " ) # initialize the parameters which also decide the initial state" ] }, { @@ -686,7 +689,7 @@ "\n", "time = 10.0\n", "evolution_problem = TimeEvolutionProblem(hamiltonian, time, aux_operators=aux_ops)\n", - "var_qrte = VarQRTE(ansatz, init_param_values,var_principle, Estimator())\n", + "var_qrte = VarQRTE(ansatz, init_param_values, var_principle, Estimator())\n", "evolution_result_re = var_qrte.evolve(evolution_problem)" ] }, @@ -729,7 +732,9 @@ "source": [ "from qiskit_algorithms import SciPyRealEvolver\n", "\n", - "evolution_problem = TimeEvolutionProblem(hamiltonian, time, initial_state = init_circ, aux_operators=aux_ops)\n", + "evolution_problem = TimeEvolutionProblem(\n", + " hamiltonian, time, initial_state=init_circ, aux_operators=aux_ops\n", + ")\n", "rtev = SciPyRealEvolver(1001)\n", "sol = rtev.evolve(evolution_problem)" ] @@ -772,8 +777,8 @@ "mz_exp_val_re = np.array([ele[0][0] for ele in evolution_result_re.observables])\n", "exact_mz_exp_val_re = sol.observables[0][0].real\n", "times = evolution_result_re.times\n", - "pylab.plot(times, mz_exp_val_re, label= \"VarQRTE\")\n", - "pylab.plot(times, exact_mz_exp_val_re , label= \"Exact\", linestyle='--')\n", + "pylab.plot(times, mz_exp_val_re, label=\"VarQRTE\")\n", + "pylab.plot(times, exact_mz_exp_val_re, label=\"Exact\", linestyle=\"--\")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\langle m_z \\rangle$\")\n", "pylab.legend(loc=\"upper right\");" @@ -800,10 +805,12 @@ "source": [ "from qiskit_algorithms.gradients import DerivativeType\n", "\n", - "var_principle = RealMcLachlanPrinciple(qgt=ReverseQGT() , gradient=ReverseEstimatorGradient(derivative_type=DerivativeType.IMAG))\n", + "var_principle = RealMcLachlanPrinciple(\n", + " qgt=ReverseQGT(), gradient=ReverseEstimatorGradient(derivative_type=DerivativeType.IMAG)\n", + ")\n", "time = 10.0\n", "evolution_problem = TimeEvolutionProblem(hamiltonian, time, aux_operators=aux_ops)\n", - "var_qrte = VarQRTE(ansatz, init_param_values,var_principle, Estimator())\n", + "var_qrte = VarQRTE(ansatz, init_param_values, var_principle, Estimator())\n", "evolution_result_re_eff = var_qrte.evolve(evolution_problem)" ] }, @@ -837,8 +844,8 @@ ], "source": [ "mz_exp_val_re_eff = np.array([ele[0][0] for ele in evolution_result_re_eff.observables])\n", - "pylab.plot(times, mz_exp_val_re_eff, label= r\"VarQRTE$_{eff}$\")\n", - "pylab.plot(times, exact_mz_exp_val_re , label= \"Exact\", linestyle='--')\n", + "pylab.plot(times, mz_exp_val_re_eff, label=r\"VarQRTE$_{eff}$\")\n", + "pylab.plot(times, exact_mz_exp_val_re, label=\"Exact\", linestyle=\"--\")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\langle m_z \\rangle$\")\n", "pylab.legend(loc=\"upper right\");" @@ -879,8 +886,10 @@ } ], "source": [ - "pylab.plot(times, (mz_exp_val_re-exact_mz_exp_val_re), label= \"VarQRTE\")\n", - "pylab.plot(times, (mz_exp_val_re_eff-exact_mz_exp_val_re), label= r\"VarQRTE$_{eff}$\", linestyle='--')\n", + "pylab.plot(times, (mz_exp_val_re - exact_mz_exp_val_re), label=\"VarQRTE\")\n", + "pylab.plot(\n", + " times, (mz_exp_val_re_eff - exact_mz_exp_val_re), label=r\"VarQRTE$_{eff}$\", linestyle=\"--\"\n", + ")\n", "pylab.xlabel(\"Time\")\n", "pylab.ylabel(r\"$\\Delta \\langle m_z \\rangle$\")\n", "pylab.legend(loc=\"upper right\");" @@ -925,8 +934,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/12_gradients_framework.ipynb b/docs/tutorials/12_gradients_framework.ipynb index 71e9863d..24b693d8 100644 --- a/docs/tutorials/12_gradients_framework.ipynb +++ b/docs/tutorials/12_gradients_framework.ipynb @@ -144,21 +144,21 @@ "from qiskit.quantum_info import SparsePauliOp\n", "import numpy as np\n", "\n", - "#Instantiate the quantum circuit\n", - "a = Parameter('a')\n", - "b = Parameter('b')\n", + "# Instantiate the quantum circuit\n", + "a = Parameter(\"a\")\n", + "b = Parameter(\"b\")\n", "q = QuantumRegister(1)\n", "qc = QuantumCircuit(q)\n", "qc.h(q)\n", "qc.rz(a, q[0])\n", "qc.rx(b, q[0])\n", "\n", - "display(qc.draw('mpl'))\n", + "display(qc.draw(\"mpl\"))\n", "\n", - "#Instantiate the Hamiltonian observable 2X+Z\n", - "H = SparsePauliOp.from_list([('X', 2), ('Z',1)])\n", + "# Instantiate the Hamiltonian observable 2X+Z\n", + "H = SparsePauliOp.from_list([(\"X\", 2), (\"Z\", 1)])\n", "\n", - "#Parameter list\n", + "# Parameter list\n", "params = [[np.pi / 4, 0]]" ] }, @@ -200,15 +200,15 @@ "from qiskit.primitives import Estimator\n", "from qiskit_algorithms.gradients import ParamShiftEstimatorGradient\n", "\n", - "#Define the estimator\n", + "# Define the estimator\n", "estimator = Estimator()\n", - "#Define the gradient\n", + "# Define the gradient\n", "gradient = ParamShiftEstimatorGradient(estimator)\n", "\n", "# Evaluate the gradient of the circuits using parameter shift gradients\n", - "pse_grad_result = gradient.run(qc, H, params).result().gradients\n", + "pse_grad_result = gradient.run(qc, H, params).result().gradients\n", "\n", - "print('State estimator gradient computed with parameter shift', pse_grad_result)" + "print(\"State estimator gradient computed with parameter shift\", pse_grad_result)" ] }, { @@ -239,17 +239,17 @@ ], "source": [ "# Instantiate the quantum state with two parameters\n", - "a = Parameter('a')\n", - "b = Parameter('b')\n", + "a = Parameter(\"a\")\n", + "b = Parameter(\"b\")\n", "\n", "q = QuantumRegister(1)\n", "qc_sample = QuantumCircuit(q)\n", "qc_sample.h(q)\n", "qc_sample.rz(a, q[0])\n", "qc_sample.rx(b, q[0])\n", - "qc_sample.measure_all() #important for sampler\n", + "qc_sample.measure_all() # important for sampler\n", "\n", - "qc_sample.draw('mpl')" + "qc_sample.draw(\"mpl\")" ] }, { @@ -269,11 +269,11 @@ "from qiskit.primitives import Sampler\n", "from qiskit_algorithms.gradients import ParamShiftSamplerGradient\n", "\n", - "param_vals = [[np.pi/4, np.pi/2]]\n", + "param_vals = [[np.pi / 4, np.pi / 2]]\n", "sampler = Sampler()\n", "gradient = ParamShiftSamplerGradient(sampler)\n", "pss_grad_result = gradient.run(qc_sample, param_vals).result().gradients\n", - "print('State sampler gradient computed with parameter shift', pss_grad_result)" + "print(\"State sampler gradient computed with parameter shift\", pss_grad_result)" ] }, { @@ -322,7 +322,7 @@ "\n", "# Evaluate the gradient\n", "lce_grad_result = state_grad.run(qc, H, params).result().gradients\n", - "print('State estimator gradient computed with the linear combination method', lce_grad_result)" + "print(\"State estimator gradient computed with the linear combination method\", lce_grad_result)" ] }, { @@ -357,11 +357,11 @@ "source": [ "from qiskit_algorithms.gradients import FiniteDiffEstimatorGradient\n", "\n", - "state_grad = FiniteDiffEstimatorGradient(estimator, epsilon = 0.001)\n", + "state_grad = FiniteDiffEstimatorGradient(estimator, epsilon=0.001)\n", "\n", "# Evaluate the gradient\n", "fde_grad_result = state_grad.run(qc, H, params).result().gradients\n", - "print('State estimator gradient computed with finite difference', fde_grad_result)" + "print(\"State estimator gradient computed with finite difference\", fde_grad_result)" ] }, { @@ -389,11 +389,11 @@ "source": [ "from qiskit_algorithms.gradients import SPSAEstimatorGradient\n", "\n", - "state_grad = SPSAEstimatorGradient(estimator, epsilon = 0.001, batch_size=10, seed=50)\n", + "state_grad = SPSAEstimatorGradient(estimator, epsilon=0.001, batch_size=10, seed=50)\n", "\n", "# Evaluate the gradient\n", "spsae_grad_result = state_grad.run(qc, H, params).result().gradients\n", - "print('State estimator gradient computed with SPSA:', spsae_grad_result)" + "print(\"State estimator gradient computed with SPSA:\", spsae_grad_result)" ] }, { @@ -435,11 +435,11 @@ "\n", "qgt = LinCombQGT(estimator, derivative_type=DerivativeType.COMPLEX)\n", "\n", - "param_vals = [[np.pi/4, 0.1]]\n", + "param_vals = [[np.pi / 4, 0.1]]\n", "\n", - "#Evaluate the QGTs\n", + "# Evaluate the QGTs\n", "qgt_result = qgt.run(qc, param_vals).result().qgts\n", - "print('QGT:')\n", + "print(\"QGT:\")\n", "print(qgt_result)" ] }, @@ -479,12 +479,12 @@ "source": [ "from qiskit_algorithms.gradients import QFI\n", "\n", - "#Define the QFI metric for the QGT\n", + "# Define the QFI metric for the QGT\n", "qfi = QFI(qgt)\n", "\n", "# Evaluate the QFI\n", "qfi_result = qfi.run(qc, param_vals).result().qfis\n", - "print('QFI:')\n", + "print(\"QFI:\")\n", "print(qfi_result)" ] }, @@ -545,17 +545,16 @@ "from qiskit.circuit import ParameterVector\n", "\n", "# Instantiate the system Hamiltonian\n", - "h2_hamiltonian = SparsePauliOp.from_list([('II', -1.05),\n", - " ('IZ', 0.39),\n", - " ('ZI', -0.39),\n", - " ('ZZ', -0.01)])\n", + "h2_hamiltonian = SparsePauliOp.from_list(\n", + " [(\"II\", -1.05), (\"IZ\", 0.39), (\"ZI\", -0.39), (\"ZZ\", -0.01)]\n", + ")\n", "\n", "# This is the target energy\n", "h2_energy = -1.85727503\n", "\n", "# Define the Ansatz\n", "wavefunction = QuantumCircuit(2)\n", - "params = ParameterVector('theta', length=8)\n", + "params = ParameterVector(\"theta\", length=8)\n", "it = iter(params)\n", "wavefunction.ry(next(it), 0)\n", "wavefunction.ry(next(it), 1)\n", @@ -567,7 +566,7 @@ "wavefunction.rz(next(it), 0)\n", "wavefunction.rz(next(it), 1)\n", "\n", - "wavefunction.draw('mpl')" + "wavefunction.draw(\"mpl\")" ] }, { @@ -576,7 +575,7 @@ "metadata": {}, "outputs": [], "source": [ - "#Make circuit copies for different VQEs\n", + "# Make circuit copies for different VQEs\n", "wavefunction_1 = wavefunction.copy()\n", "wavefunction_2 = wavefunction.copy()" ] @@ -610,16 +609,16 @@ "from qiskit_algorithms.optimizers import CG\n", "from qiskit_algorithms import VQE, SamplingVQE\n", "\n", - "#Conjugate Gradient algorithm\n", + "# Conjugate Gradient algorithm\n", "optimizer = CG(maxiter=50)\n", "\n", "# Gradient callable\n", "estimator = Estimator()\n", - "grad = LinCombEstimatorGradient(estimator) # optional estimator gradient\n", + "grad = LinCombEstimatorGradient(estimator) # optional estimator gradient\n", "vqe = VQE(estimator=estimator, ansatz=wavefunction, optimizer=optimizer, gradient=grad)\n", "\n", "result = vqe.compute_minimum_eigenvalue(h2_hamiltonian)\n", - "print('Result of Estimator VQE:', result.optimal_value, '\\nReference:', h2_energy)" + "print(\"Result of Estimator VQE:\", result.optimal_value, \"\\nReference:\", h2_energy)" ] }, { @@ -653,11 +652,11 @@ "source": [ "from scipy.optimize import minimize\n", "\n", - "#Classical optimizer\n", + "# Classical optimizer\n", "vqe_classical = VQE(estimator=estimator, ansatz=wavefunction_2, optimizer=minimize, gradient=grad)\n", "\n", "result_classical = vqe_classical.compute_minimum_eigenvalue(h2_hamiltonian)\n", - "print('Result of classical optimizer:', result_classical.optimal_value, '\\nReference:', h2_energy)" + "print(\"Result of classical optimizer:\", result_classical.optimal_value, \"\\nReference:\", h2_energy)" ] }, { @@ -696,8 +695,9 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", - "%qiskit_copyright\n" + "%qiskit_copyright" ] } ], diff --git a/docs/tutorials/13_trotterQRTE.ipynb b/docs/tutorials/13_trotterQRTE.ipynb index 2fc61b1e..738e46e5 100644 --- a/docs/tutorials/13_trotterQRTE.ipynb +++ b/docs/tutorials/13_trotterQRTE.ipynb @@ -62,15 +62,16 @@ "from qiskit.quantum_info import SparsePauliOp\n", "from math import sin, cos\n", "\n", + "\n", "def get_hamiltonian(L, J, h, alpha=0):\n", - " \n", + "\n", " # List of Hamiltonian terms as 3-tuples containing\n", " # (1) the Pauli string,\n", " # (2) the qubit indices corresponding to the Pauli string,\n", " # (3) the coefficient.\n", - " ZZ_tuples = [('ZZ', [i, i + 1], -J) for i in range(0, L-1)]\n", - " Z_tuples = [('Z', [i], -h * sin(alpha)) for i in range(0, L)]\n", - " X_tuples = [('X', [i], -h * cos(alpha)) for i in range(0, L)]\n", + " ZZ_tuples = [(\"ZZ\", [i, i + 1], -J) for i in range(0, L - 1)]\n", + " Z_tuples = [(\"Z\", [i], -h * sin(alpha)) for i in range(0, L)]\n", + " X_tuples = [(\"X\", [i], -h * cos(alpha)) for i in range(0, L)]\n", "\n", " # We create the Hamiltonian as a SparsePauliOp, via the method\n", " # `from_sparse_list`, and multiply by the interaction term.\n", @@ -108,7 +109,7 @@ "source": [ "from math import pi\n", "\n", - "H = get_hamiltonian(L=2, J=.2, h=1., alpha=pi/8)\n", + "H = get_hamiltonian(L=2, J=0.2, h=1.0, alpha=pi / 8)\n", "H" ] }, @@ -135,7 +136,7 @@ "\n", "# First spin up, second spin down\n", "# (remember that the labels are interpreted from right to left)\n", - "initial_state = Statevector.from_label('10')\n", + "initial_state = Statevector.from_label(\"10\")\n", "\n", "problem = TimeEvolutionProblem(H, initial_state=initial_state, time=final_time)" ] @@ -189,7 +190,7 @@ } ], "source": [ - "result.evolved_state.draw('mpl')" + "result.evolved_state.draw(\"mpl\")" ] }, { @@ -219,7 +220,9 @@ } ], "source": [ - "result.evolved_state.decompose(reps=2).decompose('disentangler_dg').decompose('multiplex1_reverse_dg').draw('mpl')" + "result.evolved_state.decompose(reps=2).decompose(\"disentangler_dg\").decompose(\n", + " \"multiplex1_reverse_dg\"\n", + ").draw(\"mpl\")" ] }, { @@ -290,7 +293,7 @@ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", - "bar_width = .1\n", + "bar_width = 0.1\n", "# We prepare an initial state ↑↓ (01).\n", "# Note that Statevector and SparsePauliOp interpret the qubits from right to left\n", "initial_state = Statevector.from_label(\"10\")\n", @@ -300,10 +303,10 @@ "\n", "# We create the list of angles in radians, with a small epsilon\n", "# the exactly longitudinal field, which would present no dynamics at all\n", - "alphas = np.linspace(-np.pi/2 + eps, np.pi/2 - eps, 5)\n", + "alphas = np.linspace(-np.pi / 2 + eps, np.pi / 2 - eps, 5)\n", "\n", "for i, alpha in enumerate(alphas):\n", - " H_alpha = get_hamiltonian(L=2, J=.2, h=1., alpha=alpha)\n", + " H_alpha = get_hamiltonian(L=2, J=0.2, h=1.0, alpha=alpha)\n", " problem = TimeEvolutionProblem(H_alpha, initial_state=initial_state, time=1.6)\n", " result = trotter.evolve(problem)\n", " evolved_state = Statevector(result.evolved_state)\n", @@ -313,7 +316,7 @@ " values = list(amplitudes_dict.values())\n", " # Convert angle to degrees\n", " alpha_str = f\"$\\\\alpha={int(np.round(alpha * 180 / pi))}^\\\\circ$\"\n", - " plt.bar(np.arange(4) + i * bar_width, values, bar_width, label=alpha_str, alpha=.7)\n", + " plt.bar(np.arange(4) + i * bar_width, values, bar_width, label=alpha_str, alpha=0.7)\n", "\n", "plt.xticks(np.arange(4) + 2 * bar_width, labels)\n", "plt.xlabel(\"Measurement\")\n", @@ -359,7 +362,7 @@ "from math import pi\n", "\n", "L = 6\n", - "H = get_hamiltonian(L=L, J=.2, h=1.2, alpha=pi/8)" + "H = get_hamiltonian(L=L, J=0.2, h=1.2, alpha=pi / 8)" ] }, { @@ -399,8 +402,12 @@ "metadata": {}, "outputs": [], "source": [ - "magnetization_op = SparsePauliOp.from_sparse_list([('Z', [i], 1.) for i in range(0, L)], num_qubits=L)\n", - "correlation_op = SparsePauliOp.from_sparse_list([('ZZ', [i, i+1], 1.) for i in range(0, L-1)], num_qubits=L) / (L - 1)" + "magnetization_op = SparsePauliOp.from_sparse_list(\n", + " [(\"Z\", [i], 1.0) for i in range(0, L)], num_qubits=L\n", + ")\n", + "correlation_op = SparsePauliOp.from_sparse_list(\n", + " [(\"ZZ\", [i, i + 1], 1.0) for i in range(0, L - 1)], num_qubits=L\n", + ") / (L - 1)" ] }, { @@ -421,9 +428,14 @@ "metadata": {}, "outputs": [], "source": [ - "final_time = 30.\n", - "initial_state = Statevector.from_label('001100')\n", - "problem = TimeEvolutionProblem(H, initial_state=initial_state, time=final_time, aux_operators=[H, magnetization_op, correlation_op])" + "final_time = 30.0\n", + "initial_state = Statevector.from_label(\"001100\")\n", + "problem = TimeEvolutionProblem(\n", + " H,\n", + " initial_state=initial_state,\n", + " time=final_time,\n", + " aux_operators=[H, magnetization_op, correlation_op],\n", + ")" ] }, { @@ -507,10 +519,16 @@ "import matplotlib.pyplot as plt\n", "\n", "fig, axes = plt.subplots(3, sharex=True)\n", - "times = np.linspace(0, final_time, num_timesteps + 1) # includes initial state\n", - "axes[0].plot(times, observables[:, 0], label='First order', marker='x', c='darkmagenta', ls='-', lw=.8)\n", - "axes[1].plot(times, observables[:, 1], label='First order', marker='x', c='darkmagenta', ls='-', lw=.8)\n", - "axes[2].plot(times, observables[:, 2], label='First order', marker='x', c='darkmagenta', ls='-', lw=.8)\n", + "times = np.linspace(0, final_time, num_timesteps + 1) # includes initial state\n", + "axes[0].plot(\n", + " times, observables[:, 0], label=\"First order\", marker=\"x\", c=\"darkmagenta\", ls=\"-\", lw=0.8\n", + ")\n", + "axes[1].plot(\n", + " times, observables[:, 1], label=\"First order\", marker=\"x\", c=\"darkmagenta\", ls=\"-\", lw=0.8\n", + ")\n", + "axes[2].plot(\n", + " times, observables[:, 2], label=\"First order\", marker=\"x\", c=\"darkmagenta\", ls=\"-\", lw=0.8\n", + ")\n", "axes[0].set_ylabel(\"Energy\")\n", "axes[1].set_ylabel(\"Magnetization\")\n", "axes[2].set_ylabel(\"Mean spin correlation\")\n", @@ -547,7 +565,9 @@ "exact_times = np.linspace(0, final_time, 101)\n", "\n", "# We compute the exact evolution using the exp\n", - "exact_evolution = [initial_state.evolve(sc.linalg.expm(-1j * time * H_array)) for time in exact_times]" + "exact_evolution = [\n", + " initial_state.evolve(sc.linalg.expm(-1j * time * H_array)) for time in exact_times\n", + "]" ] }, { @@ -597,11 +617,16 @@ } ], "source": [ - "axes[0].plot(exact_times, exact_energy, c='k', ls=':', label='Exact')\n", - "axes[1].plot(exact_times, exact_magnetization, c='k', ls=':', label='Exact')\n", - "axes[2].plot(exact_times, exact_correlation, c='k', ls=':', label='Exact')\n", + "axes[0].plot(exact_times, exact_energy, c=\"k\", ls=\":\", label=\"Exact\")\n", + "axes[1].plot(exact_times, exact_magnetization, c=\"k\", ls=\":\", label=\"Exact\")\n", + "axes[2].plot(exact_times, exact_correlation, c=\"k\", ls=\":\", label=\"Exact\")\n", "# Select the labels of only the first axis\n", - "legend = fig.legend(*axes[0].get_legend_handles_labels(), bbox_to_anchor=(1., .5), loc='center left', framealpha=.5)\n", + "legend = fig.legend(\n", + " *axes[0].get_legend_handles_labels(),\n", + " bbox_to_anchor=(1.0, 0.5),\n", + " loc=\"center left\",\n", + " framealpha=0.5,\n", + ")\n", "fig.tight_layout()\n", "fig" ] @@ -683,7 +708,8 @@ "circuit = circuit.decompose(reps=2)\n", "\n", "# Let us print some stats\n", - "print(f\"\"\"\n", + "print(\n", + " f\"\"\"\n", "Trotter step with Lie-Trotter\n", "-----------------------------\n", "\n", @@ -691,10 +717,11 @@ " Gate count: {len(circuit)}\n", " Nonlocal gate count: {circuit.num_nonlocal_gates()}\n", " Gate breakdown: {\", \".join([f\"{k.upper()}: {v}\" for k, v in circuit.count_ops().items()])}\n", - "\"\"\")\n", + "\"\"\"\n", + ")\n", "\n", "# And finally draw the circuit\n", - "circuit.draw('mpl')" + "circuit.draw(\"mpl\")" ] }, { @@ -748,14 +775,15 @@ "source": [ "from qiskit.synthesis import SuzukiTrotter\n", "\n", - "second_order_formula = SuzukiTrotter() # if not passed, order defaults to 2\n", + "second_order_formula = SuzukiTrotter() # if not passed, order defaults to 2\n", "trotter_step_second_order = PauliEvolutionGate(H, dt, synthesis=second_order_formula)\n", "circuit = QuantumCircuit(H.num_qubits)\n", "circuit.append(trotter_step_second_order, range(H.num_qubits))\n", "circuit = circuit.decompose(reps=2)\n", "\n", "# Let us print some stats\n", - "print(f\"\"\"\n", + "print(\n", + " f\"\"\"\n", "Trotter step with Suzuki Trotter (2nd order)\n", "--------------------------------------------\n", "\n", @@ -764,10 +792,11 @@ " Nonlocal gate count: {circuit.num_nonlocal_gates()}\n", " Gate breakdown: {\", \".join([f\"{k.upper()}: {v}\" for k, v in circuit.count_ops().items()])}\n", "\n", - "\"\"\")\n", + "\"\"\"\n", + ")\n", "\n", "# And finall\n", - "circuit.draw('mpl')" + "circuit.draw(\"mpl\")" ] }, { @@ -803,7 +832,8 @@ "circuit = circuit.decompose(reps=2)\n", "\n", "# Let us print some stats\n", - "print(f\"\"\"\n", + "print(\n", + " f\"\"\"\n", "Trotter step with Suzuki Trotter (4th order)\n", "--------------------------------------------\n", "\n", @@ -812,7 +842,8 @@ " Nonlocal gate count: {circuit.num_nonlocal_gates()}\n", " Gate breakdown: {\", \".join([f\"{k.upper()}: {v}\" for k, v in circuit.count_ops().items()])}\n", "\n", - "\"\"\")" + "\"\"\"\n", + ")" ] }, { @@ -833,7 +864,12 @@ "from qiskit.synthesis import SuzukiTrotter\n", "\n", "trotter = TrotterQRTE(SuzukiTrotter(order=4), num_timesteps=num_timesteps, estimator=Estimator())\n", - "problem = TimeEvolutionProblem(H, initial_state=initial_state, time=final_time, aux_operators=[H, magnetization_op, correlation_op])\n", + "problem = TimeEvolutionProblem(\n", + " H,\n", + " initial_state=initial_state,\n", + " time=final_time,\n", + " aux_operators=[H, magnetization_op, correlation_op],\n", + ")\n", "result = trotter.evolve(problem)\n", "observables_order4 = np.array(np.array(result.observables)[:, :, 0], dtype=np.float64)" ] @@ -865,13 +901,24 @@ } ], "source": [ - "axes[0].plot(times, observables_order4[:, 0], label='Fourth Order', marker='x', c='limegreen', ls='-', lw=.8)\n", - "axes[1].plot(times, observables_order4[:, 1], label='Fourth Order', marker='x', c='limegreen', ls='-', lw=.8)\n", - "axes[2].plot(times, observables_order4[:, 2], label='Fourth Order', marker='x', c='limegreen', ls='-', lw=.8)\n", + "axes[0].plot(\n", + " times, observables_order4[:, 0], label=\"Fourth Order\", marker=\"x\", c=\"limegreen\", ls=\"-\", lw=0.8\n", + ")\n", + "axes[1].plot(\n", + " times, observables_order4[:, 1], label=\"Fourth Order\", marker=\"x\", c=\"limegreen\", ls=\"-\", lw=0.8\n", + ")\n", + "axes[2].plot(\n", + " times, observables_order4[:, 2], label=\"Fourth Order\", marker=\"x\", c=\"limegreen\", ls=\"-\", lw=0.8\n", + ")\n", "\n", "# Replace the legend\n", "legend.remove()\n", - "legend = fig.legend(*axes[0].get_legend_handles_labels(), bbox_to_anchor=(1., .5), loc='center left', framealpha=.5)\n", + "legend = fig.legend(\n", + " *axes[0].get_legend_handles_labels(),\n", + " bbox_to_anchor=(1.0, 0.5),\n", + " loc=\"center left\",\n", + " framealpha=0.5,\n", + ")\n", "fig" ] }, @@ -925,17 +972,19 @@ "\n", "# An inner list comprehension loops over the terms of the SparsePauliOp magnetization_op,\n", "# which corresponds to the magnetization of each one of the sites\n", - "magnetizations = np.real([[sv.expectation_value(term) for term in magnetization_op] for sv in exact_evolution])\n", + "magnetizations = np.real(\n", + " [[sv.expectation_value(term) for term in magnetization_op] for sv in exact_evolution]\n", + ")\n", "# The shape of magnetizations is (101, 6), containing (t) for each site 0, 1, ..., 5\n", "plt.figure(figsize=(14, 2))\n", "# Create the 2-dim xx and yy arrays tiling the grid with the x and y values\n", "xx, yy = np.meshgrid(exact_times, np.arange(L))\n", - "plt.pcolor(xx, yy, magnetizations.T, vmin=-1, vmax=+1, cmap='RdBu')\n", + "plt.pcolor(xx, yy, magnetizations.T, vmin=-1, vmax=+1, cmap=\"RdBu\")\n", "# Force the figure to have all y ticks from 0 to 5\n", "plt.yticks(np.arange(L))\n", - "plt.ylabel('Site $i$')\n", - "plt.xlabel('Time')\n", - "plt.colorbar(label='$\\\\langle Z_i \\\\rangle$', aspect=1.8)" + "plt.ylabel(\"Site $i$\")\n", + "plt.xlabel(\"Time\")\n", + "plt.colorbar(label=\"$\\\\langle Z_i \\\\rangle$\", aspect=1.8)" ] }, { @@ -985,6 +1034,7 @@ ], "source": [ "import qiskit.tools.jupyter\n", + "\n", "%qiskit_version_table\n", "%qiskit_copyright" ] diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..7d5b2f6f --- /dev/null +++ b/mypy.ini @@ -0,0 +1,11 @@ +[mypy] +warn_unused_configs = True +ignore_missing_imports = True +strict_optional = False +no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True + +### Output +show_error_codes = True +show_error_context = True diff --git a/qiskit_algorithms/amplitude_amplifiers/amplification_problem.py b/qiskit_algorithms/amplitude_amplifiers/amplification_problem.py index 76e85842..dec39bbb 100644 --- a/qiskit_algorithms/amplitude_amplifiers/amplification_problem.py +++ b/qiskit_algorithms/amplitude_amplifiers/amplification_problem.py @@ -14,7 +14,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import Any +from typing import Any, List, cast from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import GroverOperator @@ -167,13 +167,13 @@ def is_good_state(self) -> Callable[[str], bool]: return self._is_good_state # returns None if no is_good_state arg has been set elif isinstance(self._is_good_state, list): if all(isinstance(good_bitstr, str) for good_bitstr in self._is_good_state): - return lambda bitstr: bitstr in self._is_good_state + return lambda bitstr: bitstr in cast(List[str], self._is_good_state) else: return lambda bitstr: all( - bitstr[good_index] == "1" for good_index in self._is_good_state + bitstr[good_index] == "1" for good_index in cast(List[int], self._is_good_state) ) - return lambda bitstr: bitstr in self._is_good_state.probabilities_dict() + return lambda bitstr: bitstr in cast(Statevector, self._is_good_state).probabilities_dict() @is_good_state.setter def is_good_state( diff --git a/qiskit_algorithms/amplitude_amplifiers/amplitude_amplifier.py b/qiskit_algorithms/amplitude_amplifiers/amplitude_amplifier.py index b2a35aa0..ca203019 100644 --- a/qiskit_algorithms/amplitude_amplifiers/amplitude_amplifier.py +++ b/qiskit_algorithms/amplitude_amplifiers/amplitude_amplifier.py @@ -16,8 +16,6 @@ from abc import ABC, abstractmethod from typing import Any -import numpy as np - from .amplification_problem import AmplificationProblem from ..algorithm_result import AlgorithmResult @@ -47,7 +45,7 @@ def __init__(self) -> None: self._top_measurement: str | None = None self._assignment = None self._oracle_evaluation: bool | None = None - self._circuit_results: list[np.ndarray] | list[dict[str, int]] | None = None + self._circuit_results: list[dict[str, int]] | None = None self._max_probability: float | None = None @property @@ -107,12 +105,12 @@ def oracle_evaluation(self, value: bool) -> None: self._oracle_evaluation = value @property - def circuit_results(self) -> list[np.ndarray] | list[dict[str, int]] | None: - """Return the circuit results. Can be a statevector or counts dictionary.""" + def circuit_results(self) -> list[dict[str, int]] | None: + """Return the circuit results.""" return self._circuit_results @circuit_results.setter - def circuit_results(self, value: list[np.ndarray] | list[dict[str, int]]) -> None: + def circuit_results(self, value: list[dict[str, int]]) -> None: """Set the circuit results.""" self._circuit_results = value diff --git a/qiskit_algorithms/amplitude_amplifiers/grover.py b/qiskit_algorithms/amplitude_amplifiers/grover.py index ce2db8a9..56fd2ad1 100644 --- a/qiskit_algorithms/amplitude_amplifiers/grover.py +++ b/qiskit_algorithms/amplitude_amplifiers/grover.py @@ -160,7 +160,7 @@ def __init__( elif isinstance(iterations, int): self._iterations = [iterations] else: - self._iterations = iterations + self._iterations = iterations # type: ignore[assignment] self._sampler = sampler self._sample_from_iterations = sample_from_iterations @@ -248,7 +248,9 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult" circuit_results: dict[str, Any] | Statevector | np.ndarray = { np.binary_repr(k, num_bits): v for k, v in results.quasi_dists[0].items() } - top_measurement, max_probability = max(circuit_results.items(), key=lambda x: x[1]) + top_measurement, max_probability = max( + circuit_results.items(), key=lambda x: x[1] # type: ignore[union-attr] + ) all_circuit_results.append(circuit_results) @@ -274,7 +276,7 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult" result.top_measurement = top_measurement result.assignment = amplification_problem.post_processing(top_measurement) result.oracle_evaluation = oracle_evaluation - result.circuit_results = all_circuit_results + result.circuit_results = all_circuit_results # type: ignore[assignment] result.max_probability = max_probability return result @@ -311,9 +313,9 @@ def construct_circuit( ValueError: If no power is passed and the iterations are not an integer. """ if power is None: - if len(self._iterations) > 1: + if len(self._iterations) > 1: # type: ignore[arg-type] raise ValueError("Please pass ``power`` if the iterations are not an integer.") - power = self._iterations[0] + power = self._iterations[0] # type: ignore[index] qc = QuantumCircuit(problem.oracle.num_qubits, name="Grover circuit") qc.compose(problem.state_preparation, inplace=True) diff --git a/qiskit_algorithms/amplitude_estimators/ae.py b/qiskit_algorithms/amplitude_estimators/ae.py index 836ae753..6444b4b2 100644 --- a/qiskit_algorithms/amplitude_estimators/ae.py +++ b/qiskit_algorithms/amplitude_estimators/ae.py @@ -153,7 +153,7 @@ def construct_circuit( def evaluate_measurements( self, - circuit_results: dict[str, int] | np.ndarray, + circuit_results: dict[str, int], threshold: float = 1e-6, ) -> tuple[dict[float, float], dict[int, float]]: """Evaluate the results from the circuit simulation. @@ -171,13 +171,10 @@ def evaluate_measurements( y measurements with respective probabilities, in this order. """ # compute grid sample and measurement dicts - if isinstance(circuit_results, dict): - if set(map(type, circuit_results.values())) == {int}: - samples, measurements = self._evaluate_count_results(circuit_results) - else: - samples, measurements = self._evaluate_quasi_probabilities_results(circuit_results) + if set(map(type, circuit_results.values())) == {int}: + samples, measurements = self._evaluate_count_results(circuit_results) else: - samples, measurements = self._evaluate_statevector_results(circuit_results) + samples, measurements = self._evaluate_quasi_probabilities_results(circuit_results) # cutoff probabilities below the threshold samples = {a: p for a, p in samples.items() if p > threshold} @@ -185,25 +182,6 @@ def evaluate_measurements( return samples, measurements - def _evaluate_statevector_results(self, statevector): - # map measured results to estimates - measurements = OrderedDict() # type: OrderedDict - num_qubits = int(np.log2(len(statevector))) - for i, amplitude in enumerate(statevector): - b = bin(i)[2:].zfill(num_qubits)[::-1] - y = int(b[: self._m], 2) # chop off all except the evaluation qubits - measurements[y] = measurements.get(y, 0) + np.abs(amplitude) ** 2 - - samples = OrderedDict() # type: OrderedDict - for y, probability in measurements.items(): - if y >= int(self._M / 2): - y = self._M - y - # due to the finite accuracy of the sine, we round the result to 7 decimals - a = np.round(np.power(np.sin(y * np.pi / 2**self._m), 2), decimals=7) - samples[a] = samples.get(a, 0) + probability - - return samples, measurements - def _evaluate_quasi_probabilities_results(self, circuit_results): # construct probabilities measurements = OrderedDict() @@ -326,7 +304,7 @@ def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatio result = AmplitudeEstimationResult() result.num_evaluation_qubits = self._m - result.post_processing = estimation_problem.post_processing + result.post_processing = estimation_problem.post_processing # type: ignore[assignment] circuit = self.construct_circuit(estimation_problem, measurement=True) try: @@ -337,6 +315,7 @@ def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatio shots = ret.metadata[0].get("shots") exact = True + if shots is None: result.circuit_results = ret.quasi_dists[0].binary_probabilities() shots = 1 @@ -348,11 +327,14 @@ def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatio # store shots result.shots = shots - samples, measurements = self.evaluate_measurements(result.circuit_results) + samples, measurements = self.evaluate_measurements( + result.circuit_results # type: ignore[arg-type] + ) result.samples = samples result.samples_processed = { - estimation_problem.post_processing(a): p for a, p in samples.items() + estimation_problem.post_processing(a): p # type: ignore[arg-type,misc] + for a, p in samples.items() } result.measurements = measurements @@ -370,11 +352,14 @@ def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatio # run the MLE post-processing mle = self.compute_mle(result) result.mle = mle - result.mle_processed = estimation_problem.post_processing(mle) + result.mle_processed = estimation_problem.post_processing( + mle # type: ignore[assignment,arg-type] + ) result.confidence_interval = self.compute_confidence_interval(result, exact=exact) result.confidence_interval_processed = tuple( - estimation_problem.post_processing(value) for value in result.confidence_interval + estimation_problem.post_processing(value) # type: ignore[assignment,arg-type] + for value in result.confidence_interval ) return result diff --git a/qiskit_algorithms/amplitude_estimators/amplitude_estimator.py b/qiskit_algorithms/amplitude_estimators/amplitude_estimator.py index 529c49d1..a5c4b97e 100644 --- a/qiskit_algorithms/amplitude_estimators/amplitude_estimator.py +++ b/qiskit_algorithms/amplitude_estimators/amplitude_estimator.py @@ -16,8 +16,6 @@ from abc import abstractmethod, ABC from collections.abc import Callable -import numpy as np - from .estimation_problem import EstimationProblem from ..algorithm_result import AlgorithmResult @@ -41,7 +39,7 @@ class AmplitudeEstimatorResult(AlgorithmResult): def __init__(self) -> None: super().__init__() - self._circuit_results: np.ndarray | dict[str, int] | None = None + self._circuit_results: list[dict[str, int]] | dict[str, int] | None = None self._shots: int | None = None self._estimation: float | None = None self._estimation_processed: float | None = None @@ -51,12 +49,12 @@ def __init__(self) -> None: self._confidence_interval_processed: tuple[float, float] | None = None @property - def circuit_results(self) -> np.ndarray | dict[str, int] | None: + def circuit_results(self) -> list[dict[str, int]] | dict[str, int] | None: """Return the circuit results. Can be a statevector or counts dictionary.""" return self._circuit_results @circuit_results.setter - def circuit_results(self, value: np.ndarray | dict[str, int]) -> None: + def circuit_results(self, value: list[dict[str, int]] | dict[str, int]) -> None: """Set the circuit results.""" self._circuit_results = value diff --git a/qiskit_algorithms/amplitude_estimators/estimation_problem.py b/qiskit_algorithms/amplitude_estimators/estimation_problem.py index 7b0c3d76..7bdd3afe 100644 --- a/qiskit_algorithms/amplitude_estimators/estimation_problem.py +++ b/qiskit_algorithms/amplitude_estimators/estimation_problem.py @@ -36,7 +36,9 @@ def __init__( state_preparation: QuantumCircuit, objective_qubits: int | list[int], grover_operator: QuantumCircuit | None = None, - post_processing: Callable[[float], float] | None = None, + post_processing: Callable[[list[float]], list[float]] + | Callable[[float], float] + | None = None, is_good_state: Callable[[str], bool] | None = None, ) -> None: r""" @@ -100,7 +102,7 @@ def objective_qubits(self, objective_qubits: int | list[int]) -> None: self._objective_qubits = objective_qubits @property - def post_processing(self) -> Callable[[float], float]: + def post_processing(self) -> Callable[[list[float]], list[float]] | Callable[[float], float]: """Apply post processing to the input value. Returns: @@ -112,7 +114,10 @@ def post_processing(self) -> Callable[[float], float]: return self._post_processing @post_processing.setter - def post_processing(self, post_processing: Callable[[float], float] | None) -> None: + def post_processing( + self, + post_processing: Callable[[list[float]], list[float]] | Callable[[float], float] | None, + ) -> None: """Set the post processing function. Args: diff --git a/qiskit_algorithms/amplitude_estimators/fae.py b/qiskit_algorithms/amplitude_estimators/fae.py index 10993f51..5bd482fc 100644 --- a/qiskit_algorithms/amplitude_estimators/fae.py +++ b/qiskit_algorithms/amplitude_estimators/fae.py @@ -13,6 +13,7 @@ """Faster Amplitude Estimation.""" from __future__ import annotations +from typing import cast, Tuple import warnings import numpy as np @@ -235,9 +236,11 @@ def cos_estimate(power, shots): result.success_probability = 1 - (2 * self._maxiter - j_0) * self._delta result.estimation = value - result.estimation_processed = problem.post_processing(value) + result.estimation_processed = problem.post_processing(value) # type: ignore[assignment] result.confidence_interval = value_ci - result.confidence_interval_processed = tuple(problem.post_processing(x) for x in value_ci) + result.confidence_interval_processed = cast( + Tuple[float, float], (problem.post_processing(x) for x in value_ci) + ) result.theta_intervals = theta_cis return result diff --git a/qiskit_algorithms/amplitude_estimators/iae.py b/qiskit_algorithms/amplitude_estimators/iae.py index f11397ef..7e06f983 100644 --- a/qiskit_algorithms/amplitude_estimators/iae.py +++ b/qiskit_algorithms/amplitude_estimators/iae.py @@ -13,7 +13,7 @@ """The Iterative Quantum Amplitude Estimation Algorithm.""" from __future__ import annotations -from typing import cast +from typing import cast, Callable, Tuple import warnings import numpy as np from scipy.stats import beta @@ -235,43 +235,24 @@ def construct_circuit( def _good_state_probability( self, problem: EstimationProblem, - counts_or_statevector: dict[str, int] | np.ndarray, - num_state_qubits: int, - ) -> tuple[int, float] | float: + counts_dict: dict[str, int], + ) -> tuple[int, float]: """Get the probability to measure '1' in the last qubit. Args: problem: The estimation problem, used to obtain the number of objective qubits and the ``is_good_state`` function. - counts_or_statevector: Either a counts-dictionary (with one measured qubit only!) or - the statevector returned from the statevector_simulator. - num_state_qubits: The number of state qubits. + counts_dict: A counts-dictionary (with one measured qubit only!) Returns: - If a dict is given, return (#one-counts, #one-counts/#all-counts), - otherwise Pr(measure '1' in the last qubit). + #one-counts, #one-counts/#all-counts """ - if isinstance(counts_or_statevector, dict): - one_counts = 0 - for state, counts in counts_or_statevector.items(): - if problem.is_good_state(state): - one_counts += counts - - return int(one_counts), one_counts / sum(counts_or_statevector.values()) - else: - statevector = counts_or_statevector - num_qubits = int(np.log2(len(statevector))) # the total number of qubits - - # sum over all amplitudes where the objective qubit is 1 - prob = 0 - for i, amplitude in enumerate(statevector): - # consider only state qubits and revert bit order - bitstr = bin(i)[2:].zfill(num_qubits)[-num_state_qubits:][::-1] - objectives = [bitstr[index] for index in problem.objective_qubits] - if problem.is_good_state(objectives): - prob = prob + np.abs(amplitude) ** 2 - - return prob + one_counts = 0 + for state, counts in counts_dict.items(): + if problem.is_good_state(state): + one_counts += counts + + return int(one_counts), one_counts / sum(counts_dict.values()) def estimate( self, estimation_problem: EstimationProblem @@ -364,9 +345,7 @@ def estimate( } # calculate the probability of measuring '1', 'prob' is a_i in the paper - num_qubits = circuit.num_qubits - circuit.num_ancillas - # type: ignore - one_counts, prob = self._good_state_probability(estimation_problem, counts, num_qubits) + one_counts, prob = self._good_state_probability(estimation_problem, counts) num_one_shots.append(one_counts) @@ -415,24 +394,28 @@ def estimate( a_intervals.append([a_l, a_u]) # get the latest confidence interval for the estimate of a - confidence_interval = tuple(a_intervals[-1]) + confidence_interval = cast(Tuple[float, float], a_intervals[-1]) # the final estimate is the mean of the confidence interval estimation = np.mean(confidence_interval) result = IterativeAmplitudeEstimationResult() result.alpha = self._alpha - result.post_processing = estimation_problem.post_processing + result.post_processing = cast(Callable[[float], float], estimation_problem.post_processing) result.num_oracle_queries = num_oracle_queries - result.estimation = estimation + result.estimation = float(estimation) result.epsilon_estimated = (confidence_interval[1] - confidence_interval[0]) / 2 result.confidence_interval = confidence_interval - result.estimation_processed = estimation_problem.post_processing(estimation) + result.estimation_processed = estimation_problem.post_processing( + estimation # type: ignore[arg-type,assignment] + ) confidence_interval = tuple( - estimation_problem.post_processing(x) for x in confidence_interval + estimation_problem.post_processing(x) # type: ignore[arg-type,assignment] + for x in confidence_interval ) + result.confidence_interval_processed = confidence_interval result.epsilon_estimated_processed = (confidence_interval[1] - confidence_interval[0]) / 2 result.estimate_intervals = a_intervals diff --git a/qiskit_algorithms/amplitude_estimators/mlae.py b/qiskit_algorithms/amplitude_estimators/mlae.py index a49a8508..44a8fb68 100644 --- a/qiskit_algorithms/amplitude_estimators/mlae.py +++ b/qiskit_algorithms/amplitude_estimators/mlae.py @@ -14,7 +14,7 @@ from __future__ import annotations from collections.abc import Sequence -from typing import Callable, List, Tuple +from typing import Callable, List, Tuple, cast import warnings import numpy as np @@ -216,13 +216,14 @@ def compute_confidence_interval( return interval + # TODO: add deprecation warning for num_state_qubits def compute_mle( self, - circuit_results: list[dict[str, int] | np.ndarray], + circuit_results: list[dict[str, int]], estimation_problem: EstimationProblem, - num_state_qubits: int | None = None, + num_state_qubits: int | None = None, # pylint: disable=unused-argument return_counts: bool = False, - ) -> float | tuple[float, list[float]]: + ) -> float | tuple[float, list[int]]: """Compute the MLE via a grid-search. This is a stable approach if sufficient grid-points are used. @@ -231,13 +232,12 @@ def compute_mle( circuit_results: A list of circuit outcomes. Can be counts or statevectors. estimation_problem: The estimation problem containing the evaluation schedule and the number of likelihood function evaluations used to find the minimum. - num_state_qubits: The number of state qubits, required for statevector simulations. return_counts: If True, returns the good counts. - + num_state_qubits: [Deprecated]. Number of qubits to process statevector results. Returns: The MLE for the provided result object. """ - good_counts, all_counts = _get_counts(circuit_results, estimation_problem, num_state_qubits) + good_counts, all_counts = _get_counts(circuit_results, estimation_problem) # search range eps = 1e-15 # to avoid invalid value in log @@ -252,7 +252,7 @@ def loglikelihood(theta): loglik += np.log(np.cos(angle) ** 2) * (all_counts[i] - good_counts[i]) return -loglik - est_theta = self._minimizer(loglikelihood, [search_range]) + est_theta: float = self._minimizer(loglikelihood, [search_range]) if return_counts: return est_theta, good_counts @@ -286,9 +286,7 @@ def estimate( result = MaximumLikelihoodAmplitudeEstimationResult() result.evaluation_schedule = self._evaluation_schedule result.minimizer = self._minimizer - result.post_processing = estimation_problem.post_processing - - shots = 0 + result.post_processing = cast(Callable[[float], float], estimation_problem.post_processing) circuits = self.construct_circuits(estimation_problem, measurement=True) @@ -298,27 +296,29 @@ def estimate( except Exception as exc: raise AlgorithmError("The job was not completed successfully. ") from exc - result.circuit_results = [] + circuit_results = [] shots = ret.metadata[0].get("shots") exact = True if shots is None: for quasi_dist in ret.quasi_dists: circuit_result = quasi_dist.binary_probabilities() - result.circuit_results.append(circuit_result) + circuit_results.append(circuit_result) shots = 1 else: # get counts and construct MLE input for quasi_dist in ret.quasi_dists: counts = {k: round(v * shots) for k, v in quasi_dist.binary_probabilities().items()} - result.circuit_results.append(counts) + circuit_results.append(counts) exact = False result.shots = shots - + result.circuit_results = circuit_results # run maximum likelihood estimation num_state_qubits = circuits[0].num_qubits - circuits[0].num_ancillas - theta, good_counts = self.compute_mle( - result.circuit_results, estimation_problem, num_state_qubits, True + + theta, good_counts = cast( + Tuple[float, List[float]], + self.compute_mle(result.circuit_results, estimation_problem, num_state_qubits, True), ) # store results @@ -338,8 +338,9 @@ def estimate( result, alpha=0.05, kind="fisher", exact=exact ) result.confidence_interval = confidence_interval - result.confidence_interval_processed = tuple( - estimation_problem.post_processing(value) for value in confidence_interval + result.confidence_interval_processed = tuple( # type: ignore[assignment] + estimation_problem.post_processing(value) # type: ignore[arg-type] + for value in confidence_interval ) return result @@ -407,13 +408,15 @@ def fisher_information(self, value: float) -> None: self._fisher_information = value -def _safe_min(array, default=0): +def _safe_min(array: list[float], default: float = 0.0) -> float: if len(array) == 0: return default return np.min(array) -def _safe_max(array, default=(np.pi / 2)): # pylint: disable=superfluous-parens +def _safe_max( + array: list[float], default: float = (np.pi / 2) # pylint: disable=superfluous-parens +) -> float: if len(array) == 0: return default return np.max(array) @@ -555,16 +558,16 @@ def loglikelihood(theta, one_counts, all_counts): # to still provide a valid result use safe_min/max which # then yield [0, pi/2] confint = [_safe_min(above_thres, default=0), _safe_max(above_thres, default=np.pi / 2)] - mapped_confint = tuple(result.post_processing(np.sin(bound) ** 2) for bound in confint) + mapped_confint = cast( + Tuple[float, float], tuple(result.post_processing(np.sin(bound) ** 2) for bound in confint) + ) return mapped_confint def _get_counts( - circuit_results: Sequence[np.ndarray | list[float] | dict[str, int]], - estimation_problem: EstimationProblem, - num_state_qubits: int, -) -> tuple[list[float], list[int]]: + circuit_results: Sequence[dict[str, int]], estimation_problem: EstimationProblem +) -> tuple[list[int], list[int]]: """Get the good and total counts. Returns: @@ -574,33 +577,15 @@ def _get_counts( AlgorithmError: If self.run() has not been called yet. """ one_hits = [] # h_k: how often 1 has been measured, for a power Q^(m_k) - # shots_k: how often has been measured at a power Q^(m_k) - all_hits: np.ndarray | list[float] = [] - if all(isinstance(data, (list, np.ndarray)) for data in circuit_results): - probabilities = [] - num_qubits = int(np.log2(len(circuit_results[0]))) # the total number of qubits - for statevector in circuit_results: - p_k = 0.0 - for i, amplitude in enumerate(statevector): - probability = np.abs(amplitude) ** 2 - # consider only state qubits and revert bit order - bitstr = bin(i)[2:].zfill(num_qubits)[-num_state_qubits:][::-1] - objectives = [bitstr[index] for index in estimation_problem.objective_qubits] - if estimation_problem.is_good_state(objectives): - p_k += probability - probabilities += [p_k] - - one_hits = probabilities - all_hits = np.ones_like(one_hits) - else: - for counts in circuit_results: - all_hits.append(sum(counts.values())) - one_hits.append( - sum( - count - for bitstr, count in counts.items() - if estimation_problem.is_good_state(bitstr) - ) + all_hits = [] + for counts in circuit_results: + all_hits.append(sum(counts.values())) + one_hits.append( + sum( + count + for bitstr, count in counts.items() + if estimation_problem.is_good_state(bitstr) ) + ) return one_hits, all_hits diff --git a/qiskit_algorithms/eigensolvers/eigensolver.py b/qiskit_algorithms/eigensolvers/eigensolver.py index 02d767c9..c60ab65e 100644 --- a/qiskit_algorithms/eigensolvers/eigensolver.py +++ b/qiskit_algorithms/eigensolvers/eigensolver.py @@ -73,9 +73,7 @@ class EigensolverResult(AlgorithmResult): def __init__(self) -> None: super().__init__() self._eigenvalues: np.ndarray | None = None - self._aux_operators_evaluated: list[ - ListOrDict[tuple[complex, dict[str, Any]]] - ] | None = None + self._aux_operators_evaluated: list[ListOrDict[tuple[float, dict[str, Any]]]] | None = None @property def eigenvalues(self) -> np.ndarray | None: @@ -90,7 +88,7 @@ def eigenvalues(self, value: np.ndarray) -> None: @property def aux_operators_evaluated( self, - ) -> list[ListOrDict[tuple[complex, dict[str, Any]]]] | None: + ) -> list[ListOrDict[tuple[float, dict[str, Any]]]] | None: """Return the aux operator expectation values. These values are in fact tuples formatted as (mean, metadata). @@ -99,7 +97,7 @@ def aux_operators_evaluated( @aux_operators_evaluated.setter def aux_operators_evaluated( - self, value: list[ListOrDict[tuple[complex, dict[str, Any]]]] + self, value: list[ListOrDict[tuple[float, dict[str, Any]]]] ) -> None: """Set the aux operator eigenvalues.""" self._aux_operators_evaluated = value diff --git a/qiskit_algorithms/eigensolvers/numpy_eigensolver.py b/qiskit_algorithms/eigensolvers/numpy_eigensolver.py index 0f089839..d1aef88c 100644 --- a/qiskit_algorithms/eigensolvers/numpy_eigensolver.py +++ b/qiskit_algorithms/eigensolvers/numpy_eigensolver.py @@ -14,7 +14,8 @@ from __future__ import annotations -from typing import Callable, Union, List, Optional +from collections.abc import Iterable +from typing import Callable, Union, Tuple, Dict, List, Optional, cast import logging import numpy as np from scipy import sparse as scisparse @@ -29,7 +30,9 @@ logger = logging.getLogger(__name__) -FilterType = Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] +FilterType = Callable[ + [Union[List, np.ndarray], float, Optional[ListOrDict[Tuple[float, Dict[str, float]]]]], bool +] class NumPyEigensolver(Eigensolver): @@ -159,21 +162,22 @@ def _solve_sparse(op_matrix: scisparse.csr_matrix, k: int) -> tuple[np.ndarray, def _solve_dense(op_matrix: np.ndarray) -> tuple[np.ndarray, np.ndarray]: if op_matrix.all() == op_matrix.conj().T.all(): # Operator is Hermitian - return np.linalg.eigh(op_matrix) + return cast(Tuple[np.ndarray, np.ndarray], np.linalg.eigh(op_matrix)) else: - return np.linalg.eig(op_matrix) + return cast(Tuple[np.ndarray, np.ndarray], np.linalg.eig(op_matrix)) @staticmethod def _eval_aux_operators( aux_operators: ListOrDict[BaseOperator], wavefn: np.ndarray, threshold: float = 1e-12, - ) -> ListOrDict[tuple[complex, complex]]: + ) -> ListOrDict[tuple[float, dict[str, float]]]: - values: ListOrDict[tuple[complex, complex]] + values: ListOrDict[tuple[float, dict[str, float]]] # As a list, aux_operators can contain None operators for which None values are returned. # As a dict, the None operators in aux_operators have been dropped in compute_eigenvalues. + key_op_iterator: Iterable[tuple[str | int, BaseOperator]] if isinstance(aux_operators, list): values = [None] * len(aux_operators) key_op_iterator = enumerate(aux_operators) @@ -217,7 +221,7 @@ def _eval_aux_operators( # The metadata includes variance (and, for other eigensolvers, shots). # Since this is an exact computation, there are no shots # and the variance is known to be zero. - values[key] = (value, {"variance": 0.0}) + values[key] = (value, {"variance": 0.0}) # type: ignore[index] return values def compute_eigenvalues( diff --git a/qiskit_algorithms/eigensolvers/vqd.py b/qiskit_algorithms/eigensolvers/vqd.py index c4db44dd..5a3af52d 100644 --- a/qiskit_algorithms/eigensolvers/vqd.py +++ b/qiskit_algorithms/eigensolvers/vqd.py @@ -17,8 +17,8 @@ from __future__ import annotations -from collections.abc import Callable, Sequence -from typing import Any +from collections.abc import Callable, Sequence, Iterable +from typing import Any, cast import logging from time import time @@ -125,7 +125,7 @@ def __init__( k: int = 2, betas: Sequence[float] | None = None, initial_point: Sequence[float] | Sequence[Sequence[float]] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, + callback: Callable[[int, np.ndarray, float, dict[str, Any], int], None] | None = None, ) -> None: """ @@ -148,7 +148,7 @@ def __init__( If ``None`` then VQD will look to the ansatz for a preferred point and if not will simply compute a random one. callback: A callback that can access the intermediate data - during the optimization. Four parameter values are passed to the callback as + during the optimization. Five parameter values are passed to the callback as follows during each evaluation by the optimizer: the evaluation count, the optimizer parameters for the ansatz, the estimated value, the estimation metadata, and the current step. @@ -167,7 +167,7 @@ def __init__( self._eval_count = 0 - @property + @property # type: ignore[override] def initial_point(self) -> Sequence[float] | Sequence[Sequence[float]] | None: """Returns initial point.""" return self._initial_point @@ -214,6 +214,7 @@ def compute_eigenvalues( # Convert the None and zero values when aux_operators is a list. # Drop None and convert zero values when aux_operators is a dict. + key_op_iterator: Iterable[tuple[str | int, BaseOperator]] if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) converted: ListOrDict[BaseOperator] = [zero_op] * len(aux_operators) @@ -222,13 +223,14 @@ def compute_eigenvalues( converted = {} for key, op in key_op_iterator: if op is not None: - converted[key] = zero_op if op == 0 else op + converted[key] = zero_op if op == 0 else op # type: ignore[index] aux_operators = converted else: aux_operators = None + betas = self.betas if self.betas is None: try: upper_bound = sum(np.abs(operator.coeffs)) @@ -241,8 +243,6 @@ def compute_eigenvalues( betas = [upper_bound * 10] * (self.k) logger.info("beta autoevaluated to %s", betas[0]) - else: - betas = self.betas result = self._build_vqd_result() @@ -261,7 +261,9 @@ def compute_eigenvalues( # 0 just means the initial point is ``None`` and ``validate_initial_point`` # will select a random point if num_initial_points <= 1: - initial_point = validate_initial_point(self.initial_point, self.ansatz) + initial_point = validate_initial_point( + self.initial_point, self.ansatz # type: ignore[arg-type] + ) for step in range(1, self.k + 1): if num_initial_points > 1: @@ -285,7 +287,9 @@ def compute_eigenvalues( if callable(optimizer): opt_result = optimizer( # pylint: disable=not-callable - fun=energy_evaluation, x0=initial_point, bounds=bounds + fun=energy_evaluation, # type: ignore[arg-type,call-arg] + x0=initial_point, # type: ignore[arg-type] + bounds=bounds, ) else: # we always want to submit as many estimations per job as possible for minimal @@ -293,7 +297,7 @@ def compute_eigenvalues( was_updated = _set_default_batchsize(optimizer) opt_result = optimizer.minimize( - fun=energy_evaluation, x0=initial_point, bounds=bounds + fun=energy_evaluation, x0=initial_point, bounds=bounds # type: ignore[arg-type] ) # reset to original value @@ -394,7 +398,7 @@ def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray: fidelity_job = self.fidelity.run( batch_size * [self.ansatz] * (step - 1), batched_prev_states, - np.tile(parameters, (step - 1, 1)), + np.tile(parameters, (step - 1, 1)), # type: ignore[arg-type] ) costs = fidelity_job.result().fidelities @@ -430,7 +434,7 @@ def _build_vqd_result() -> VQDResult: result.optimal_values = np.array([]) result.cost_function_evals = np.array([], dtype=int) result.optimizer_times = np.array([]) - result.eigenvalues = [] + result.eigenvalues = [] # type: ignore[assignment] result.optimizer_results = [] result.optimal_circuits = [] return result @@ -444,11 +448,13 @@ def _update_vqd_result( if len(result.optimal_points) > 0 else np.array([opt_result.x]) ) - result.optimal_parameters.append(dict(zip(ansatz.parameters, opt_result.x))) + result.optimal_parameters.append( + dict(zip(ansatz.parameters, cast(np.ndarray, opt_result.x))) + ) result.optimal_values = np.concatenate([result.optimal_values, [opt_result.fun]]) result.cost_function_evals = np.concatenate([result.cost_function_evals, [opt_result.nfev]]) result.optimizer_times = np.concatenate([result.optimizer_times, [eval_time]]) - result.eigenvalues.append(opt_result.fun + 0j) + result.eigenvalues.append(opt_result.fun + 0j) # type: ignore[attr-defined] result.optimizer_results.append(opt_result) result.optimal_circuits.append(ansatz) return result diff --git a/qiskit_algorithms/gradients/base/base_estimator_gradient.py b/qiskit_algorithms/gradients/base/base_estimator_gradient.py index 557645a3..f7ea927b 100644 --- a/qiskit_algorithms/gradients/base/base_estimator_gradient.py +++ b/qiskit_algorithms/gradients/base/base_estimator_gradient.py @@ -187,7 +187,9 @@ def _preprocess( parameter_values and parameters are updated to match the gradient circuit. """ translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] + g_circuits: list[QuantumCircuit] = [] + g_parameter_values: list[Sequence[float]] = [] + g_parameters: list[Sequence[Parameter]] = [] for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): circuit_key = _circuit_key(circuit) if circuit_key not in self._gradient_circuit_cache: @@ -196,7 +198,9 @@ def _preprocess( gradient_circuit = self._gradient_circuit_cache[circuit_key] g_circuits.append(gradient_circuit.gradient_circuit) g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) + _make_gradient_parameter_values( # type: ignore[arg-type] + circuit, gradient_circuit, parameter_value_ + ) ) g_parameters.append(_make_gradient_parameters(gradient_circuit, parameters_)) return g_circuits, g_parameter_values, g_parameters diff --git a/qiskit_algorithms/gradients/base/base_qgt.py b/qiskit_algorithms/gradients/base/base_qgt.py index 9d71436e..2e254a8f 100644 --- a/qiskit_algorithms/gradients/base/base_qgt.py +++ b/qiskit_algorithms/gradients/base/base_qgt.py @@ -198,7 +198,9 @@ def _preprocess( parameter_values and parameters are updated to match the gradient circuit. """ translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] + g_circuits: list[QuantumCircuit] = [] + g_parameter_values: list[Sequence[float]] = [] + g_parameters: list[Sequence[Parameter]] = [] for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): circuit_key = _circuit_key(circuit) if circuit_key not in self._gradient_circuit_cache: @@ -207,7 +209,9 @@ def _preprocess( gradient_circuit = self._gradient_circuit_cache[circuit_key] g_circuits.append(gradient_circuit.gradient_circuit) g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) + _make_gradient_parameter_values( # type: ignore[arg-type] + circuit, gradient_circuit, parameter_value_ + ) ) g_parameters_ = [ g_param @@ -253,7 +257,7 @@ def _postprocess( for param in gradient_circuit.gradient_circuit.parameters if param in g_parameters ] - g_parameter_indices = {param: i for i, param in enumerate(g_parameter_indices)} + g_parameter_indices_d = {param: i for i, param in enumerate(g_parameter_indices)} rows, cols = np.triu_indices(len(parameters_)) for row, col in zip(rows, cols): for g_parameter1, coeff1 in gradient_circuit.parameter_map[parameters_[row]]: @@ -278,7 +282,8 @@ def _postprocess( float(bound_coeff1) * float(bound_coeff2) * results.qgts[idx][ - g_parameter_indices[g_parameter1], g_parameter_indices[g_parameter2] + g_parameter_indices_d[g_parameter1], + g_parameter_indices_d[g_parameter2], ] ) diff --git a/qiskit_algorithms/gradients/base/base_sampler_gradient.py b/qiskit_algorithms/gradients/base/base_sampler_gradient.py index c3cbfdce..1114b5f0 100644 --- a/qiskit_algorithms/gradients/base/base_sampler_gradient.py +++ b/qiskit_algorithms/gradients/base/base_sampler_gradient.py @@ -127,7 +127,7 @@ def _preprocess( parameter_values: Sequence[Sequence[float]], parameters: Sequence[Sequence[Parameter]], supported_gates: Sequence[str], - ) -> tuple[Sequence[QuantumCircuit], Sequence[Sequence[float]], Sequence[set[Parameter]]]: + ) -> tuple[Sequence[QuantumCircuit], Sequence[Sequence[float]], Sequence[Sequence[Parameter]]]: """Preprocess the gradient. This makes a gradient circuit for each circuit. The gradient circuit is a transpiled circuit by using the supported gates, and has unique parameters. ``parameter_values`` and ``parameters`` are also updated to match the gradient circuit. @@ -144,7 +144,9 @@ def _preprocess( parameter_values and parameters are updated to match the gradient circuit. """ translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] + g_circuits: list[QuantumCircuit] = [] + g_parameter_values: list[Sequence[float]] = [] + g_parameters: list[Sequence[Parameter]] = [] for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): circuit_key = _circuit_key(circuit) if circuit_key not in self._gradient_circuit_cache: @@ -153,7 +155,9 @@ def _preprocess( gradient_circuit = self._gradient_circuit_cache[circuit_key] g_circuits.append(gradient_circuit.gradient_circuit) g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) + _make_gradient_parameter_values( # type: ignore[arg-type] + circuit, gradient_circuit, parameter_value_ + ) ) g_parameters.append(_make_gradient_parameters(gradient_circuit, parameters_)) return g_circuits, g_parameter_values, g_parameters diff --git a/qiskit_algorithms/gradients/base/qgt_result.py b/qiskit_algorithms/gradients/base/qgt_result.py index 3ab600fb..f7a9d80b 100644 --- a/qiskit_algorithms/gradients/base/qgt_result.py +++ b/qiskit_algorithms/gradients/base/qgt_result.py @@ -33,7 +33,7 @@ class QGTResult: """The QGT.""" derivative_type: DerivativeType """The type of derivative.""" - metadata: list[dict[str, Any]] + metadata: list[dict[str, Any]] | list[list[dict[str, Any]]] """Additional information about the job.""" options: Options """Primitive runtime options for the execution of the job.""" diff --git a/qiskit_algorithms/gradients/base/sampler_gradient_result.py b/qiskit_algorithms/gradients/base/sampler_gradient_result.py index 4efc75d5..393319ab 100644 --- a/qiskit_algorithms/gradients/base/sampler_gradient_result.py +++ b/qiskit_algorithms/gradients/base/sampler_gradient_result.py @@ -27,7 +27,7 @@ class SamplerGradientResult: gradients: list[list[dict[int, float]]] """The gradients of the sample probabilities.""" - metadata: list[dict[str, Any]] + metadata: list[dict[str, Any]] | list[list[dict[str, Any]]] """Additional information about the job.""" options: Options """Primitive runtime options for the execution of the job.""" diff --git a/qiskit_algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py b/qiskit_algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py index 4c64524e..1be05900 100644 --- a/qiskit_algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py +++ b/qiskit_algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py @@ -89,7 +89,7 @@ def __init__( self._lin_comb_cache: dict[tuple, dict[Parameter, QuantumCircuit]] = {} super().__init__(estimator, options, derivative_type=derivative_type) - @BaseEstimatorGradient.derivative_type.setter + @BaseEstimatorGradient.derivative_type.setter # type: ignore[attr-defined] def derivative_type(self, derivative_type: DerivativeType) -> None: """Set the derivative type.""" self._derivative_type = derivative_type diff --git a/qiskit_algorithms/gradients/lin_comb/lin_comb_qgt.py b/qiskit_algorithms/gradients/lin_comb/lin_comb_qgt.py index 77c88fdc..a00c7b67 100644 --- a/qiskit_algorithms/gradients/lin_comb/lin_comb_qgt.py +++ b/qiskit_algorithms/gradients/lin_comb/lin_comb_qgt.py @@ -140,7 +140,8 @@ def _run_unique( ) -> QGTResult: """Compute the QGTs on the given circuits.""" job_circuits, job_observables, job_param_values, metadata = [], [], [], [] - all_n, all_m, phase_fixes = [], [], [] + all_n, all_m = [], [] + phase_fixes: list[int | np.ndarray] = [] for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): # Prepare circuits for the gradient of the specified parameters. diff --git a/qiskit_algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py b/qiskit_algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py index 17e34c4e..30083d53 100644 --- a/qiskit_algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py +++ b/qiskit_algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py @@ -100,6 +100,7 @@ def _run_unique( all_n = [] for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): # Prepare circuits for the gradient of the specified parameters. + # TODO: why is this not wrapped into another list level like it is done elsewhere? metadata.append({"parameters": parameters_}) circuit_key = _circuit_key(circuit) if circuit_key not in self._lin_comb_cache: diff --git a/qiskit_algorithms/gradients/reverse/bind.py b/qiskit_algorithms/gradients/reverse/bind.py index c9a2c949..575a1e1e 100644 --- a/qiskit_algorithms/gradients/reverse/bind.py +++ b/qiskit_algorithms/gradients/reverse/bind.py @@ -51,3 +51,5 @@ def bind( if not inplace: return bound if return_list else bound[0] + + return None diff --git a/qiskit_algorithms/gradients/reverse/reverse_gradient.py b/qiskit_algorithms/gradients/reverse/reverse_gradient.py index 01801a47..e14e7755 100644 --- a/qiskit_algorithms/gradients/reverse/reverse_gradient.py +++ b/qiskit_algorithms/gradients/reverse/reverse_gradient.py @@ -14,6 +14,7 @@ from __future__ import annotations from collections.abc import Sequence +from typing import cast import logging import numpy as np @@ -66,7 +67,7 @@ def __init__(self, derivative_type: DerivativeType = DerivativeType.REAL): dummy_estimator = Estimator() # this is required by the base class, but not used super().__init__(dummy_estimator, derivative_type=derivative_type) - @BaseEstimatorGradient.derivative_type.setter + @BaseEstimatorGradient.derivative_type.setter # type: ignore[attr-defined] def derivative_type(self, derivative_type: DerivativeType) -> None: """Set the derivative type.""" self._derivative_type = derivative_type @@ -148,7 +149,7 @@ def _run_unique( bind(gate, parameter_binds, inplace=True) # iterate the state variable - unitary_j_dagger = bind(unitary_j, parameter_binds).inverse() + unitary_j_dagger = cast(QuantumCircuit, bind(unitary_j, parameter_binds)).inverse() phi = phi.evolve(unitary_j_dagger) # compute current gradient diff --git a/qiskit_algorithms/gradients/reverse/reverse_qgt.py b/qiskit_algorithms/gradients/reverse/reverse_qgt.py index 46a2e2d0..817b5b34 100644 --- a/qiskit_algorithms/gradients/reverse/reverse_qgt.py +++ b/qiskit_algorithms/gradients/reverse/reverse_qgt.py @@ -14,6 +14,7 @@ from __future__ import annotations from collections.abc import Sequence +from typing import cast, List import logging import numpy as np @@ -81,21 +82,21 @@ def _run( # pylint: disable=arguments-renamed self, circuits: Sequence[QuantumCircuit], parameter_values: Sequence[Sequence[float]], - parameter_sets: Sequence[set[Parameter]], + parameters: Sequence[Sequence[Parameter]], **options, ) -> QGTResult: """Compute the QGT on the given circuits.""" g_circuits, g_parameter_values, g_parameter_sets = self._preprocess( - circuits, parameter_values, parameter_sets, self.SUPPORTED_GATES + circuits, parameter_values, parameters, self.SUPPORTED_GATES ) results = self._run_unique(g_circuits, g_parameter_values, g_parameter_sets, **options) - return self._postprocess(results, circuits, parameter_values, parameter_sets) + return self._postprocess(results, circuits, parameter_values, parameters) def _run_unique( self, circuits: Sequence[QuantumCircuit], parameter_values: Sequence[Sequence[float]], - parameter_sets: Sequence[set[Parameter]], + parameter_sets: Sequence[Sequence[Parameter]], **options, # pylint: disable=unused-argument ) -> QGTResult: num_qgts = len(circuits) @@ -120,7 +121,7 @@ def _run_unique( # initialize the state variables -- naming convention is the same as the paper parameter_binds = dict(zip(circuit.parameters, values)) - bound_unitaries = bind(unitaries, parameter_binds) + bound_unitaries = cast(List[QuantumCircuit], bind(unitaries, parameter_binds)) chi = Statevector(bound_unitaries[0]) psi = chi.copy() diff --git a/qiskit_algorithms/gradients/utils.py b/qiskit_algorithms/gradients/utils.py index d9207683..2ad9b028 100644 --- a/qiskit_algorithms/gradients/utils.py +++ b/qiskit_algorithms/gradients/utils.py @@ -88,7 +88,7 @@ class LinearCombGradientCircuit: ################################################################################ def _make_param_shift_parameter_values( # pylint: disable=invalid-name circuit: QuantumCircuit, - parameter_values: np.ndarray | list[float], + parameter_values: np.ndarray | Sequence[float], parameters: Sequence[Parameter], ) -> list[np.ndarray]: """Returns a list of parameter values with offsets for parameter shift rule. @@ -329,7 +329,7 @@ def _assign_unique_parameters( def _make_gradient_parameter_values( circuit: QuantumCircuit, gradient_circuit: GradientCircuit, - parameter_values: np.ndarray, + parameter_values: np.ndarray | Sequence[float], ) -> np.ndarray: """Makes parameter values for the gradient circuit. diff --git a/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py b/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py index 72946a7f..9d18b023 100644 --- a/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py +++ b/qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py @@ -18,7 +18,6 @@ import re import logging -from typing import Any import numpy as np @@ -131,7 +130,7 @@ def __init__( self._excitation_pool: list[BaseOperator] = [] self._excitation_list: list[BaseOperator] = [] - @property + @property # type: ignore[override] def initial_point(self) -> Sequence[float] | None: """Returns the initial point of the internal :class:`~.VQE` solver.""" return self.solver.initial_point @@ -149,7 +148,7 @@ def _compute_gradients( self, theta: list[float], operator: BaseOperator, - ) -> list[tuple[complex, dict[str, Any]]]: + ) -> ListOrDict[tuple[float, dict[str, BaseOperator]]]: """ Computes the gradients for all available excitation operators. @@ -225,7 +224,7 @@ def compute_minimum_eigenvalue( prev_raw_vqe_result: VQEResult | None = None raw_vqe_result: VQEResult | None = None theta: list[float] = [] - max_grad: tuple[complex, dict[str, Any] | None] = (0.0, None) + max_grad: tuple[float, dict[str, BaseOperator] | None] = (0.0, None) self._excitation_list = [] history: list[complex] = [] iteration = 0 @@ -236,8 +235,9 @@ def compute_minimum_eigenvalue( logger.debug("Computing gradients") cur_grads = self._compute_gradients(theta, operator) # pick maximum gradient - max_grad_index, max_grad = max( - enumerate(cur_grads), key=lambda item: np.abs(item[1][0]) + max_grad_index, max_grad = max( # type: ignore[assignment] + enumerate(cur_grads), + key=lambda item: np.abs(item[1][0]), # type: ignore[call-overload] ) logger.info( "Found maximum gradient %s at index %s", @@ -313,15 +313,18 @@ def compute_minimum_eigenvalue( result.combine(raw_vqe_result) result.num_iterations = iteration result.final_max_gradient = max_grad[0] - result.termination_criterion = termination_criterion + result.termination_criterion = termination_criterion # type: ignore[assignment] result.eigenvalue_history = history # once finished evaluate auxiliary operators if any if aux_operators is not None: aux_values = estimate_observables( - self.solver.estimator, self.solver.ansatz, aux_operators, result.optimal_point + self.solver.estimator, + self.solver.ansatz, + aux_operators, + result.optimal_point, # type: ignore[arg-type] ) - result.aux_operators_evaluated = aux_values + result.aux_operators_evaluated = aux_values # type: ignore[assignment] logger.info("The final eigenvalue is: %s", str(result.eigenvalue)) self.solver.ansatz.operators = self._excitation_pool @@ -336,7 +339,7 @@ def __init__(self) -> None: self._num_iterations: int | None = None self._final_max_gradient: float | None = None self._termination_criterion: str = "" - self._eigenvalue_history: list[float] | None = None + self._eigenvalue_history: list[complex] | None = None @property def num_iterations(self) -> int: @@ -369,7 +372,7 @@ def termination_criterion(self, value: str) -> None: self._termination_criterion = value @property - def eigenvalue_history(self) -> list[float]: + def eigenvalue_history(self) -> list[complex]: """Returns the history of computed eigenvalues. The history's length matches the number of iterations and includes the final computed value. @@ -377,6 +380,6 @@ def eigenvalue_history(self) -> list[float]: return self._eigenvalue_history @eigenvalue_history.setter - def eigenvalue_history(self, eigenvalue_history: list[float]) -> None: + def eigenvalue_history(self, eigenvalue_history: list[complex]) -> None: """Sets the history of computed eigenvalues.""" self._eigenvalue_history = eigenvalue_history diff --git a/qiskit_algorithms/minimum_eigensolvers/diagonal_estimator.py b/qiskit_algorithms/minimum_eigensolvers/diagonal_estimator.py index 64988862..a1fd2f8c 100644 --- a/qiskit_algorithms/minimum_eigensolvers/diagonal_estimator.py +++ b/qiskit_algorithms/minimum_eigensolvers/diagonal_estimator.py @@ -14,7 +14,7 @@ from __future__ import annotations -from collections.abc import Callable, Sequence, Mapping +from collections.abc import Callable, Sequence, Mapping, Iterable from typing import Any from dataclasses import dataclass @@ -43,7 +43,7 @@ class _DiagonalEstimator(BaseEstimator): def __init__( self, sampler: BaseSampler, - aggregation: float | Callable[[Sequence[tuple[float, float]]], float] | None = None, + aggregation: float | Callable[[Iterable[tuple[float, float]]], float] | None = None, callback: Callable[[Sequence[Mapping[str, Any]]], None] | None = None, **options, ) -> None: @@ -65,8 +65,8 @@ def __init__( self.aggregation = aggregation self.callback = callback - self._circuit_ids = {} - self._observable_ids = {} + self._circuit_ids: dict[int, QuantumCircuit] = {} + self._observable_ids: dict[int, BaseOperator] = {} def _run( self, @@ -119,7 +119,7 @@ def _call( samples = sampler_result.quasi_dists # a list of dictionaries containing: {state: (measurement probability, value)} - evaluations = [ + evaluations: list[dict[int, tuple[float, float]]] = [ { state: (probability, _evaluate_sparsepauli(state, self._observables[i])) for state, probability in sampled.items() @@ -151,7 +151,7 @@ def _call( ) -def _get_cvar_aggregation(alpha): +def _get_cvar_aggregation(alpha: float | None) -> Callable[[Iterable[tuple[float, float]]], float]: """Get the aggregation function for CVaR with confidence level ``alpha``.""" if alpha is None: alpha = 1 @@ -161,17 +161,17 @@ def _get_cvar_aggregation(alpha): # if alpha is close to 1 we can avoid the sorting if np.isclose(alpha, 1): - def aggregate(measurements): + def aggregate(measurements: Iterable[tuple[float, float]]) -> float: return sum(probability * value for probability, value in measurements) else: - def aggregate(measurements): + def aggregate(measurements: Iterable[tuple[float, float]]) -> float: # sort by values sorted_measurements = sorted(measurements, key=lambda x: x[1]) - accumulated_percent = 0 # once alpha is reached, stop - cvar = 0 + accumulated_percent = 0.0 # once alpha is reached, stop + cvar = 0.0 for probability, value in sorted_measurements: cvar += value * min(probability, alpha - accumulated_percent) accumulated_percent += probability @@ -186,7 +186,7 @@ def aggregate(measurements): _PARITY = np.array([-1 if bin(i).count("1") % 2 else 1 for i in range(256)], dtype=np.complex128) -def _evaluate_sparsepauli(state: int, observable: SparsePauliOp) -> complex: +def _evaluate_sparsepauli(state: int, observable: SparsePauliOp) -> float: packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder="little") state_bytes = np.frombuffer(state.to_bytes(packed_uint8.shape[1], "little"), dtype=np.uint8) reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1) diff --git a/qiskit_algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py b/qiskit_algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py index 8acafebe..6e564c7c 100644 --- a/qiskit_algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py +++ b/qiskit_algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py @@ -14,7 +14,7 @@ from __future__ import annotations -from typing import Callable, List, Union, Optional +from typing import Callable, Union, Tuple, Dict, List, Optional import logging import numpy as np @@ -28,7 +28,9 @@ logger = logging.getLogger(__name__) # future type annotations not supported in type aliases in 3.8 -FilterType = Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] +FilterType = Callable[ + [Union[List, np.ndarray], float, Optional[ListOrDict[Tuple[float, Dict[str, float]]]]], bool +] class NumPyMinimumEigensolver(MinimumEigensolver): @@ -81,7 +83,9 @@ def compute_minimum_eigenvalue( result.eigenvalue = eigensolver_result.eigenvalues[0] result.eigenstate = eigensolver_result.eigenstates[0] if eigensolver_result.aux_operators_evaluated: - result.aux_operators_evaluated = eigensolver_result.aux_operators_evaluated[0] + result.aux_operators_evaluated = eigensolver_result.aux_operators_evaluated[ + 0 + ] # type: ignore[assignment] logger.debug("NumPy minimum eigensolver result: %s", result) diff --git a/qiskit_algorithms/minimum_eigensolvers/qaoa.py b/qiskit_algorithms/minimum_eigensolvers/qaoa.py index 8953b795..9bbc5fd0 100644 --- a/qiskit_algorithms/minimum_eigensolvers/qaoa.py +++ b/qiskit_algorithms/minimum_eigensolvers/qaoa.py @@ -129,7 +129,7 @@ def __init__( sampler=sampler, ansatz=None, optimizer=optimizer, - initial_point=initial_point, + initial_point=initial_point, # type: ignore[arg-type] aggregation=aggregation, callback=callback, ) diff --git a/qiskit_algorithms/minimum_eigensolvers/sampling_vqe.py b/qiskit_algorithms/minimum_eigensolvers/sampling_vqe.py index a934a4cf..f6661400 100644 --- a/qiskit_algorithms/minimum_eigensolvers/sampling_vqe.py +++ b/qiskit_algorithms/minimum_eigensolvers/sampling_vqe.py @@ -152,7 +152,7 @@ def __init__( # this has to go via getters and setters due to the VariationalAlgorithm interface self._initial_point = initial_point - @property + @property # type: ignore[override] def initial_point(self) -> Sequence[float] | None: """Return the initial point.""" return self._initial_point @@ -202,21 +202,27 @@ def compute_minimum_eigenvalue( bounds = validate_bounds(self.ansatz) - evaluate_energy, best_measurement = self._get_evaluate_energy( + # NOTE: we type ignore below because the `return_best_measurement=True` is guaranteed to + # return a tuple + evaluate_energy, best_measurement = self._get_evaluate_energy( # type: ignore[misc] operator, self.ansatz, return_best_measurement=True ) start_time = time() if callable(self.optimizer): - optimizer_result = self.optimizer(fun=evaluate_energy, x0=initial_point, bounds=bounds) + optimizer_result = self.optimizer( + fun=evaluate_energy, # type: ignore[call-arg,arg-type] + x0=initial_point, # type: ignore[arg-type] + bounds=bounds, + ) else: # we always want to submit as many estimations per job as possible for minimal # overhead on the hardware was_updated = _set_default_batchsize(self.optimizer) optimizer_result = self.optimizer.minimize( - fun=evaluate_energy, x0=initial_point, bounds=bounds + fun=evaluate_energy, x0=initial_point, bounds=bounds # type: ignore[arg-type] ) # reset to original value @@ -238,7 +244,7 @@ def compute_minimum_eigenvalue( _DiagonalEstimator(sampler=self.sampler), self.ansatz, aux_operators, - optimizer_result.x, + optimizer_result.x, # type: ignore[arg-type] ) else: aux_operators_evaluated = None @@ -246,7 +252,7 @@ def compute_minimum_eigenvalue( return self._build_sampling_vqe_result( self.ansatz.copy(), optimizer_result, - aux_operators_evaluated, + aux_operators_evaluated, # type: ignore[arg-type] best_measurement, final_state, optimizer_time, @@ -295,7 +301,9 @@ def store_best_measurement(best): best_measurement["best"] = best_i estimator = _DiagonalEstimator( - sampler=self.sampler, callback=store_best_measurement, aggregation=self.aggregation + sampler=self.sampler, + callback=store_best_measurement, + aggregation=self.aggregation, # type: ignore[arg-type] ) def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: @@ -335,11 +343,13 @@ def _build_sampling_vqe_result( result = SamplingVQEResult() result.eigenvalue = optimizer_result.fun result.cost_function_evals = optimizer_result.nfev - result.optimal_point = optimizer_result.x - result.optimal_parameters = dict(zip(self.ansatz.parameters, optimizer_result.x)) + result.optimal_point = optimizer_result.x # type: ignore[assignment] + result.optimal_parameters = dict( + zip(self.ansatz.parameters, optimizer_result.x) # type: ignore[arg-type] + ) result.optimal_value = optimizer_result.fun result.optimizer_time = optimizer_time - result.aux_operators_evaluated = aux_operators_evaluated + result.aux_operators_evaluated = aux_operators_evaluated # type: ignore[assignment] result.optimizer_result = optimizer_result result.best_measurement = best_measurement["best"] result.eigenstate = final_state diff --git a/qiskit_algorithms/minimum_eigensolvers/vqe.py b/qiskit_algorithms/minimum_eigensolvers/vqe.py index 4d7af6eb..ea67d23e 100644 --- a/qiskit_algorithms/minimum_eigensolvers/vqe.py +++ b/qiskit_algorithms/minimum_eigensolvers/vqe.py @@ -150,7 +150,7 @@ def __init__( self.initial_point = initial_point self.callback = callback - @property + @property # type: ignore[override] def initial_point(self) -> Sequence[float] | None: return self._initial_point @@ -181,7 +181,10 @@ def compute_minimum_eigenvalue( # perform optimization if callable(self.optimizer): optimizer_result = self.optimizer( - fun=evaluate_energy, x0=initial_point, jac=evaluate_gradient, bounds=bounds + fun=evaluate_energy, # type: ignore[arg-type] + x0=initial_point, # type: ignore[arg-type] + jac=evaluate_gradient, + bounds=bounds, ) else: # we always want to submit as many estimations per job as possible for minimal @@ -189,7 +192,10 @@ def compute_minimum_eigenvalue( was_updated = _set_default_batchsize(self.optimizer) optimizer_result = self.optimizer.minimize( - fun=evaluate_energy, x0=initial_point, jac=evaluate_gradient, bounds=bounds + fun=evaluate_energy, # type: ignore[arg-type] + x0=initial_point, # type: ignore[arg-type] + jac=evaluate_gradient, # type: ignore[arg-type] + bounds=bounds, ) # reset to original value @@ -206,13 +212,19 @@ def compute_minimum_eigenvalue( if aux_operators is not None: aux_operators_evaluated = estimate_observables( - self.estimator, self.ansatz, aux_operators, optimizer_result.x + self.estimator, + self.ansatz, + aux_operators, + optimizer_result.x, # type: ignore[arg-type] ) else: aux_operators_evaluated = None return self._build_vqe_result( - self.ansatz, optimizer_result, aux_operators_evaluated, optimizer_time + self.ansatz, + optimizer_result, + aux_operators_evaluated, # type: ignore[arg-type] + optimizer_time, ) @classmethod @@ -290,7 +302,9 @@ def _get_evaluate_gradient( def evaluate_gradient(parameters: np.ndarray) -> np.ndarray: # broadcasting not required for the estimator gradients try: - job = self.gradient.run([ansatz], [operator], [parameters]) + job = self.gradient.run( + [ansatz], [operator], [parameters] # type: ignore[list-item] + ) gradients = job.result().gradients except Exception as exc: raise AlgorithmError("The primitive job to evaluate the gradient failed!") from exc @@ -330,11 +344,13 @@ def _build_vqe_result( result.optimal_circuit = ansatz.copy() result.eigenvalue = optimizer_result.fun result.cost_function_evals = optimizer_result.nfev - result.optimal_point = optimizer_result.x - result.optimal_parameters = dict(zip(self.ansatz.parameters, optimizer_result.x)) + result.optimal_point = optimizer_result.x # type: ignore[assignment] + result.optimal_parameters = dict( + zip(self.ansatz.parameters, optimizer_result.x) # type: ignore[arg-type] + ) result.optimal_value = optimizer_result.fun result.optimizer_time = optimizer_time - result.aux_operators_evaluated = aux_operators_evaluated + result.aux_operators_evaluated = aux_operators_evaluated # type: ignore[assignment] result.optimizer_result = optimizer_result return result diff --git a/qiskit_algorithms/observables_evaluator.py b/qiskit_algorithms/observables_evaluator.py index ca171771..ae125bfb 100644 --- a/qiskit_algorithms/observables_evaluator.py +++ b/qiskit_algorithms/observables_evaluator.py @@ -33,7 +33,7 @@ def estimate_observables( observables: ListOrDict[BaseOperator], parameter_values: Sequence[float] | None = None, threshold: float = 1e-12, -) -> ListOrDict[tuple[complex, dict[str, Any]]]: +) -> ListOrDict[tuple[float, dict[str, Any]]]: """ Accepts a sequence of operators and calculates their expectation values - means and metadata. They are calculated with respect to a quantum state provided. A user @@ -64,10 +64,11 @@ def estimate_observables( if len(observables_list) > 0: observables_list = _handle_zero_ops(observables_list) quantum_state = [quantum_state] * len(observables) + parameter_values_: Sequence[float] | Sequence[Sequence[float]] | None = parameter_values if parameter_values is not None: - parameter_values = [parameter_values] * len(observables) + parameter_values_ = [parameter_values] * len(observables) try: - estimator_job = estimator.run(quantum_state, observables_list, parameter_values) + estimator_job = estimator.run(quantum_state, observables_list, parameter_values_) expectation_values = estimator_job.result().values except Exception as exc: raise AlgorithmError("The primitive job failed!") from exc @@ -97,9 +98,9 @@ def _handle_zero_ops( def _prepare_result( - observables_results: list[tuple[complex, dict]], + observables_results: list[tuple[float, dict]], observables: ListOrDict[BaseOperator], -) -> ListOrDict[tuple[complex, dict[str, Any]]]: +) -> ListOrDict[tuple[float, dict[str, Any]]]: """ Prepares a list of tuples of eigenvalues and metadata tuples from ``observables_results`` and ``observables``. @@ -113,14 +114,16 @@ def _prepare_result( A list or a dictionary of tuples (mean, metadata). """ + observables_eigenvalues: ListOrDict[tuple[float, dict]] + if isinstance(observables, list): - # by construction, all None values will be overwritten - observables_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len(observables) - key_value_iterator = enumerate(observables_results) + observables_eigenvalues = [] + for value in observables_results: + observables_eigenvalues.append(value) + else: observables_eigenvalues = {} - key_value_iterator = zip(observables.keys(), observables_results) + for key, value in zip(observables.keys(), observables_results): + observables_eigenvalues[key] = value - for key, value in key_value_iterator: - observables_eigenvalues[key] = value return observables_eigenvalues diff --git a/qiskit_algorithms/optimizers/adam_amsgrad.py b/qiskit_algorithms/optimizers/adam_amsgrad.py index 4132b5a0..6deea91b 100644 --- a/qiskit_algorithms/optimizers/adam_amsgrad.py +++ b/qiskit_algorithms/optimizers/adam_amsgrad.py @@ -188,7 +188,7 @@ def load_params(self, load_dir: str) -> None: m = m[1:-1] self._m = np.fromstring(m, dtype=float, sep=" ") t = t[1:-1] - self._t = np.fromstring(t, dtype=int, sep=" ") + self._t = int(np.fromstring(t, dtype=int, sep=" ")) def minimize( self, diff --git a/qiskit_algorithms/optimizers/aqgd.py b/qiskit_algorithms/optimizers/aqgd.py index f74bbda4..cf5695c8 100644 --- a/qiskit_algorithms/optimizers/aqgd.py +++ b/qiskit_algorithms/optimizers/aqgd.py @@ -223,7 +223,7 @@ def _converged_objective(self, objval: float, tol: float, window_size: int) -> b # and current windowed average of objective values prev_avg = np.mean(self._prev_loss[:window_size]) curr_avg = np.mean(self._prev_loss[1 : window_size + 1]) - self._avg_objval = curr_avg + self._avg_objval = curr_avg # type: ignore[assignment] # Update window of objective values # (Remove earliest value) @@ -333,7 +333,7 @@ def minimize( objval, gradient = self._compute_objective_fn_and_gradient(params, fun) else: objval = fun(params) - gradient = jac(params) + gradient = jac(params) # type: ignore[assignment] logger.info( " Iter: %4d | Obj: %11.6f | Grad Norm: %f", diff --git a/qiskit_algorithms/optimizers/gradient_descent.py b/qiskit_algorithms/optimizers/gradient_descent.py index 17426a3f..d9561b19 100644 --- a/qiskit_algorithms/optimizers/gradient_descent.py +++ b/qiskit_algorithms/optimizers/gradient_descent.py @@ -213,7 +213,7 @@ def __init__( ) self.learning_rate = learning_rate - @property + @property # type: ignore[override] def state(self) -> GradientDescentState: """Return the current state of the optimizer.""" return self._state @@ -259,7 +259,7 @@ def _callback_wrapper(self) -> None: if self.callback is not None: self.callback( self.state.nfev, - self.state.x, + self.state.x, # type: ignore[arg-type] self.state.fun(self.state.x), self.state.stepsize, ) @@ -267,13 +267,10 @@ def _callback_wrapper(self) -> None: @property def settings(self) -> dict[str, Any]: # if learning rate or perturbation are custom iterators expand them + learning_rate = self.learning_rate if callable(self.learning_rate): iterator = self.learning_rate() - learning_rate: float | np.ndarray = np.array( - [next(iterator) for _ in range(self.maxiter)] - ) - else: - learning_rate = self.learning_rate + learning_rate = np.array([next(iterator) for _ in range(self.maxiter)]) return { "maxiter": self.maxiter, @@ -305,10 +302,10 @@ def tell(self, ask_data: AskData, tell_data: TellData) -> None: Raises: ValueError: If the gradient passed doesn't have the right dimension. """ - if np.shape(self.state.x) != np.shape(tell_data.eval_jac): + if np.shape(self.state.x) != np.shape(tell_data.eval_jac): # type: ignore[arg-type] raise ValueError("The gradient does not have the correct dimension") self.state.x = self.state.x - next(self.state.learning_rate) * tell_data.eval_jac - self.state.stepsize = np.linalg.norm(tell_data.eval_jac) + self.state.stepsize = np.linalg.norm(tell_data.eval_jac) # type: ignore[arg-type,assignment] self.state.nit += 1 def evaluate(self, ask_data: AskData) -> TellData: @@ -334,9 +331,9 @@ def evaluate(self, ask_data: AskData) -> TellData: epsilon=eps, max_evals_grouped=self._max_evals_grouped, ) - self.state.nfev += 1 + len(ask_data.x_jac) + self.state.nfev += 1 + len(ask_data.x_jac) # type: ignore[arg-type] else: - grad = self.state.jac(ask_data.x_jac) + grad = self.state.jac(ask_data.x_jac) # type: ignore[arg-type] self.state.njev += 1 return TellData(eval_jac=grad) diff --git a/qiskit_algorithms/optimizers/gsls.py b/qiskit_algorithms/optimizers/gsls.py index 34144ae2..9d0671db 100644 --- a/qiskit_algorithms/optimizers/gsls.py +++ b/qiskit_algorithms/optimizers/gsls.py @@ -15,7 +15,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import Any, SupportsFloat +from typing import Any import numpy as np @@ -122,11 +122,11 @@ def minimize( var_lb = np.array([l for (l, _) in bounds]) var_ub = np.array([u for (_, u) in bounds]) - x, fun, nfev, _ = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub) + x, fun_, nfev, _ = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub) result = OptimizerResult() result.x = x - result.fun = fun + result.fun = fun_ result.nfev = nfev return result @@ -172,7 +172,7 @@ def ls_optimize( prev_directions, prev_sample_set_x, prev_sample_set_y = None, None, None consecutive_fail_iter = 0 alpha = self._options["initial_step_size"] - grad_norm: SupportsFloat = np.inf + grad_norm: float = np.inf sample_set_size = int(round(self._options["sample_size_factor"] * n)) # Initial point @@ -202,7 +202,7 @@ def ls_optimize( grad = self.gradient_approximation( n, x, x_value, directions, sample_set_x, sample_set_y ) - grad_norm = np.linalg.norm(grad) + grad_norm = float(np.linalg.norm(grad)) new_x = np.clip(x - alpha * grad, var_lb, var_ub) new_x_value = obj_fun(new_x) n_evals += 1 diff --git a/qiskit_algorithms/optimizers/spsa.py b/qiskit_algorithms/optimizers/spsa.py index 7e1092b1..3d663610 100644 --- a/qiskit_algorithms/optimizers/spsa.py +++ b/qiskit_algorithms/optimizers/spsa.py @@ -386,13 +386,13 @@ def settings(self) -> dict[str, Any]: iterator = self.learning_rate() learning_rate = np.array([next(iterator) for _ in range(self.maxiter)]) else: - learning_rate = self.learning_rate + learning_rate = self.learning_rate # type: ignore[assignment] if callable(self.perturbation): iterator = self.perturbation() perturbation = np.array([next(iterator) for _ in range(self.maxiter)]) else: - perturbation = self.perturbation + perturbation = self.perturbation # type: ignore[assignment] return { "maxiter": self.maxiter, @@ -509,6 +509,7 @@ def minimize( ) -> OptimizerResult: # ensure learning rate and perturbation are correctly set: either none or both # this happens only here because for the calibration the loss function is required + x0 = np.asarray(x0) if self.learning_rate is None and self.perturbation is None: get_eta, get_eps = self.calibrate(fun, x0, max_evals_grouped=self._max_evals_grouped) else: @@ -517,10 +518,9 @@ def minimize( ) eta, eps = get_eta(), get_eps() + lse_solver = self.lse_solver if self.lse_solver is None: lse_solver = np.linalg.solve - else: - lse_solver = self.lse_solver # prepare some initials x = np.asarray(x0) @@ -629,7 +629,7 @@ def minimize( logger.info("SPSA: Finished in %s", time() - start) if self.last_avg > 1: - x = np.mean(last_steps, axis=0) + x = np.mean(last_steps, axis=0) # type: ignore[call-overload] result = OptimizerResult() result.x = x diff --git a/qiskit_algorithms/optimizers/umda.py b/qiskit_algorithms/optimizers/umda.py index e7eba0c6..f420e82d 100644 --- a/qiskit_algorithms/optimizers/umda.py +++ b/qiskit_algorithms/optimizers/umda.py @@ -126,7 +126,7 @@ def __init__( maxiter: int = 100, size_gen: int = 20, alpha: float = 0.5, - callback: Callable[[int, np.array, float], None] | None = None, + callback: Callable[[int, np.ndarray, float], None] | None = None, ) -> None: r""" Args: @@ -151,7 +151,7 @@ def __init__( super().__init__() self._best_cost_global: float | None = None - self._best_ind_global: int | None = None + self._best_ind_global: np.ndarray | None = None self._evaluations: np.ndarray | None = None self._n_variables: int | None = None @@ -216,11 +216,11 @@ def minimize( result = OptimizerResult() if isinstance(x0, float): - x0 = [x0] + x0 = np.asarray([x0]) self._n_variables = len(x0) self._best_cost_global = 999999999999 - self._best_ind_global = 9999999 + self._best_ind_global = None history = [] self._evaluations = np.array(0) diff --git a/qiskit_algorithms/phase_estimators/ipe.py b/qiskit_algorithms/phase_estimators/ipe.py index 8dfcf89c..7c797182 100644 --- a/qiskit_algorithms/phase_estimators/ipe.py +++ b/qiskit_algorithms/phase_estimators/ipe.py @@ -138,7 +138,7 @@ def _estimate_phase_iteratively(self, unitary, state_preparation): # pylint: disable=signature-differs def estimate( - self, unitary: QuantumCircuit, state_preparation: QuantumCircuit + self, unitary: QuantumCircuit, state_preparation: QuantumCircuit | None = None ) -> "IterativePhaseEstimationResult": """ Estimate the eigenphase of the input unitary and initial-state pair. diff --git a/qiskit_algorithms/phase_estimators/phase_estimation_result.py b/qiskit_algorithms/phase_estimators/phase_estimation_result.py index 25d54760..bf57c800 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimation_result.py +++ b/qiskit_algorithms/phase_estimators/phase_estimation_result.py @@ -77,11 +77,11 @@ def phase(self) -> float: else: # numpy.argmax ignores complex part of number. But, we take abs anyway idx = numpy.argmax(abs(self.phases)) - binary_phase_string = numpy.binary_repr(idx, self._num_evaluation_qubits)[::-1] + binary_phase_string = f"{idx:0{self._num_evaluation_qubits}b}"[::-1] phase = _bit_string_to_phase(binary_phase_string) return phase - def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> dict: + def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> dict[str | float, float]: """Return a filtered dict of phases (keys) and frequencies (values). Only phases with frequencies (counts) larger than `cutoff` are included. @@ -101,6 +101,7 @@ def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> dict: Returns: A filtered dict of phases (keys) and frequencies (values). """ + phases: dict[str | float, float] if isinstance(self.phases, dict): counts = self.phases if as_float: diff --git a/qiskit_algorithms/phase_estimators/phase_estimation_scale.py b/qiskit_algorithms/phase_estimators/phase_estimation_scale.py index e6606c50..25e06a32 100644 --- a/qiskit_algorithms/phase_estimators/phase_estimation_scale.py +++ b/qiskit_algorithms/phase_estimators/phase_estimation_scale.py @@ -116,7 +116,7 @@ def scale_phases(self, phases: list | dict, id_coefficient: float = 0.0) -> dict @classmethod def from_pauli_sum( cls, pauli_sum: SparsePauliOp | Operator | BaseOperator - ) -> "PhaseEstimationScale" | float: + ) -> "PhaseEstimationScale": """Create a PhaseEstimationScale from a `SummedOp` representing a sum of Pauli Operators. It is assumed that the ``pauli_sum`` is the sum of ``PauliOp`` objects. The bound on diff --git a/qiskit_algorithms/state_fidelities/base_state_fidelity.py b/qiskit_algorithms/state_fidelities/base_state_fidelity.py index 834b42dd..1909e63f 100644 --- a/qiskit_algorithms/state_fidelities/base_state_fidelity.py +++ b/qiskit_algorithms/state_fidelities/base_state_fidelity.py @@ -15,7 +15,8 @@ from __future__ import annotations from abc import ABC, abstractmethod -from collections.abc import Sequence, Mapping +from collections.abc import MutableMapping +from typing import cast, Sequence, List import numpy as np from qiskit import QuantumCircuit @@ -44,13 +45,13 @@ class BaseStateFidelity(ABC): def __init__(self) -> None: # use cache for preventing unnecessary circuit compositions - self._circuit_cache: Mapping[tuple[int, int], QuantumCircuit] = {} + self._circuit_cache: MutableMapping[tuple[int, int], QuantumCircuit] = {} @staticmethod def _preprocess_values( circuits: QuantumCircuit | Sequence[QuantumCircuit], values: Sequence[float] | Sequence[Sequence[float]] | None = None, - ) -> Sequence[Sequence[float]]: + ) -> Sequence[list[float]]: """ Checks whether the passed values match the shape of the parameters of the corresponding circuits and formats values to 2D list. @@ -96,9 +97,11 @@ def _preprocess_values( # ensure 2d if len(values) > 0 and not isinstance(values[0], Sequence) or len(values) == 0: - values = [values] + values = [cast(List[float], values)] - return values + # we explicitly cast the type here because mypy appears to be unable to understand the + # above few lines where we ensure that values are 2d + return cast(Sequence[List[float]], values) def _check_qubits_match(self, circuit_1: QuantumCircuit, circuit_2: QuantumCircuit) -> None: """ @@ -200,7 +203,7 @@ def _construct_value_list( circuits_2: Sequence[QuantumCircuit], values_1: Sequence[float] | Sequence[Sequence[float]] | None = None, values_2: Sequence[float] | Sequence[Sequence[float]] | None = None, - ) -> list[float]: + ) -> list[list[float]]: """ Preprocesses input parameter values to match the fidelity circuit parametrization, and return in list format. @@ -214,11 +217,12 @@ def _construct_value_list( values_2: Numerical parameters to be bound to the second circuits. Returns: - List of parameter values for fidelity circuit. + List of lists of parameter values for fidelity circuit. """ values_1 = self._preprocess_values(circuits_1, values_1) values_2 = self._preprocess_values(circuits_2, values_2) + # now, values_1 and values_2 are explicitly made 2d lists values = [] if len(values_2[0]) == 0: @@ -227,8 +231,11 @@ def _construct_value_list( values = list(values_2) else: for (val_1, val_2) in zip(values_1, values_2): + # the `+` operation concatenates the lists + # and then this new list gets appended to the values list values.append(val_1 + val_2) + # values is guaranteed to be 2d return values @abstractmethod diff --git a/qiskit_algorithms/time_evolvers/classical_methods/evolve.py b/qiskit_algorithms/time_evolvers/classical_methods/evolve.py index c578f6b5..57f42f2c 100644 --- a/qiskit_algorithms/time_evolvers/classical_methods/evolve.py +++ b/qiskit_algorithms/time_evolvers/classical_methods/evolve.py @@ -54,7 +54,7 @@ def _create_observable_output( observable_evolution = [(ops_ev_mean[i], zero_array) for i in range(operators_number)] if isinstance(aux_ops, dict): - observable_evolution = dict(zip(aux_ops.keys(), observable_evolution)) + return dict(zip(aux_ops.keys(), observable_evolution)), time_array return observable_evolution, time_array @@ -78,7 +78,7 @@ def _create_obs_final( aux_ops = evolution_problem.aux_operators aux_ops_evaluated: ListOrDict[tuple[complex, complex]] = [(op_ev, 0) for op_ev in ops_ev_mean] if isinstance(aux_ops, dict): - aux_ops_evaluated = dict(zip(aux_ops.keys(), aux_ops_evaluated)) + aux_ops_evaluated = dict(zip(aux_ops.keys(), aux_ops_evaluated)) # type: ignore[arg-type] return aux_ops_evaluated @@ -209,7 +209,7 @@ def _evolve( return TimeEvolutionResult( evolved_state=Statevector(state), - aux_ops_evaluated=aux_ops_evaluated, - observables=observable_history, + aux_ops_evaluated=aux_ops_evaluated, # type: ignore[arg-type] + observables=observable_history, # type: ignore[arg-type] times=times, ) diff --git a/qiskit_algorithms/time_evolvers/pvqd/pvqd.py b/qiskit_algorithms/time_evolvers/pvqd/pvqd.py index 0585f547..9c5c3f0a 100644 --- a/qiskit_algorithms/time_evolvers/pvqd/pvqd.py +++ b/qiskit_algorithms/time_evolvers/pvqd/pvqd.py @@ -199,9 +199,11 @@ def step( initial_guess = algorithm_globals.random.random(self.initial_parameters.size) * 0.01 if isinstance(self.optimizer, Optimizer): - optimizer_result = self.optimizer.minimize(loss, initial_guess, gradient) + optimizer_result = self.optimizer.minimize( + loss, initial_guess, gradient # type: ignore[arg-type] + ) else: - optimizer_result = self.optimizer(loss, initial_guess, gradient) + optimizer_result = self.optimizer(loss, initial_guess, gradient) # type: ignore[call-arg] # clip the fidelity to [0, 1] fidelity = np.clip(1 - optimizer_result.fun, 0, 1) @@ -297,7 +299,7 @@ def evaluate_gradient(displacement: np.ndarray) -> np.ndarray: plus_shifts = (displacement + np.pi / 2 * np.identity(dim)).tolist() minus_shifts = (displacement - np.pi / 2 * np.identity(dim)).tolist() - evaluated = evaluate_loss(plus_shifts + minus_shifts) + evaluated = np.asarray(evaluate_loss(plus_shifts + minus_shifts)) gradient = (evaluated[:dim] - evaluated[dim:]) / 2 @@ -306,14 +308,14 @@ def evaluate_gradient(displacement: np.ndarray) -> np.ndarray: else: evaluate_gradient = None - return evaluate_loss, evaluate_gradient + return evaluate_loss, evaluate_gradient # type: ignore[return-value] def _transpose_param_dicts(self, params: dict) -> list[dict[Parameter, float]]: p_0 = list(params.values())[0] if isinstance(p_0, (list, np.ndarray)): num_parameterizations = len(p_0) param_bindings = [ - {param: value_list[i] for param, value_list in params.items()} # type: ignore + {param: value_list[i] for param, value_list in params.items()} for i in range(num_parameterizations) ] else: @@ -394,11 +396,11 @@ def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult times=times, parameters=parameters, fidelities=fidelities, - estimated_error=1 - np.prod(fidelities), + estimated_error=1 - float(np.prod(fidelities)), ) if observables is not None: - result.observables = observable_values - result.aux_ops_evaluated = observable_values[-1] + result.observables = observable_values # type: ignore[assignment] + result.aux_ops_evaluated = observable_values[-1] # type: ignore[assignment] return result diff --git a/qiskit_algorithms/time_evolvers/pvqd/pvqd_result.py b/qiskit_algorithms/time_evolvers/pvqd/pvqd_result.py index 8b1abc09..194aefcb 100644 --- a/qiskit_algorithms/time_evolvers/pvqd/pvqd_result.py +++ b/qiskit_algorithms/time_evolvers/pvqd/pvqd_result.py @@ -46,9 +46,9 @@ def __init__( product of all fidelities. observables: The value of the observables evaluated at each iteration. """ - super().__init__(evolved_state, aux_ops_evaluated) - self.times = times + super().__init__(evolved_state, aux_ops_evaluated) # type: ignore[arg-type] + self.times = times # type: ignore[assignment] self.parameters = parameters self.fidelities = fidelities self.estimated_error = estimated_error - self.observables = observables + self.observables = observables # type: ignore[assignment] diff --git a/qiskit_algorithms/time_evolvers/time_evolution_result.py b/qiskit_algorithms/time_evolvers/time_evolution_result.py index f3e62d81..8bc51f0f 100644 --- a/qiskit_algorithms/time_evolvers/time_evolution_result.py +++ b/qiskit_algorithms/time_evolvers/time_evolution_result.py @@ -38,8 +38,8 @@ class TimeEvolutionResult(AlgorithmResult): def __init__( self, evolved_state: QuantumCircuit | Statevector, - aux_ops_evaluated: ListOrDict[tuple[complex, complex]] | None = None, - observables: ListOrDict[tuple[np.ndarray, np.ndarray]] | None = None, + aux_ops_evaluated: ListOrDict[tuple[complex, dict[str, complex]]] | None = None, + observables: list[ListOrDict[tuple[complex, dict[str, complex]]]] | None = None, times: np.ndarray | None = None, ): """ diff --git a/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py b/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py index 75570bc9..407254ec 100644 --- a/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py +++ b/qiskit_algorithms/time_evolvers/trotterization/trotter_qrte.py @@ -241,4 +241,6 @@ def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult if evolution_problem.aux_operators is not None: evaluated_aux_ops = observables[-1] - return TimeEvolutionResult(evolved_state, evaluated_aux_ops, observables) + return TimeEvolutionResult( + evolved_state, evaluated_aux_ops, observables # type: ignore[arg-type] + ) diff --git a/qiskit_algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py b/qiskit_algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py index c2c96c75..00d3a693 100644 --- a/qiskit_algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py +++ b/qiskit_algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py @@ -125,6 +125,6 @@ def solve_lse( hamiltonian, self._ansatz, param_values, self._gradient_params ) - x = self._lse_solver(metric_tensor_lse_lhs, evolution_grad_lse_rhs) + x = self._lse_solver(metric_tensor_lse_lhs, evolution_grad_lse_rhs) # type: ignore[arg-type] - return np.real(x), metric_tensor_lse_lhs, evolution_grad_lse_rhs + return np.real(x), np.asarray(metric_tensor_lse_lhs), evolution_grad_lse_rhs diff --git a/qiskit_algorithms/time_evolvers/variational/var_qte.py b/qiskit_algorithms/time_evolvers/variational/var_qte.py index f3532b74..42e50b8b 100644 --- a/qiskit_algorithms/time_evolvers/variational/var_qte.py +++ b/qiskit_algorithms/time_evolvers/variational/var_qte.py @@ -172,7 +172,11 @@ def evolve(self, evolution_problem: TimeEvolutionProblem) -> VarQTEResult: evaluated_aux_ops = observables[-1] if len(observables) > 0 else None return VarQTEResult( - evolved_state, evaluated_aux_ops, observables, time_points, param_values + evolved_state, + evaluated_aux_ops, # type: ignore[arg-type] + observables, # type: ignore[arg-type] + time_points, # type: ignore[arg-type] + param_values, # type: ignore[arg-type] ) def _evolve( @@ -250,7 +254,7 @@ def _create_init_state_param_dict( TypeError: If an unsupported type of ``param_values`` provided. """ if isinstance(param_values, Mapping): - init_state_parameter_values: Sequence[float] = [] + init_state_parameter_values: list[float] = [] for param in init_state_parameters: if param in param_values.keys(): init_state_parameter_values.append(param_values[param]) @@ -269,7 +273,7 @@ def _create_init_state_param_dict( f" list of values has {len(param_values)} elements. They should be" f" equal in length." ) - init_state_parameter_values = param_values + init_state_parameter_values = list(param_values) else: raise TypeError(f"Unsupported type of param_values provided: {type(param_values)}.") diff --git a/qiskit_algorithms/time_evolvers/variational/var_qte_result.py b/qiskit_algorithms/time_evolvers/variational/var_qte_result.py index 64d9a9e0..c3cb2f79 100644 --- a/qiskit_algorithms/time_evolvers/variational/var_qte_result.py +++ b/qiskit_algorithms/time_evolvers/variational/var_qte_result.py @@ -52,5 +52,5 @@ def __init__( """ - super().__init__(evolved_state, aux_ops_evaluated, observables, times) + super().__init__(evolved_state, aux_ops_evaluated, observables, times) # type: ignore[arg-type] self.parameter_values = parameter_values diff --git a/qiskit_algorithms/utils/algorithm_globals.py b/qiskit_algorithms/utils/algorithm_globals.py index 75d01733..5396b2d8 100644 --- a/qiskit_algorithms/utils/algorithm_globals.py +++ b/qiskit_algorithms/utils/algorithm_globals.py @@ -60,7 +60,7 @@ class QiskitAlgorithmGlobals: def __init__(self) -> None: self._random_seed: int | None = None - self._random = None + self._random: np.random.Generator | None = None @property def random_seed(self) -> int | None: diff --git a/qiskit_algorithms/utils/validate_initial_point.py b/qiskit_algorithms/utils/validate_initial_point.py index fde01f02..44925652 100644 --- a/qiskit_algorithms/utils/validate_initial_point.py +++ b/qiskit_algorithms/utils/validate_initial_point.py @@ -14,12 +14,12 @@ from __future__ import annotations -from collections.abc import Sequence +from typing import cast, Sequence import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit_algorithms.utils import algorithm_globals +from qiskit_algorithms.utils.algorithm_globals import algorithm_globals def validate_initial_point( @@ -58,7 +58,7 @@ def validate_initial_point( upper_bounds.append(upper if upper is not None else 2 * np.pi) # sample from within bounds - point = algorithm_globals.random.uniform(lower_bounds, upper_bounds) + point = cast(Sequence[float], algorithm_globals.random.uniform(lower_bounds, upper_bounds)) elif len(point) != expected_size: raise ValueError( diff --git a/requirements-dev.txt b/requirements-dev.txt index 366b3d7d..b3b60507 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -17,6 +17,8 @@ discover rustworkx>=0.13 qiskit-aer>=0.12 networkx>=2.2 +mypy>=0.991 +mypy-extensions>=0.4.3 # Tweedledum is unmaintained and its existing Mac wheels are unreliable. If you # manage to get a working install on a Mac the functionality should still work, diff --git a/test/optimizers/test_optimizers.py b/test/optimizers/test_optimizers.py index 05867cb0..91bf821b 100644 --- a/test/optimizers/test_optimizers.py +++ b/test/optimizers/test_optimizers.py @@ -74,7 +74,7 @@ def run_optimizer( grad: Whether to pass the gradient function as input. bounds: Optimizer bounds. """ - x_0 = [1.3, 0.7, 0.8, 1.9, 1.2] + x_0 = np.asarray([1.3, 0.7, 0.8, 1.9, 1.2]) jac = rosen_der if grad else None res = optimizer.minimize(rosen, x_0, jac, bounds) diff --git a/test/test_amplitude_estimators.py b/test/test_amplitude_estimators.py index 178ce0b3..4aa3e281 100644 --- a/test/test_amplitude_estimators.py +++ b/test/test_amplitude_estimators.py @@ -399,7 +399,7 @@ def test_confidence_intervals(self, qae, key, expect): for method, expected_confint in expect.items(): confint = qae.compute_confidence_interval(result, alpha, method) - np.testing.assert_array_almost_equal(confint, expected_confint, decimal=1) + np.testing.assert_array_almost_equal(np.asarray(confint), expected_confint, decimal=1) self.assertTrue(confint[0] <= getattr(result, key) <= confint[1]) def test_iqae_confidence_intervals(self): diff --git a/test/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py b/test/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py index 38c8de9a..9e602c07 100644 --- a/test/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py +++ b/test/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py @@ -13,6 +13,7 @@ """Test Classical Imaginary Evolver.""" import unittest from test import QiskitAlgorithmsTestCase +from typing import cast from ddt import data, ddt, unpack import numpy as np @@ -97,7 +98,7 @@ def test_observables( classic_evolver = SciPyImaginaryEvolver(num_timesteps=300) result = classic_evolver.evolve(evolution_problem) - z_mean, z_std = result.observables["Z"] + z_mean, z_std = cast(dict, result.observables)["Z"] time_vector = result.times expected_z = 1 / (np.cosh(time_vector) ** 2 + np.sinh(time_vector) ** 2)