From 3dd92877732a8cbc0a092ecb9bf39367c277a3d0 Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Mon, 12 May 2025 23:37:39 +0530 Subject: [PATCH 1/9] Add: H Molecule Generator --- qiskit_machine_learning/datasets/__init__.py | 15 +- .../datasets/h_molecule_evolution.py | 215 ++++++++++++++ .../hamiltonians/create_h_molecules.py | 264 ++++++++++++++++++ .../h_molecule_hamiltonians/H2.bin | Bin 0 -> 1117 bytes .../h_molecule_hamiltonians/H3.bin | Bin 0 -> 3701 bytes .../h_molecule_hamiltonians/H6.bin | Bin 0 -> 54910 bytes 6 files changed, 487 insertions(+), 7 deletions(-) create mode 100644 qiskit_machine_learning/datasets/h_molecule_evolution.py create mode 100644 qiskit_machine_learning/datasets/hamiltonians/create_h_molecules.py create mode 100644 qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H2.bin create mode 100644 qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H3.bin create mode 100644 qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H6.bin diff --git a/qiskit_machine_learning/datasets/__init__.py b/qiskit_machine_learning/datasets/__init__.py index 2e198db3f..3e45e9366 100644 --- a/qiskit_machine_learning/datasets/__init__.py +++ b/qiskit_machine_learning/datasets/__init__.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2019, 2024. +# (C) Copyright IBM 2019, 2025. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -14,23 +14,24 @@ Datasets (:mod:`qiskit_machine_learning.datasets`) ================================================== -A set of sample datasets to test machine learning algorithms. +A collection of synthetic datasets used to test and benchmark machine-learning +algorithms implemented in Qiskit Machine Learning. .. currentmodule:: qiskit_machine_learning.datasets -Datasets --------- +Synthetic dataset generators +---------------------------- .. autosummary:: :toctree: ../stubs/ :nosignatures: ad_hoc_data + h_molecule_evolution_data """ from .ad_hoc import ad_hoc_data +from .h_molecule_evolution import h_molecule_evolution_data -__all__ = [ - "ad_hoc_data", -] +__all__ = ["ad_hoc_data", "h_molecule_evolution_data"] diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py new file mode 100644 index 000000000..e6e61c45b --- /dev/null +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -0,0 +1,215 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2025. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +H Molecule Evolution +""" + +from __future__ import annotations + +import warnings +import os + +import numpy as np +import pickle as pkl + +from qiskit import QuantumCircuit, transpile +from qiskit.circuit import Parameter +from qiskit.quantum_info import SparsePauliOp, Statevector +from qiskit.circuit.library import PauliEvolutionGate +from qiskit.synthesis import SuzukiTrotter +from qiskit.providers import QiskitBackendNotFoundError + +from qiskit_ibm_runtime import QiskitRuntimeService + +from qiskit_aer import AerSimulator +from qiskit_aer.noise import NoiseModel, depolarizing_error + +from scipy.linalg import expm + + +from ..utils import algorithm_globals + + +# pylint: disable=too-many-positional-arguments +def h_molecule_evolution_data( + delta_t: float, + train_end: int, + test_start: int, + test_end: int, + molecule: str = "H2", + noise_mode: str = "ibm_brisbane", + formatting: str = "ndarray" +) -> ( + tuple[Statevector, np.ndarray, list[Statevector], np.ndarray, list[Statevector]] +): + r""" """ + + occupancy = {"H2": 2, "H3": 2, "H6": 6} + num_occupancy = occupancy[molecule] + + # Noise Models for Training Data + simulator = _noise_simulator(noise_mode) + + # Import Hamiltonian and Unitary Evolution Circuit + qc, t, hamiltonian = _evolution_circuit(molecule) + qc_evo = qc.assign_parameters({t: delta_t}) + + # Get Hartree Fock State + psi_hf = _initial_state(hamiltonian, num_occupancy) + + # Time stamps for Train & Test + idx_train, idx_test = np.arange(0, train_end + 1), np.arange(test_start, test_end + 1) + x_train, x_test = delta_t * idx_train, delta_t * idx_test + + # Noisy Shortterm Evolutions + y_train = _simulate_shortterm(psi_hf, qc_evo, simulator, train_end) + + # Ideal Longterm Evolutions + y_test = _ideal_longterm(psi_hf, hamiltonian, x_test) + + if formatting == "ndarray": + y_train = _to_np(y_train) + y_test = _to_np(y_test) + + return (psi_hf, x_train, y_train, x_test, y_test) + + +def _evolution_circuit(molecule): + """Get the parametrized circuit for evolution after Trotterization. + Returns: + - QuantumCircuit (for training set) + - Parameter Object "t" (for training set) + - Original Hamiltonian (for testing set)""" + + spo = _hamiltonian_import(molecule) + + t = Parameter("t") + trotterizer = SuzukiTrotter(order=2, reps=1) + u_evolution = PauliEvolutionGate(spo, time=t, synthesis=trotterizer) + + n_qubits = spo.num_qubits + qc = QuantumCircuit(n_qubits) + qc.append(u_evolution, range(n_qubits)) + + qc_flat = qc.decompose() + + return qc_flat, t, spo + + +def _hamiltonian_import(molecule): + """Import Hamiltonian from Hamiltonians folder""" + + dir_path = os.path.dirname(__file__) + filename = os.path.join(dir_path, f"hamiltonians\\{molecule}.bin") + + with open(filename, "rb") as f: + spo = pkl.load(f) + + return spo + + +def _initial_state(hamiltonian, num_occupancy): + """Sets a realistic initial state + + JW map automatically keeps orbitals in ascending order of energy""" + + n_qubits = hamiltonian.num_qubits + + bitstring = ["1"] * num_occupancy + ["0"] * (n_qubits - num_occupancy) + + occupation_label = "".join(bitstring) + + return Statevector.from_label(occupation_label) + + +def _noise_simulator(noise_mode): + """Returns a Noisy/Noiseless AerSimulator object""" + + if noise_mode == "noiseless": + noise_model = None + + elif noise_mode == "reduced": + single_qubit_error = depolarizing_error(0.001, 1) + two_qubit_error = depolarizing_error(0.01, 2) + noise_model = NoiseModel() + noise_model.add_all_qubit_quantum_error(single_qubit_error, ["u1", "u2", "u3"]) + noise_model.add_all_qubit_quantum_error(two_qubit_error, ["cx"]) + + # If the given Model is an IBM location + else: + + service = QiskitRuntimeService() + + try: + backend = service.backend(noise_mode) + except QiskitBackendNotFoundError: + backends = service.backends(min_num_qubits=4, operational=True, simulator=False) + raise QiskitBackendNotFoundError( + f"""The specified backend '{noise_mode}' was not found / was busy. Please select one from {backends}""" + ) + + noise_model = NoiseModel.from_backend(backend) + + simulator = AerSimulator(noise_model=noise_model) + return simulator + +def _simulate_shortterm(psi_hf, qc_evo, simulator, train_end): + """Simulates short-term dynamics using a noisy simulator.""" + + y_train = [] + psi = psi_hf.copy() + + for _ in range(train_end): + + # Create a new quantum circuit for each step + qc = QuantumCircuit(psi.num_qubits) + + # psi persists after each step + qc.initialize(psi.data, qc.qubits) + qc.append(qc_evo, qc.qubits) + + qc.save_statevector() + qc_resolved = transpile(qc, simulator) + + # Execute the circuit on the noisy simulator + job = simulator.run(qc_resolved) + result = job.result() + + # Update the statevector with the result + psi = Statevector(result.get_statevector(qc)) + y_train.append(psi.copy()) + + return y_train + +def _ideal_longterm(psi_hf, H, t): + """ + Return the list of statevectors exp(-i H t_k) @ psi_hf + for every t_k in `times`, using an exact matrix exponential. + """ + h_dense = H.to_matrix() + y_test = [] + + for t_k in t: + u_t = expm(-1j * h_dense * t_k) + psi_t = Statevector(u_t @ psi_hf.data) + y_test.append(psi_t) + + return y_test + +def _to_np(states): + """Convert list[Statevector] to ndarray""" + dim = len(states[0]) + return np.stack([sv.data for sv in states], axis=0).reshape(len(states), dim, 1) + + +print(h_molecule_evolution_data(0.1, 3, 6, 8)) diff --git a/qiskit_machine_learning/datasets/hamiltonians/create_h_molecules.py b/qiskit_machine_learning/datasets/hamiltonians/create_h_molecules.py new file mode 100644 index 000000000..bfd2a74a1 --- /dev/null +++ b/qiskit_machine_learning/datasets/hamiltonians/create_h_molecules.py @@ -0,0 +1,264 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2025. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +H Atom Pauli Forms (Developer-Only) +""" + +import numpy as np +import pickle as pkl +import os +import itertools + +from qiskit.quantum_info import SparsePauliOp + +""" +- H2: Usually found at a `0.735 Å` equilibrium bond distance +- H3+: Usually found both in an equilateral triangle configuration with `0.9 Å` between each pair +- H6: Usually modelled as a linear chain of 6 atoms with bond lengths `1 Å` between each pair +""" +_molecules = { + "H2": {"atom": "H 0 0 0; H 0 0 0.735", "basis": "sto-3g", "charge": 0, "spin": 0}, + "H3": { + "atom": "H 0 0 -0.45; H 0 0 0.45; H 0 0.78 0;", + "basis": "sto-3g", + "charge": 1, + "spin": 0, + }, + "H6": { + "atom": "H 0 0 0; H 0 0 1; H 0 0 2; H 0 0 3; H 0 0 4; H 0 0 5;", + "basis": "sto-3g", + "charge": 0, + "spin": 0, + }, +} + +# Pauli multiplication table +_mul_table = { + ("I", "I"): (1, "I"), + ("I", "X"): (1, "X"), + ("I", "Y"): (1, "Y"), + ("I", "Z"): (1, "Z"), + ("X", "I"): (1, "X"), + ("X", "X"): (1, "I"), + ("X", "Y"): (1j, "Z"), + ("X", "Z"): (-1j, "Y"), + ("Y", "I"): (1, "Y"), + ("Y", "X"): (-1j, "Z"), + ("Y", "Y"): (1, "I"), + ("Y", "Z"): (1j, "X"), + ("Z", "I"): (1, "Z"), + ("Z", "X"): (1j, "Y"), + ("Z", "Y"): (-1j, "X"), + ("Z", "Z"): (1, "I"), +} + + +def _a_p(p, n): + """JW Substituion for Annihilation Operator""" + z_str, t_str = ["Z"] * p, ["I"] * (n - p - 1) + return {tuple(z_str + ["X"] + t_str): 0.5, tuple(z_str + ["Y"] + t_str): 0.5j} + + +def _a_p_dag(p, n): + """JW Substitution for Creation Operator""" + z_str, t_str = ["Z"] * p, ["I"] * (n - p - 1) + return {tuple(z_str + ["X"] + t_str): 0.5, tuple(z_str + ["Y"] + t_str): -0.5j} + + +def _n_p(p, n): + """JW Substitution for 1-Body Diagonal Entries""" + id_str = ("I",) * n + z_str = tuple("Z" if i == p else "I" for i in range(n)) + return {id_str: 0.5, z_str: -0.5} + + +def _z_string(idxs, n): + """Helper for Z string generation""" + return {tuple("Z" if i in idxs else "I" for i in range(n)): 1.0} + + +def _mul_strs(s1, s2): + """Multiply Pauli Strings of form (Q0_op, Q1_op, Q2_op...)""" + phase, res = 1, [] + for a, b in zip(s1, s2): + ph, op = _mul_table[(a, b)] + phase *= ph + res.append(op) + return phase, tuple(res) + + +def _mul_pauli(t1, t2): + """Multiply Pauli Summation Dicts of form {Pauli String: Coefficient}""" + res = {} + for s1, c1 in t1.items(): + for s2, c2 in t2.items(): + ph, s = _mul_strs(s1, s2) + res[s] = res.get(s, 0) + ph * c1 * c2 + if abs(res[s]) == 0: + del res[s] + return res + + +def _add_pauli(H, t, coef=1.0): + """Add Pauli Summation Dicts of form {Pauli String: Coefficient}""" + for s, c in t.items(): + H[s] = H.get(s, 0) + coef * c + if abs(H[s]) == 0: + del H[s] + + +def _JW_map(E_nuc, h_so, g_so, eps=1e-15): + """Jordan Wigner mapping of each Spin Orbital to a Qubit""" + n = h_so.shape[0] + H = {("I",) * n: E_nuc} + + for p in range(n): + _add_pauli(H, _n_p(p, n), h_so[p, p]) + + for p in range(n): + for q in range(p + 1, n): + c = 0.5 * (h_so[p, q] + np.conj(h_so[q, p])) + if abs(c) < eps: + continue + _add_pauli(H, _mul_pauli(_a_p_dag(p, n), _a_p(q, n)), c) + _add_pauli(H, _mul_pauli(_a_p_dag(q, n), _a_p(p, n)), np.conj(c)) + + for p, q, r, s in itertools.product(range(n), repeat=4): + g = 0.5 * g_so[p, q, r, s] + if abs(g) < eps: + continue + uniq = len({p, q, r, s}) + + if uniq == 4: + term1 = _mul_pauli( + _mul_pauli(_a_p_dag(p, n), _a_p_dag(q, n)), _mul_pauli(_a_p(r, n), _a_p(s, n)) + ) + term2 = _mul_pauli( + _mul_pauli(_a_p_dag(s, n), _a_p_dag(r, n)), _mul_pauli(_a_p(q, n), _a_p(p, n)) + ) + _add_pauli(H, term1, g) + _add_pauli(H, term2, np.conj(g)) + elif uniq == 3: + term = _mul_pauli( + _mul_pauli(_a_p_dag(p, n), _a_p_dag(q, n)), _mul_pauli(_a_p(r, n), _a_p(s, n)) + ) + _add_pauli(H, term, g) + _add_pauli(H, _mul_pauli(_z_string([p], n), term), -g) + _add_pauli(H, _mul_pauli(_z_string([q], n), term), -g) + elif uniq == 2 and p != q and r == s: + coeff = 0.25 * g + z_p = _z_string([p], n) + z_q = _z_string([q], n) + z_pq = _z_string([p, q], n) + _add_pauli(H, {("I",) * n: -coeff}) + _add_pauli(H, z_p, coeff) + _add_pauli(H, z_q, coeff) + _add_pauli(H, z_pq, -coeff) + + return {k: v for k, v in H.items() if abs(v) > eps} + + +def _save_H_atom_pauli_forms(): + r"""Generates and saves + + This is a Developer-only module. The results of this code has already been + cached in the repository. This has been included in the repository for + ready availability of the generation process for future developments. + + Running this needs installing PySCF which is not included as a + requirement in requirements-dev.txt. If you are using a Windows + environment, it is recommended that you proceed with this module + on a different operating system and drop-in import the results. + + This saves H Atom Hamiltonians in Pauli form for further usage + by other dataset generators + + """ + try: + from pyscf import gto, scf, ao2mo + except ModuleNotFoundError: + raise ModuleNotFoundError( + """This Developer-Only Module requires PySCF. Please install it with + pip install --prefer-binary pyscf. If you are using a Windows + environment, it is recommended that you proceed with this module + on a different operating system and drop-in import the results""" + ) + + dir_path = os.path.dirname(__file__) + + for label, params in _molecules.items(): + + # PySCF Simulations + mol = gto.M(**params) + mf = scf.RHF(mol).run(conv_tol=1e-12) + + # E_nuc, 1-body and 2-body Integrals + E_nuc = mol.energy_nuc() + h_ao = mf.get_hcore() + g_ao = mol.intor("int2e") + + # Converting to Spatial Orbitals + C = mf.mo_coeff + h_mo = C.T @ h_ao @ C + g_mo8 = ao2mo.kernel(mol, C) + g_mo = ao2mo.restore(1, g_mo8, C.shape[1]) + + # Converting to Spin Orbitals + eps = 1e-18 + n_mo = h_mo.shape[0] + n_so = 2 * n_mo + + h_so = np.zeros((n_so, n_so), dtype=complex) + g_so = np.zeros((n_so, n_so, n_so, n_so), dtype=complex) + + # Single Body Terms + for p in range(n_mo): + for q in range(n_mo): + val = h_mo[p, q] + if abs(val) < eps: + continue + h_so[2 * p, 2 * q] = val + h_so[2 * p + 1, 2 * q + 1] = val + + # Two Body Terms + for p, q, r, s in itertools.product(range(n_mo), repeat=4): + val = g_mo[p, q, r, s] + if abs(val) < eps: + continue + g_so[2 * p, 2 * q, 2 * r, 2 * s] = val + g_so[2 * p + 1, 2 * q + 1, 2 * r + 1, 2 * s + 1] = val + g_so[2 * p, 2 * q + 1, 2 * r, 2 * s + 1] = val + g_so[2 * p + 1, 2 * q, 2 * r + 1, 2 * s] = val + + # Jordan Wigner Transform uses O(n) depth. + JW_H = _JW_map(E_nuc, h_so, g_so) + # Alternatively Bravyi-Kaetev Transform can give O(logn) + + # Convert to SparsePauliOp + + # We used qubits in (0,1...) indexing in pauli strings while qiskit + # uses (...1,0). Hence we reverse here + pauli_list = [("".join(reversed(k)), v) for k, v in JW_H.items()] + spo = SparsePauliOp.from_list(pauli_list) + + fname = f"h_molecule_hamiltonians/{label}.bin" + finalpath = os.path.join(dir_path, fname) + + with open(finalpath, "wb") as f: + pkl.dump(JW_H, f) + + print("Hamiltonians saved.") + + +if __name__ == "__main__": + _save_H_atom_pauli_forms() diff --git a/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H2.bin b/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H2.bin new file mode 100644 index 0000000000000000000000000000000000000000..1a492b56c79ef32d258d8edc05e6aa040cf2bdc5 GIT binary patch literal 1117 zcma)5ON-P%5Kd;ZI}W?!!NY>bx#)TcEGx1HWrcvRNJe&UUYaD6v4eT^ba%iJ1wD*9 z*tadglYcPtN_UhNy2T4Hw`vil)7 z$C;b(EJk56O3jo6)a5Bf@S2gOj%dBJFg3G zF8ht};-e?sTV=31D{N&~XOq8_SB^s=F$23R*3j6c_ZT>$BPS0r?8*@k=T4S=4s7?y zK&8|=Z=n1EMT#P@4LOi=2hAOAGz?=VgHCm&;ACFr;XMam#a1!v_?m6t>+-wd;F~IJ F{sUD(Q#}9x literal 0 HcmV?d00001 diff --git a/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H3.bin b/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H3.bin new file mode 100644 index 0000000000000000000000000000000000000000..e1ee6b286eb61b72a8a90fcc7aaca806c5703478 GIT binary patch literal 3701 zcmbVPTWB0r7@pm@8)Fh5lA3@Z*3z^ohOI@g1urRWy|f!9p(_=NFwE{woN4zmnVE&G z7ihs=HqMLWNG~sa5Xp0(lu{dw^dV4eK#VUzu!0~I3tD_AMUCe^(?5H<+0M?MbH4xk z{{KJc-;*<$6Hk0`>}ic&;hrhSKkfvXshU{{YGuQzOja{h*YeDu>iHSJUUo}XA#e&A z-!(noGF-D(a*V2r!rmOUOc2Aki)PV|ljsvP9CjLosx>+3qi~>7E4%d!#-5ca*Ghq7 zdY)NFVUOWig=)nQyjmf^o?(hAQP^22GRZ8;YNyctL>`?%htY60k3hl3n^u06Jj6eLb#vGojChE(l>Jmi>zDrPa2MbK{^R~fTULg8z7_L>_zvJ9e(;l~ zZ=1O;{l_vRUtgPEl32mZ`EtF)3cWI3?wJ>7rap`E|29%yEYzd?3SQ>pdWjW!nIG~) zX?y%Ra-RCcSdgOUIzuP;nUR-F*%Xkn!yZx^9=E>c%9uNL7{%=G*|6%o%>w)!wJjAW^Fdy7suznE3`aukTpQC^M_siO9 z^!I~~{r+A!a6_(7!OP#D>m^p`)$#Dj8FurNzU24;3SZvvV}d;&m{%8lHjI%7^68K? zK1BADb~icR?Ou7fXVa&V-4`bFyW(g1$4$@ldkOK&^T8v#M1s;x69agNZ3+AlEsH8F zgy0(Hi;>97hg2H#XAx=3V+jx3vzvHQ52j)fNE&i6&AI{CW0p{)5f_;{v67pPInS`r z%Y@hsGx$qCY>HxHECP)u8meW0q@_3UX!7LAxMO%?~Sacn5($Y#2cmaJqWI;LUMeJCTTMAMtp zjm(?2?IaNuj=Ucp#los5FA|gVZQjim87+pf>cmZ}Nq0utaBb7Kn(YpJ(BPBB*%O^C z_CT}$70)Mm`p4*?>*HdTUn{%dMK6{hu;@2x7dp`kf6+rb#4268b{Mx67V%X*^oKgZ ziyra0uXsBQt#4#IHu-wnsm!BVp&b zo1-rB;oL(L(4a;MWz+aYjbvm7j~F_QqEq%Usf(e9^jf}_w?}n*uf3nW1KMw8)Arl= L`;KlOq;%$gtSneX literal 0 HcmV?d00001 diff --git a/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H6.bin b/qiskit_machine_learning/datasets/hamiltonians/h_molecule_hamiltonians/H6.bin new file mode 100644 index 0000000000000000000000000000000000000000..96111f4affbd5bc5a3fe0ca2b83699ac9349dcae GIT binary patch literal 54910 zcmeHQ33yf2wGIk6Fj=eiA!=>4Dn1R`BE>p@ASxgzSgU9uhN+1J2uUac;s^-7U@=@| zC^(C?TBt(NIt7PkrB$?umRcSlRcpm6t%^?(d3%j}-<9m1d*eM{ef>P2IeYzU{p(+A z?Q?Dp2V=ad`P}MPitwoIIr*t0#`G ztQaw@rhHg&b!BN)^@x(n((x7LC1WcywQbHr&$-|@r!q4q)A8!el+01Jdz1_tJ7VO> z>P+ncW5$oJoLKB|)rjKJ<11>)ORK6%CuV9}msE`yHg-&PP1X2eH7>KZ1u`?WdyE-Q zlsTEcMbk1pn+?reml>Ejs&BJ?<;|yM%J!;lF}!ACQ0FG;>kWF~^*`VotMvbc^BL-v8NM;}>t! zxWx8~_y@z6uR_jXa`UG*}b#g=dJaFcIwy9u4wlwGw-^(`)WckeSdR^6s?i}yA87GGlW zEnZN+s>RUH9#egb=bO02@0UM1^@#-!b(8(E_#G3s_|8Y`4sCJEQmt?CA2uyIV&bMz zs&8?h?cG;zzVKen?{LW5<2wHOamA6i>M>t&gdf$r^pKzbqf_rG8gIM&g`;~tbF1P= zTl z%?-EgT=-{yzeVEO5AzjA_^NN?rKfLv@A;CM8m~G2*xO55xs&KdLwq*ZRy?9N|axTAVZE!4JOkUz$H~;;k=k zdSa2{NL=-puQa925KP=|!srflx?NS- z^A$(q<7Ci88*_g=r?qbct`q|AzJ^PQ$9SY_uP!uXJ!>&;!+XJhG@E9d;s`&g*ZQ-wU;bVD$(lcJ?Ywh#FPWn_5?4LuD~|A^dXHb& z`3D`IpP=yr4{x?^^5wTH=D6zHxW!3)TkoY;|1oy)qki6xK0NHMxBl+$&q!SRVZP!B zU+1y$t+TJ**?!Bdns4#yzHe{7Z`<`6uXu6G;jQ<(Q!&R?-^MLY;@f&d_nozKL63#1 zzvs<2O`i5b@V?P_T>E3b;(Ybi{^HiotBYzhf61Fqe$?|@a}{&EUjBS<;}$3JZN2B7 zZTU+1AvbBgm!BN;lf|#yt(fCl-^MLY;@f)DdM)kP`vw2~;I9V^-nHV+IPSXOfcYn% zdY$%j=^N$K>qgGeemSn##w||b+j?8O-+#^C!+)syr}y8~{PxFcwI7ZvwsDJ-__kiJ z3*I>C_nl^|zQqsp+%WHRbF$zYF`lzTJPGT9v$7 zeBAa$%ICOZ8@D)#Z|lvP)$EOfmYv#7KCf5qIIj8cmX1`+ajkFT7ANs-y$6d2b-1`! zh3Z@U=Dx=bJ!0Y&8n3F^`9_;p>J&%f-DDo-D~|A^djI{??@linHD2>itlP2Z%J%+w z&T-ARaf_4qwqDTz?N3?%`n9S*@QC&|ubN${{hU#K#5ZocYOakdwsDJDFUq&|uDkT$ ztLhirp!F?2`Q2>~&gw8!&vli#8XWuvT zeP7L&Z|m9j%`e|?v)`xleIK>&vli#8XWuvTeP7L&Z|m9j&3xZi^X1!mU%t;a`hKk6 zpU*JwvlcI>_gUrJ_tQvRp9joW9O0|JjUPzwvzl+;PdTpnHg0he-`2D5mtWBLS>2yH zdY{$(u>Yf3?`YU*`LMnlInhYiHhPEk1+ZXZ8NG@24Es``5-TPU72o z_U|X``>e(Geb(Zt*`;@O-h8djWAU=Lmj7(m0RQt27MHbO`;(84y-(+Ddr|x41J3e4 zzZ8jUJ?1Np@KxW&k2=12-SR<~YW%~K2fg#!$CZjBajnOE#Swl~&+eDS-9PB_?T_F4 zrRERo_tw`gXz71`B@)+un6Eg(SA82l@URoRp19!@%`cv}v2M-b)0H2ITfX85KdQ(3 zsrfnXca!=}Il5~X|MMy1rWGyT(b38>K%eVDDU9^A6ZPhEZewSxHJo43!k0_4BwLbF|NBB{_?yv5>?zlBS(0q%Z*|}!R zX9N7dZ)kpM_u3hI`M(e5xaQlq#Yucy?>{>4Uh(ud4^{o1TQ{#ba-VUEIbJW<-^MLY z;@f%^EALu8u!sNtVR5?&`%HW07{5QO246XH<=uB^Kasfh&wRxZepIjWr|XtIcI{Nn zA6>QYDMv59M={4W-^MLY;@f&7Kfb2VAKLi8-@RrD~|BBe;cp< z-f0&teC0BYTimW{!$EJKQmgv+@7L#n={^0=XK-BeZQSA{zO6U#%%5)SeeC6`-}$V| zdOh9$M(xMqr325t=!KWB)A(;j@Au)XHj6c%@+qlI^d|PkJcR#!Q$?8!$f7J==s}8?%qT)zg`(eJ~ z2tTSfJ=OFB94hzghJnaqWlsiX;4}-sR2C+cx8o z$(p~j(^n4cGSUD16vs8+#w||b+j>2hOk4B%-TwQt#rs@1=F>el_HT!vPpPv@n=Ucwd$9(y=o_&5=d^9~z_49eQd7eh%`uStN;s{^$ zZJeKf8n343sbc$lv~i1*__m&Xep<}WKka8UJx>+e=cA2VoW!^F?4RH4^Df`#Wxjk{ z&puBr&hdFzFV7Etp6Y&DoUfjJo?2|5pBCS3o~IVu=X1xl!;4#WnWp_)T+#Qp#iK|0 z_qPWY_M17shyVMBNL=-puQU-s!AMHUB>cUOVUJN3T{KiK`y-6-W3{ zJ-Z(k|9109%}bZuullbosyT7@U;4ivi^R1a^A$(-To| zKQGR4t#9KNC-H5)d!GKw?>A2IpC5}CtlRXBNl#|9{!_yS|7mK=1&Skaorn2~BmAh| z%2Sqo=lnMQ{cpmGqt>r`(*OMn$2H%^El%RwdY@i&{lQCCU#?DAM?GR^X1!m_I|gR??=79Io{7r=G%Jq`-Q##EuO#iz*Aqp#sB?{ z#bauw7tMXs|NeuopZwI))gRXD{T+$xJj_=d;YamsKNh#$`@Q>)e)9p%e{SVt=bk=z zmEuTT`(eJ~2tTTK|M%AqJ?*daG{0!SS-)=eiU0d)j%&V+Tb#tV^`3tBd*?P={wu9- zaq%mAF1hn>H*5TfH+EI@=;Qax;vDPSc~qb0QJk-y?cZYCuf=xWb+;ecdCJOV+V25X zr}nw#r6&|e;@Us+6-W3{y^Q{c=|OM<+j=*-s{LWOKk$z+sH3NF&H6Ybq8HKlZ=^ zpf9>;xD)W)S=@*WASp^1QlV6r}%V1pz9jxq3l zeBl=UG>b%@vC3_*9GCYhLkjk90f zqR$S;@nr=-jJzb_a)cyq2+$XJfG-)COMqm;Gcw_X$^MLLtVhw%qghgmDxw?MSPfcG zlRTd85~xiKu%BE6;7o_AIY@exBiibwFuM^r8@aH;fr7B8=?u1UWezT$?<~xbg(_mI z1gISJ$0X+9BE8^B913#)T+RVLq6i={Mna;J42FV~Nd?$*KGr*P}&}Jgauok9r zKDp!^D50qdK_6Ivs$p1ir7qqV+=DCP8)5*9bFw}H#DQN22uK{p!XSXUR0io<9u?W; zZSD#aM4narH3NN0l2otn{c`cZL)xT+ zJ+LPAgADAEE0OJjl!}J8*-y>;M!^d8$c8itU>uvltT2HcAPspWL{w7cGl>#MNSF(8 zalnc;w~q^)fFlQ%34lZ)7Y;m=jc4YI#|!azm_(Tz11ix`P76X5Vm=iLiyOL=NYF#rD7YmdOCJYNA)W`QNQWeM(3Xqs z*krOG187K0Oc1{J13{CuD8)q;DG+n%D1$t(`2bn5k;IVP9Ku|(kuND>t`qT)T9Xof zKeZh#DTdF*jSk$ty)aulqlO$G40lK-IJ7YMo05Tws0spnKm~7$1*`Xm-YmCkLt@+! zLTFZW(cl_RQQW9+07+2-+kp<4MMyCw`Z#?m(2>RG8hIMm0zeQ;Fox!91ewoWNm9AWE+AOp_5eJgaG0mL2dCoFWJ3xCSsyj1y|5zMkRml2n+$?N2}C6a!;gN52+gAzH}&w%_CB8uiZ9grX|UJEus zQ`L~5IaCw0fy-_O5K>7<;Nn<7Z7+)Y%!7pv()Kork`|i?JRRynVde-4eL-*94fRZH zYFJZI1&Juf97jTZ907dZk2Llp32c<(a%giGo=p7)Iw-+9S^7FaAj+E91=(DZIKi1K z2orju-sOzLV5@;BnC?qBXhUz}32bansO+~@z}-k)zbP0-3bg};sTL^9-lIvqpc_e) zB^ABj4*&v~rI*J+(WlC!3^p;@0Dz`qFnn{)M$9Cq4-h!+z!m!uA2vdgtShY{pA*Oq zl+e@!Cx%2R0Ce_I0828(aj7Y!fQ=P#5gxhxS$7GjNaAFoKrN?^CtO67s8pEjU<(2| zWa9xOr{nFwFC-E|4x1DSa>?&nap__MKpR(YhqX`$b;7!&qe7yC60AdY8q+5=)(rH$ zUp5y!r0sOLE7p~T{TN3w1b`}hSc$}E2nt6?K%r6;L@LQpfE;Kuk^E8@ojw8Npa>sW z5GmXn`Xjm06LI`CLcxZTjur@E`%I#_Q3g1^ADEu1fW>WMj|9Mk0H;C)1wMfl1ROF!UjVtnvy1^;%FsMc7r#QJ>dA#Mf@i)&ECFq@Cej0&J7N#z!dVhXl9V}@ z3m}KW9B_*eQ38YHhNK%0y6e=K% z0pIHY3n3W@LF|DesI59kL}lucq7Ba6DRNwp5`b*!-%3DH6>|6rx5oL_NF(GXp5$h0#P=#2gwz2+5v}9 zLt2K_+YrL$aPc;{8#WC-aC4L>0I&%AG0s)POF}O0MtZ_zQh>AMVvFnmD$$*S3!nm; zgrT@Qf?Obx82B;9B+Mndp(v=hBgjP+!rTNgJb*GlK)(bTU?SN_bJ!s=!Dfr{1pwdM zV!`VDp*PDNh+-{Hk^V@P-r=zQj~H zsH?d^3hIgC_9{3!ow9{P8EFJ^u{zZwVYy1^lSYEBL5RA6G78A12-*-rc|c&f15HMO zn}iyQ0tI*1u0d9|NOofv>8Z@Nh7?duNr~q^@8#ro#CmRVT%dJ}ln%EK2 zoQwiAxQ0^%$Wjyu0GXjX5Ji*8VQ-+OiV$Z2Wu%m>l5R2jfv2V_JO+zen*f7V>y zPjpR|R7VJOuqJVoiHQ)P3Q`BDcxvdO=0gSb;EtjiDsnC8nFWy`scw}3G%Orv1pRvx z2%h6bvvZY!B9e_O1GnITeL5>>0&++I7B6LL zk1VEgK2Zo{pMlL8Xlg=G3yZ@kOBDKKHuzo7Lh%3?&W#bY5h9%})P!Wp1`^3+lfcgv z$mD9^WqsT@$p%3Y($0t}Zshw~V!^7Zp)=bZh=#R7lJAT=+5EfYsZPjqvJ@jQ`LCF0t|4ut%Rjy-+Mm5&~XG zc841Ce%~Alv5w!uthoTl-ev`$lC>JKS2>O#6cqOyFPfdJ4Ac_@GAaa?2Gp|pO4lZ7Ht&~N zaSh#G-l_Y#eq69Zl5~ST_>KY!IF;p+13PTP`$KQm1WpwHgRI?3E{p$%Q%BD1VgIA3 zEWs}TfE|!sur{>Uz-Jv{@)s=<8O)7b}_t4Y3(B1W3ZE;?+#<*Ey(_~ zY?OyKxCg9LC|guPKNTBxEWLkVZ|pVUax@Ki&RP za2{T9)8tg}$*@v7lVQ&=7k(D8kRJ*BIHm+4o`&s2c}6UdhK-|;a0+MHC#d2Ms31eY zWGULH&iZNCFi9#G2lmMw&^op+Hs0spOJf?;GzFmxcW@)Pq-DD*DSwQ0j&LR%4@QT% z+<~`bp=}-w^$TfZ$9&>@1BRrHOraX@i`)-bnSAbyb*X6KZIGugu7H>tNac^cct6DF zZP-skPnd1HhW$#fZ`l{A3bhJvJH>EBwQ)9V0q#Wn!s3Orc^cF&ybYb=08T#*+iCL! z!7>H5v2&V$reGTYPlFZHuz50?h^8!{&Eur_9gcU1d&V6qluf(e@FSDjG~sT+JyFnU z!d*dWsDQ(9SuBSoxpW$~d~-=}H0nB~b7Ol(m&t9VtC^k+;!YjsO2bC^)V_Iwx<7cM z(7|?!lkslRy$&@Sl-C|yECt(%Y7eQ{jm^Y^LS-qQf{p5|Utl{ORop7iB&=hO~a9_BjW%Fb-m#YQ!pjH9=Ai-ZKCw6Mz4Ww_G0A@+Wropm?qTz7+*q@4> z$~b(%al812OUZ_fG(2H7cCgLgLIA-%!>2;pphX9lM1VFs(z4xd zf;81n!)9$P657s@%7*}Sc^X*Kv3bA3d1Mb^Dy-`qsn}H8mB2$*hB&$SU=3~HH1t^y zXMFhw8ro94Np(-~l49VW_#K-PQo0r3GGc*L&NOy|8wG3eWNFwi6n{YnYP(Mpe}=)C zhE2t?ic$kGCiDTPRBXPCsGoujt>XYVgtr00JXE*9wv*L^zY}8lbZnXr3i3Qyn+oEH z=y%g{4?rxC-AGN_0~Tc-G-l)A^M{Rc_Ltv?X^Y^p(WRp`2OBal*>Tgboi=rm#u>!< z3uaI|oo|A)Ls^BwqGZFE&20+a>)!rQD$b5VuD+R{C(Gzr@H`KMZLF5WmC zumCpb$^kWlx@tG*NFx8XH`Bl9V#|eqHF`1rWt$$oJA$^;Y7YOSsFo#_Wu?_48vPHW z-2Y$7{r{xOyZZm1RM`Rj`}gm&%N=;?Tjc&nS2KH@by(1C%I0oH|KAnI=1THQTvpjG z!Lr8u5|=f`i}{rf-`K?Zj7xPPguJI(E=*b%6$NZ9KSK-TQ z<4IWZWKH5rJjRdtV|kWuvE;n_9Dvwn-Mj^G$C=9fHKW4uE7rH)-iUsf9zY~vOS&#p0^ z^;>Lp1jl%V^2h2}{UjXIN%BivR$C{@pA>JZUQ=}?&#uyktTrCQF@DT1I=RM^c%mn3 zj34t$o?V46tBogN$&)o#zVMowmv(-vub5x@v8(W9wec8^@ne3`$u*wD6Fs|1o~$;W zge6bb7~jT|aIQMSE3{rrH_4wAw|yq@C2m*Ik=4d6{=7V!Z?UZ>IL3?lZCcw!(FFLYXUXowpvNkn8-@F)K`mn3;Wwr4b zj`3rD(Xnxhg=g0ouTXwlPq66ADt^1#xW$$)IL0fKKUODg{aD>3zr^h-{mN?N7Jpt| zQoh7xwedKlcUwE?GeDTX_snd_{S6reAsU+Lk5bN~?ZUo#|h@SIOAQlIpV3$`P5`BgU0ik1DSz9yh*pOpQw@ zA2V`n@z}}{Ri!m!tE!zP#68ZbJP%XmWIA4*nUXoGwl!8j(UOWx-_}`t5EAgv!q7+q&#b_jOkPvfdaj{y$JEHDv$* literal 0 HcmV?d00001 From 9da84814d935831be31ed73cea3de524e4edeae4 Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Tue, 13 May 2025 00:41:00 +0530 Subject: [PATCH 2/9] Add Unittests --- .../datasets/h_molecule_evolution.py | 205 +++++++++++++++--- ...t_forwarding_dataset-06bbf48921ee645c.yaml | 20 ++ .../test_h_molecule_evolution_data.py | 135 ++++++++++++ 3 files changed, 331 insertions(+), 29 deletions(-) create mode 100644 releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml create mode 100644 test/datasets/test_h_molecule_evolution_data.py diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index e6e61c45b..37991ffaa 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -47,18 +47,173 @@ def h_molecule_evolution_data( test_start: int, test_end: int, molecule: str = "H2", - noise_mode: str = "ibm_brisbane", - formatting: str = "ndarray" + noise_mode: str = "reduced", + formatting: str = "ndarray", ) -> ( tuple[Statevector, np.ndarray, list[Statevector], np.ndarray, list[Statevector]] + | tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray] ): - r""" """ + r""" + + Generates a dataset based on the time-evolution of Hydrogen molecules that can be used + to benchmark Variational Fast Forward (VFF) pipelines such as those discussed in [1]. + The dataset generator gives the user the Hartree-Fock (HF) state of a Hydrogen molecule's + spin orbital occupancy, a few noisy short-term evolutions of this state over time for + training the VFF and exact long-term evolutions of this state for comparing with the + long-term inferences made by the pipeline. + + + The Fermionic Hamiltonian for the Hydrogen Molecule is first obtained using Quantum + Chemistry calculations. This is then mapped to Qubits in Pauli form using the + Jordan-Wigner Mapping. Thus each qubit state represents the occupancy state of + one spin orbital each. The HF state :math:`\ket{\psi_{HF}}` is obtained by setting a + :math:`\ket{1}` state for the lowest energy orbitals and :math:`\ket{0}` for higher. + + + For generating the short-term evolutions with realistic noise models that will be + incurred by a Quantum Computer if it were to simulate short-term evolution terms, + the unitary operator for the evolution is first traspiled into a circuit with + :class:`~qiskit.synthesis.SuzukiTrotter` and :class:`~qiskit.circuit.library.PauliEvolutionGate`. + That is, suppose :math:`U` represents the noisy circuit's effect on a given state to simulate + the evolution through a time step of :math:`\Delta T` (``delta_t`` given by the user), then: + + + .. math:: + U \approx e^{- j H \Delta T} + + + Where the approximate sign signifies that there is noise added by the noisy simulation and + also the approximate nature of transpiling with a trotterized hamiltonian. Now, the + short-term evolution terms are generate until ``train_end`` such evolutions. Suppose we + denote ``train_end`` as N. Then: + + .. math:: + \text{y_train} = + \left[\ket{\psi_{HF}}, U \ket{\psi_{HF}}, ...U^N \ket{\psi_{HF}}\right] + + + Long-term evolution for testing as numerically generated from the exact Hamiltonian without + the uncertainities introduced by noise and trotterization. Suppose ``test_start`` is denoted + as P and ``test_end`` as Q. Then: + + + .. math:: + \text{y_test} = + \left[e^{-jHP\Delta T} \ket{\psi_{HF}}...e^{-jHQ\Delta T} \ket{\psi_{HF}}\right] + + + The choice of noise added in simulation is determined by ``noise_mode``, which can also + fetch calibration data from IBM runtimes. ``formatting`` parameter can be used to get + the data as numpy arrays or as list of statevectors as per the usecase. + + + **References:** + + [1] Filip M-A, Muñoz Ramo D, Fitzpatrick N. *Variational Phase Estimation + with Variational Fast Forwarding*. Quantum. 2024 Mar;8:1278. + `arXiv:2211.16097 `_ + + [2] Cîrstoiu C, Holmes Z, Iosue J, Cincio Ł, Coles PJ, Sornborger A. + *Variational fast forwarding for quantum simulation beyond the coherence time*. + npj Quantum Information. 2020 Sep;6(1):82. + `arXiv:1910.04292 `_ + + delta_t: float, + train_end: int, + test_start: int, + test_end: int, + molecule: str = "H2", + noise_mode: str = "reduced", + formatting: str = "ndarray" + Parameters: + delta_t : Time step per evolution term (in atomic units). 1 a.u. = 2.42e-17 s + train_end : Generate short term evolutions up until math::`U ^ \text{train_end}` + test_start : Generate long term evolution terms from math::`U ^ \text{test_start}` + test_end : Generate long term evolution terms until math::`U ^ \text{test_end}` + molecule : Decides which molecule is being simulation. The options are: + + * ``"H2"``: A linear H2 molecule at 0.735 A bond-length + * ``"H3"``: H3 molecule at an equilateral triangle of side 0.9 A + + Default is ``"H2"``. + noise_mode: The noise model used in the simulation of noisy short term evolutions + Choices are: + + * ``"noiseless"``: Which will generate no noise + * ``"reduced"``: Uses a low noise profile + * One of the IBM runtimes such as "ibm_brisbane". + + Default is ``"reduced"``. The available runtime backends can be found using + :class:`qiskit_ibm_runtime.QiskitRuntimeService.backends` + formatting: The format in which datapoints are given. + Choices are: + + * ``"ndarray"``: gives a numpy array of shape (n_points, 2**n_qubits, 1) + * ``"statevector"``: gives a python list of Statevector objects + + Default is ``"ndarray"``. + + Returns: + Tuple + containing the following: + + * **Hartree-Fock State** : ``np.ndarray`` | ``qiskit.quantum_info.Statevector`` + * **training_timestamps** : ``np.ndarray`` + * **training_states** : ``np.ndarray`` | ``qiskit.quantum_info.Statevector`` + * **testing_timestamps** : ``np.ndarray`` + * **testing_states** : ``np.ndarray`` | ``qiskit.quantum_info.Statevector`` + + """ + + # Errors and Warnings + if delta_t <= 0: + raise ValueError("delta_t must be positive (atomic-units of time).") + + if not isinstance(train_end, int) or train_end < 1: + raise ValueError("train_end must be a positive integer.") + + if not isinstance(test_start, int) or test_start < 1: + raise ValueError("test_start must be a positive integer.") + + if not isinstance(test_end, int) or test_end <= test_start: + raise ValueError("test_end must be an integer greater than test_start.") + + if molecule not in {"H2", "H3"}: # H6 disabled for now + raise ValueError("molecule must be 'H2' or 'H3'; 'H6' is temporarily unsupported.") + if formatting not in {"ndarray", "statevector"}: + raise ValueError("formatting must be 'ndarray' or 'statevector'.") + + if test_start <= train_end: + warnings.warn("Training and testing ranges overlap; this can cause data leakage.") + + backend = None + if noise_mode not in {"reduced", "noiseless"}: + try: + service = QiskitRuntimeService() + # real, operational, ≥4-qubit devices + backends = service.backends(min_num_qubits=4, operational=True, simulator=False) + backend_names = [b.name for b in backends] # list-comprehension + allowed_modes = backend_names + ["reduced", "noiseless"] + + if noise_mode not in allowed_modes: + raise ValueError( + f"'{noise_mode}' is not available. " f"Choose from {allowed_modes}" + ) + + backend = service.backend(noise_mode) + except Exception as exc: + raise RuntimeError( + "Unable to fetch IBM backends; check your internet connection " + "and IBM Quantum account configuration." + ) from exc + + # Electron Occupancy occupancy = {"H2": 2, "H3": 2, "H6": 6} num_occupancy = occupancy[molecule] # Noise Models for Training Data - simulator = _noise_simulator(noise_mode) + simulator = _noise_simulator(noise_mode, backend) # Import Hamiltonian and Unitary Evolution Circuit qc, t, hamiltonian = _evolution_circuit(molecule) @@ -80,6 +235,7 @@ def h_molecule_evolution_data( if formatting == "ndarray": y_train = _to_np(y_train) y_test = _to_np(y_test) + psi_hf = psi_hf.probabilities() return (psi_hf, x_train, y_train, x_test, y_test) @@ -110,7 +266,7 @@ def _hamiltonian_import(molecule): """Import Hamiltonian from Hamiltonians folder""" dir_path = os.path.dirname(__file__) - filename = os.path.join(dir_path, f"hamiltonians\\{molecule}.bin") + filename = os.path.join(dir_path, f"hamiltonians\\h_molecule_hamiltonians\\{molecule}.bin") with open(filename, "rb") as f: spo = pkl.load(f) @@ -132,7 +288,7 @@ def _initial_state(hamiltonian, num_occupancy): return Statevector.from_label(occupation_label) -def _noise_simulator(noise_mode): +def _noise_simulator(noise_mode, backend): """Returns a Noisy/Noiseless AerSimulator object""" if noise_mode == "noiseless": @@ -147,50 +303,43 @@ def _noise_simulator(noise_mode): # If the given Model is an IBM location else: - - service = QiskitRuntimeService() - - try: - backend = service.backend(noise_mode) - except QiskitBackendNotFoundError: - backends = service.backends(min_num_qubits=4, operational=True, simulator=False) - raise QiskitBackendNotFoundError( - f"""The specified backend '{noise_mode}' was not found / was busy. Please select one from {backends}""" - ) - noise_model = NoiseModel.from_backend(backend) simulator = AerSimulator(noise_model=noise_model) return simulator + def _simulate_shortterm(psi_hf, qc_evo, simulator, train_end): """Simulates short-term dynamics using a noisy simulator.""" - - y_train = [] + + y_train = [ + psi_hf, + ] psi = psi_hf.copy() - + for _ in range(train_end): - + # Create a new quantum circuit for each step qc = QuantumCircuit(psi.num_qubits) - + # psi persists after each step qc.initialize(psi.data, qc.qubits) qc.append(qc_evo, qc.qubits) qc.save_statevector() qc_resolved = transpile(qc, simulator) - + # Execute the circuit on the noisy simulator job = simulator.run(qc_resolved) result = job.result() - + # Update the statevector with the result psi = Statevector(result.get_statevector(qc)) y_train.append(psi.copy()) - + return y_train + def _ideal_longterm(psi_hf, H, t): """ Return the list of statevectors exp(-i H t_k) @ psi_hf @@ -200,16 +349,14 @@ def _ideal_longterm(psi_hf, H, t): y_test = [] for t_k in t: - u_t = expm(-1j * h_dense * t_k) + u_t = expm(-1j * h_dense * t_k) psi_t = Statevector(u_t @ psi_hf.data) y_test.append(psi_t) return y_test + def _to_np(states): """Convert list[Statevector] to ndarray""" dim = len(states[0]) return np.stack([sv.data for sv in states], axis=0).reshape(len(states), dim, 1) - - -print(h_molecule_evolution_data(0.1, 3, 6, 8)) diff --git a/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml new file mode 100644 index 000000000..d81a12f76 --- /dev/null +++ b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml @@ -0,0 +1,20 @@ +features: + - The :func:`~qiskit_machine_learning.datasets.h_molecule_evolution_data` function has + been added. This dataset generator simulates time evolution of Hydrogen-based molecules + (H₂, H₃) under their electronic Hamiltonians using quantum simulation. It supports + realistic noise models from IBM Quantum backends and can be used to benchmark + variational fast-forwarding algorithms. + + Example of a 4-qubit H₂ dataset in noiseless statevector format: + + .. code-block:: python + + h_molecule_evolution_data( + delta_t=1.0, + train_end=5, + test_start=10, + test_end=15, + molecule="H2", + noise_mode="noiseless", + formatting="statevector" + ) \ No newline at end of file diff --git a/test/datasets/test_h_molecule_evolution_data.py b/test/datasets/test_h_molecule_evolution_data.py new file mode 100644 index 000000000..2753ab802 --- /dev/null +++ b/test/datasets/test_h_molecule_evolution_data.py @@ -0,0 +1,135 @@ +# This code is part of a Qiskit project. +# +# (C) Copyright IBM 2025. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test H Molecule Evolution Data """ + +from test import QiskitMachineLearningTestCase + +import unittest +import numpy as np +from ddt import ddt, unpack, idata + +from qiskit.quantum_info import Statevector +from qiskit_ibm_runtime import QiskitRuntimeService + +from qiskit_machine_learning.utils import algorithm_globals +from qiskit_machine_learning.datasets import h_molecule_evolution_data + + +@ddt +class TestHMoleculeEvolution(QiskitMachineLearningTestCase): + """H Molecule Evolution Tests""" + + @idata([(Hx, nq) for Hx, nq in [("H2", 4), ("H3", 6)]]) + @unpack + def test_default_params(self, molecule, n_qubits): + """Checking for right shapes and labels""" + HF, x_train, y_train, x_test, y_test = h_molecule_evolution_data( + delta_t=1.0, train_end=2, test_start=4, test_end=6, molecule=molecule + ) + + np.testing.assert_array_equal(HF.shape, (2**n_qubits,)) + np.testing.assert_array_equal(x_train.shape, (3,)) + np.testing.assert_array_equal(x_test.shape, (3,)) + np.testing.assert_array_equal(y_train.shape, (3, 2**n_qubits, 1)) + np.testing.assert_array_equal(y_test.shape, (3, 2**n_qubits, 1)) + + @idata([(Hx, nq) for Hx, nq in [("H2", 4), ("H3", 6)]]) + @unpack + def test_statevector_formatting_noiseless(self, molecule, n_qubits): + """Check if output values are normalized qiskit.circuit_info.Statevector objects""" + HF, x_tr, y_tr, x_te, y_te = h_molecule_evolution_data( + 1.0, + 1, + 3, + 4, + molecule=molecule, + formatting="statevector", + noise_mode="noiseless", + ) + self.assertIsInstance(HF, Statevector) + self.assertTrue(all(isinstance(sv, Statevector) for sv in y_tr)) + self.assertTrue(all(isinstance(sv, Statevector) for sv in y_te)) + self.assertAlmostEqual(HF.probabilities().sum(), 1.0, places=7) + for sv in y_tr[:2] + y_te[:2]: + self.assertAlmostEqual(sv.probabilities().sum(), 1.0, places=7) + np.testing.assert_array_equal(x_tr.shape, (2,)) + np.testing.assert_array_equal(x_te.shape, (2,)) + self.assertEqual(len(y_tr), 2) + self.assertEqual(len(y_te), 2) + + @idata( + [ + (Hx, nq) + for Hx, nq in [ + ("H2", 4), + ] + ] + ) + @unpack + def test_connecting_to_runtime(self, molecule, n_qubits): + """Fetches the best runtime and connects to it's noise model""" + try: + service = QiskitRuntimeService() + backend = service.backends(min_num_qubits=4, operational=True, simulator=False)[0] + except Exception: + self.skipTest("IBMQ account or internet unavailable") + HF, _, y_tr, _, y_te = h_molecule_evolution_data( + 1.0, + 1, + 2, + 3, + molecule=molecule, + noise_mode=backend.name, + formatting="ndarray", + ) + np.testing.assert_array_equal(HF.shape, (2**n_qubits,)) + self.assertEqual(y_tr.shape[-1], 1) + self.assertEqual(y_te.shape[-1], 1) + + def test_error_raises(self): + """Check if parameter errors are handled""" + valid = dict( + delta_t=0.1, + train_end=5, + test_start=6, + test_end=10, + molecule="H2", + noise_mode="noiseless", + formatting="ndarray", + ) + + with self.assertRaises(ValueError): # bad delta_t + h_molecule_evolution_data(**{**valid, "delta_t": 0}) + + with self.assertRaises(ValueError): # bad train_end + h_molecule_evolution_data(**{**valid, "train_end": 0}) + + with self.assertRaises(ValueError): # bad test_start + h_molecule_evolution_data(**{**valid, "test_start": 0}) + + with self.assertRaises(ValueError): # test_end ≤ test_start + h_molecule_evolution_data(**{**valid, "test_end": 5}) + + with self.assertRaises(ValueError): # unsupported molecule + h_molecule_evolution_data(**{**valid, "molecule": "H6"}) + + with self.assertRaises(ValueError): # bad formatting + h_molecule_evolution_data(**{**valid, "formatting": "json"}) + + # invalid backend name – ValueError _or_ RuntimeError depending on connectivity + with self.assertRaises((ValueError, RuntimeError)): + h_molecule_evolution_data(**{**valid, "noise_mode": "bad_backend"}) + + +if __name__ == "__main__": + unittest.main() From 652b06ef842dd7bb35dfe1971ce326f3aaaedd5f Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Wed, 14 May 2025 22:39:36 +0530 Subject: [PATCH 3/9] lint and spell fixes --- .pylintdict | 18 +++++++++ .../datasets/h_molecule_evolution.py | 40 +++++++++---------- ...t_forwarding_dataset-06bbf48921ee645c.yaml | 2 +- requirements-dev.txt | 2 +- requirements.txt | 2 + .../test_h_molecule_evolution_data.py | 39 ++++++++---------- 6 files changed, 56 insertions(+), 47 deletions(-) diff --git a/.pylintdict b/.pylintdict index 34bef92c3..0141314b1 100644 --- a/.pylintdict +++ b/.pylintdict @@ -66,6 +66,8 @@ centroid chernoff choi chuang +cincio +cîrstoiu clbit clbits clopper @@ -164,14 +166,17 @@ fae failsafe farhi farrokh +fermionic fi fidelities fidelity fidelityquantumkernel filippo +filip fletcher fm fmin +fock formatter fourier frac @@ -240,6 +245,7 @@ instantiations interatomic interdependencies ints +iosue iprint iqft isaac @@ -253,11 +259,13 @@ izaac izz jac jacobian +jhp jm jonathan jones july jupyter +jw kandala kernelized killoran @@ -281,6 +289,7 @@ leq lin linalg loglik +longterm loglikelihood lov lr @@ -326,6 +335,7 @@ msg multiclass multinomial multioutput +muñoz mxd mypy nabla @@ -355,6 +365,7 @@ nones nonlocal nosignatures np +npj ns num numpy @@ -398,6 +409,7 @@ pedro pegasos peruzzo pixelated +pj platt polyfit postprocess @@ -452,6 +464,7 @@ quantile quantumcircuit qubit qubits +ramo rangle raymond rbf @@ -501,6 +514,7 @@ serializable shalev shanno shende +shortterm shwartz sigmoid sima @@ -511,6 +525,7 @@ slsqp sobol softmax soloviev +sornborger spall sparsearray spedalieri @@ -575,6 +590,7 @@ transpiling trotterization trotterized tunable +uncertainities uncompiled uncompress uncompute @@ -583,6 +599,7 @@ univariate uno unscaled unsymmetric +usecase utf utils varadarajan @@ -591,6 +608,7 @@ vatan vec vectorized veeravalli +vff vicente vicentini vigo diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index 37991ffaa..74261854e 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -18,16 +18,15 @@ import warnings import os +import pickle as pkl import numpy as np -import pickle as pkl from qiskit import QuantumCircuit, transpile from qiskit.circuit import Parameter -from qiskit.quantum_info import SparsePauliOp, Statevector +from qiskit.quantum_info import Statevector from qiskit.circuit.library import PauliEvolutionGate from qiskit.synthesis import SuzukiTrotter -from qiskit.providers import QiskitBackendNotFoundError from qiskit_ibm_runtime import QiskitRuntimeService @@ -37,9 +36,6 @@ from scipy.linalg import expm -from ..utils import algorithm_globals - - # pylint: disable=too-many-positional-arguments def h_molecule_evolution_data( delta_t: float, @@ -72,10 +68,10 @@ def h_molecule_evolution_data( For generating the short-term evolutions with realistic noise models that will be incurred by a Quantum Computer if it were to simulate short-term evolution terms, - the unitary operator for the evolution is first traspiled into a circuit with + the unitary operator for the evolution is first transpiled into a circuit with :class:`~qiskit.synthesis.SuzukiTrotter` and :class:`~qiskit.circuit.library.PauliEvolutionGate`. That is, suppose :math:`U` represents the noisy circuit's effect on a given state to simulate - the evolution through a time step of :math:`\Delta T` (``delta_t`` given by the user), then: + the evolution through a time step of :math:`\Delta T` (``delta_t`` given by the user), then .. math:: @@ -85,7 +81,7 @@ def h_molecule_evolution_data( Where the approximate sign signifies that there is noise added by the noisy simulation and also the approximate nature of transpiling with a trotterized hamiltonian. Now, the short-term evolution terms are generate until ``train_end`` such evolutions. Suppose we - denote ``train_end`` as N. Then: + denote ``train_end`` as N. Then .. math:: \text{y_train} = @@ -94,7 +90,7 @@ def h_molecule_evolution_data( Long-term evolution for testing as numerically generated from the exact Hamiltonian without the uncertainities introduced by noise and trotterization. Suppose ``test_start`` is denoted - as P and ``test_end`` as Q. Then: + as P and ``test_end`` as Q. Then .. math:: @@ -113,7 +109,7 @@ def h_molecule_evolution_data( with Variational Fast Forwarding*. Quantum. 2024 Mar;8:1278. `arXiv:2211.16097 `_ - [2] Cîrstoiu C, Holmes Z, Iosue J, Cincio Ł, Coles PJ, Sornborger A. + [2] Cîrstoiu C, Holmes Z, Iosue J, Cincio L, Coles PJ, Sornborger A. *Variational fast forwarding for quantum simulation beyond the coherence time*. npj Quantum Information. 2020 Sep;6(1):82. `arXiv:1910.04292 `_ @@ -216,8 +212,8 @@ def h_molecule_evolution_data( simulator = _noise_simulator(noise_mode, backend) # Import Hamiltonian and Unitary Evolution Circuit - qc, t, hamiltonian = _evolution_circuit(molecule) - qc_evo = qc.assign_parameters({t: delta_t}) + qc, time, hamiltonian = _evolution_circuit(molecule) + qc_evo = qc.assign_parameters({time: delta_t}) # Get Hartree Fock State psi_hf = _initial_state(hamiltonian, num_occupancy) @@ -249,9 +245,9 @@ def _evolution_circuit(molecule): spo = _hamiltonian_import(molecule) - t = Parameter("t") + time = Parameter("time") trotterizer = SuzukiTrotter(order=2, reps=1) - u_evolution = PauliEvolutionGate(spo, time=t, synthesis=trotterizer) + u_evolution = PauliEvolutionGate(spo, time=time, synthesis=trotterizer) n_qubits = spo.num_qubits qc = QuantumCircuit(n_qubits) @@ -259,17 +255,17 @@ def _evolution_circuit(molecule): qc_flat = qc.decompose() - return qc_flat, t, spo + return qc_flat, time, spo def _hamiltonian_import(molecule): """Import Hamiltonian from Hamiltonians folder""" dir_path = os.path.dirname(__file__) - filename = os.path.join(dir_path, f"hamiltonians\\h_molecule_hamiltonians\\{molecule}.bin") + filename = os.path.join(dir_path, f"hamiltonians/h_molecule_hamiltonians/{molecule}.bin") - with open(filename, "rb") as f: - spo = pkl.load(f) + with open(filename, "rb") as ham_file: + spo = pkl.load(ham_file) return spo @@ -340,15 +336,15 @@ def _simulate_shortterm(psi_hf, qc_evo, simulator, train_end): return y_train -def _ideal_longterm(psi_hf, H, t): +def _ideal_longterm(psi_hf, hamiltonian, timestamps): """ Return the list of statevectors exp(-i H t_k) @ psi_hf for every t_k in `times`, using an exact matrix exponential. """ - h_dense = H.to_matrix() + h_dense = hamiltonian.to_matrix() y_test = [] - for t_k in t: + for t_k in timestamps: u_t = expm(-1j * h_dense * t_k) psi_t = Statevector(u_t @ psi_hf.data) y_test.append(psi_t) diff --git a/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml index d81a12f76..907900db2 100644 --- a/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml +++ b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml @@ -5,7 +5,7 @@ features: realistic noise models from IBM Quantum backends and can be used to benchmark variational fast-forwarding algorithms. - Example of a 4-qubit H₂ dataset in noiseless statevector format: + Example of a 4-qubit H₂ dataset in noiseless statevector format is below. .. code-block:: python diff --git a/requirements-dev.txt b/requirements-dev.txt index 6a56691fc..85e181931 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,9 +12,9 @@ sphinx-design>=0.4.0 sphinxcontrib-spelling jupyter-sphinx discover -qiskit-aer>=0.11.2 mypy>=0.981 mypy-extensions>=0.4.3 nbsphinx qiskit_sphinx_theme~=1.16.0 +qiskit-aer>=0.11.2 qiskit-ibm-runtime>=0.21 diff --git a/requirements.txt b/requirements.txt index 811a007a0..fc0ef10fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,5 @@ scipy>=1.4 scikit-learn>=1.2 setuptools>=40.1 dill>=0.3.4 +qiskit-aer>=0.11.2 +qiskit-ibm-runtime>=0.21 diff --git a/test/datasets/test_h_molecule_evolution_data.py b/test/datasets/test_h_molecule_evolution_data.py index 2753ab802..f90c7a1e5 100644 --- a/test/datasets/test_h_molecule_evolution_data.py +++ b/test/datasets/test_h_molecule_evolution_data.py @@ -19,9 +19,11 @@ from ddt import ddt, unpack, idata from qiskit.quantum_info import Statevector +from qiskit.providers.exceptions import QiskitBackendNotFoundError + from qiskit_ibm_runtime import QiskitRuntimeService +from qiskit_ibm_runtime.exceptions import IBMInputValueError -from qiskit_machine_learning.utils import algorithm_globals from qiskit_machine_learning.datasets import h_molecule_evolution_data @@ -29,25 +31,25 @@ class TestHMoleculeEvolution(QiskitMachineLearningTestCase): """H Molecule Evolution Tests""" - @idata([(Hx, nq) for Hx, nq in [("H2", 4), ("H3", 6)]]) + @idata([('H2', 4), ('H3', 6)]) @unpack def test_default_params(self, molecule, n_qubits): """Checking for right shapes and labels""" - HF, x_train, y_train, x_test, y_test = h_molecule_evolution_data( + psi_hf, x_train, y_train, x_test, y_test = h_molecule_evolution_data( delta_t=1.0, train_end=2, test_start=4, test_end=6, molecule=molecule ) - np.testing.assert_array_equal(HF.shape, (2**n_qubits,)) + np.testing.assert_array_equal(psi_hf.shape, (2**n_qubits,)) np.testing.assert_array_equal(x_train.shape, (3,)) np.testing.assert_array_equal(x_test.shape, (3,)) np.testing.assert_array_equal(y_train.shape, (3, 2**n_qubits, 1)) np.testing.assert_array_equal(y_test.shape, (3, 2**n_qubits, 1)) - @idata([(Hx, nq) for Hx, nq in [("H2", 4), ("H3", 6)]]) + @idata([('H2',), ('H3',)]) @unpack - def test_statevector_formatting_noiseless(self, molecule, n_qubits): + def test_statevector_formatting_noiseless(self, molecule): """Check if output values are normalized qiskit.circuit_info.Statevector objects""" - HF, x_tr, y_tr, x_te, y_te = h_molecule_evolution_data( + psi_hf, x_tr, y_tr, x_te, y_te = h_molecule_evolution_data( 1.0, 1, 3, @@ -56,10 +58,10 @@ def test_statevector_formatting_noiseless(self, molecule, n_qubits): formatting="statevector", noise_mode="noiseless", ) - self.assertIsInstance(HF, Statevector) + self.assertIsInstance(psi_hf, Statevector) self.assertTrue(all(isinstance(sv, Statevector) for sv in y_tr)) self.assertTrue(all(isinstance(sv, Statevector) for sv in y_te)) - self.assertAlmostEqual(HF.probabilities().sum(), 1.0, places=7) + self.assertAlmostEqual(psi_hf.probabilities().sum(), 1.0, places=7) for sv in y_tr[:2] + y_te[:2]: self.assertAlmostEqual(sv.probabilities().sum(), 1.0, places=7) np.testing.assert_array_equal(x_tr.shape, (2,)) @@ -67,32 +69,23 @@ def test_statevector_formatting_noiseless(self, molecule, n_qubits): self.assertEqual(len(y_tr), 2) self.assertEqual(len(y_te), 2) - @idata( - [ - (Hx, nq) - for Hx, nq in [ - ("H2", 4), - ] - ] - ) - @unpack - def test_connecting_to_runtime(self, molecule, n_qubits): + def test_connecting_to_runtime(self): """Fetches the best runtime and connects to it's noise model""" try: service = QiskitRuntimeService() backend = service.backends(min_num_qubits=4, operational=True, simulator=False)[0] - except Exception: + except (IBMInputValueError, QiskitBackendNotFoundError): self.skipTest("IBMQ account or internet unavailable") - HF, _, y_tr, _, y_te = h_molecule_evolution_data( + psi_hf, _, y_tr, _, y_te = h_molecule_evolution_data( 1.0, 1, 2, 3, - molecule=molecule, + molecule="H2", noise_mode=backend.name, formatting="ndarray", ) - np.testing.assert_array_equal(HF.shape, (2**n_qubits,)) + np.testing.assert_array_equal(psi_hf.shape, (2**4,)) self.assertEqual(y_tr.shape[-1], 1) self.assertEqual(y_te.shape[-1], 1) From 5b63ada1be4b7d6010bea6aceee8d2665e8cae4a Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Wed, 14 May 2025 22:45:39 +0530 Subject: [PATCH 4/9] Reno and unittest fix --- ...variational_fast_forwarding_dataset-06bbf48921ee645c.yaml | 5 +++-- test/datasets/test_h_molecule_evolution_data.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml index 907900db2..de5909fde 100644 --- a/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml +++ b/releasenotes/notes/add_variational_fast_forwarding_dataset-06bbf48921ee645c.yaml @@ -1,5 +1,6 @@ features: - - The :func:`~qiskit_machine_learning.datasets.h_molecule_evolution_data` function has + - | + The :func:`~qiskit_machine_learning.datasets.h_molecule_evolution_data` function has been added. This dataset generator simulates time evolution of Hydrogen-based molecules (H₂, H₃) under their electronic Hamiltonians using quantum simulation. It supports realistic noise models from IBM Quantum backends and can be used to benchmark @@ -17,4 +18,4 @@ features: molecule="H2", noise_mode="noiseless", formatting="statevector" - ) \ No newline at end of file + ) diff --git a/test/datasets/test_h_molecule_evolution_data.py b/test/datasets/test_h_molecule_evolution_data.py index f90c7a1e5..8a84f5615 100644 --- a/test/datasets/test_h_molecule_evolution_data.py +++ b/test/datasets/test_h_molecule_evolution_data.py @@ -23,6 +23,7 @@ from qiskit_ibm_runtime import QiskitRuntimeService from qiskit_ibm_runtime.exceptions import IBMInputValueError +from qiskit_ibm_runtime.accounts.exceptions import AccountNotFoundError from qiskit_machine_learning.datasets import h_molecule_evolution_data @@ -74,7 +75,7 @@ def test_connecting_to_runtime(self): try: service = QiskitRuntimeService() backend = service.backends(min_num_qubits=4, operational=True, simulator=False)[0] - except (IBMInputValueError, QiskitBackendNotFoundError): + except (IBMInputValueError, QiskitBackendNotFoundError, AccountNotFoundError): self.skipTest("IBMQ account or internet unavailable") psi_hf, _, y_tr, _, y_te = h_molecule_evolution_data( 1.0, From 2f2ef6008c51e3237534080787a1eac32156ea08 Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Wed, 14 May 2025 23:29:56 +0530 Subject: [PATCH 5/9] Fixes related to make html --- .pylintdict | 7 +++++++ test/datasets/test_h_molecule_evolution_data.py | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.pylintdict b/.pylintdict index 0141314b1..7c380b82d 100644 --- a/.pylintdict +++ b/.pylintdict @@ -55,6 +55,7 @@ borujeni boyer brassard broyden +brisbane callables cambridge cancelled @@ -173,6 +174,7 @@ fidelity fidelityquantumkernel filippo filip +fitzpatrick fletcher fm fmin @@ -224,6 +226,7 @@ hermitian hessians hilbert hoc +holmes homebrew hopkins hoyer @@ -233,6 +236,7 @@ hubbard hyperparameter hyperparameters hyperplanes +ibm idx im imag @@ -263,6 +267,7 @@ jhp jm jonathan jones +jordan july jupyter jw @@ -510,6 +515,7 @@ scipy sdg seealso semidefinite +sep serializable shalev shanno @@ -622,6 +628,7 @@ vx vy vz wavefunction +wigner wikipedia wilhelm williams diff --git a/test/datasets/test_h_molecule_evolution_data.py b/test/datasets/test_h_molecule_evolution_data.py index 8a84f5615..eb3566b2f 100644 --- a/test/datasets/test_h_molecule_evolution_data.py +++ b/test/datasets/test_h_molecule_evolution_data.py @@ -32,7 +32,7 @@ class TestHMoleculeEvolution(QiskitMachineLearningTestCase): """H Molecule Evolution Tests""" - @idata([('H2', 4), ('H3', 6)]) + @idata([("H2", 4), ("H3", 6)]) @unpack def test_default_params(self, molecule, n_qubits): """Checking for right shapes and labels""" @@ -46,7 +46,7 @@ def test_default_params(self, molecule, n_qubits): np.testing.assert_array_equal(y_train.shape, (3, 2**n_qubits, 1)) np.testing.assert_array_equal(y_test.shape, (3, 2**n_qubits, 1)) - @idata([('H2',), ('H3',)]) + @idata([("H2",), ("H3",)]) @unpack def test_statevector_formatting_noiseless(self, molecule): """Check if output values are normalized qiskit.circuit_info.Statevector objects""" @@ -71,7 +71,7 @@ def test_statevector_formatting_noiseless(self, molecule): self.assertEqual(len(y_te), 2) def test_connecting_to_runtime(self): - """Fetches the best runtime and connects to it's noise model""" + """Fetches the best runtime and connects to its noise model""" try: service = QiskitRuntimeService() backend = service.backends(min_num_qubits=4, operational=True, simulator=False)[0] @@ -90,6 +90,7 @@ def test_connecting_to_runtime(self): self.assertEqual(y_tr.shape[-1], 1) self.assertEqual(y_te.shape[-1], 1) + def test_error_raises(self): """Check if parameter errors are handled""" valid = dict( From 6a24503f2e5e6b205da81218abf14eeefe610184 Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Sat, 17 May 2025 23:25:56 +0530 Subject: [PATCH 6/9] Update h_molecule_evolution.py --- .../datasets/h_molecule_evolution.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index 74261854e..4ba147d12 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -75,29 +75,30 @@ def h_molecule_evolution_data( .. math:: - U \approx e^{- j H \Delta T} - - + U \approx e^{- j H \Delta T} + + Where the approximate sign signifies that there is noise added by the noisy simulation and also the approximate nature of transpiling with a trotterized hamiltonian. Now, the short-term evolution terms are generate until ``train_end`` such evolutions. Suppose we denote ``train_end`` as N. Then + .. math:: - \text{y_train} = + \text{y_train} = \left[\ket{\psi_{HF}}, U \ket{\psi_{HF}}, ...U^N \ket{\psi_{HF}}\right] - - + + Long-term evolution for testing as numerically generated from the exact Hamiltonian without the uncertainities introduced by noise and trotterization. Suppose ``test_start`` is denoted as P and ``test_end`` as Q. Then .. math:: - \text{y_test} = + \text{y_test} = \left[e^{-jHP\Delta T} \ket{\psi_{HF}}...e^{-jHQ\Delta T} \ket{\psi_{HF}}\right] - - + + The choice of noise added in simulation is determined by ``noise_mode``, which can also fetch calibration data from IBM runtimes. ``formatting`` parameter can be used to get the data as numpy arrays or as list of statevectors as per the usecase. From dcd099247ba0ca8fa2f0b6abfe6760483d1b91a7 Mon Sep 17 00:00:00 2001 From: RishiNandha Vanchi Date: Tue, 20 May 2025 17:29:02 +0530 Subject: [PATCH 7/9] Made black --- .../datasets/h_molecule_evolution.py | 14 +++++++------- test/datasets/test_h_molecule_evolution_data.py | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index 4ba147d12..e6b30a356 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -76,19 +76,19 @@ def h_molecule_evolution_data( .. math:: U \approx e^{- j H \Delta T} - - + + Where the approximate sign signifies that there is noise added by the noisy simulation and also the approximate nature of transpiling with a trotterized hamiltonian. Now, the short-term evolution terms are generate until ``train_end`` such evolutions. Suppose we denote ``train_end`` as N. Then - + .. math:: \text{y_train} = \left[\ket{\psi_{HF}}, U \ket{\psi_{HF}}, ...U^N \ket{\psi_{HF}}\right] - - + + Long-term evolution for testing as numerically generated from the exact Hamiltonian without the uncertainities introduced by noise and trotterization. Suppose ``test_start`` is denoted as P and ``test_end`` as Q. Then @@ -97,8 +97,8 @@ def h_molecule_evolution_data( .. math:: \text{y_test} = \left[e^{-jHP\Delta T} \ket{\psi_{HF}}...e^{-jHQ\Delta T} \ket{\psi_{HF}}\right] - - + + The choice of noise added in simulation is determined by ``noise_mode``, which can also fetch calibration data from IBM runtimes. ``formatting`` parameter can be used to get the data as numpy arrays or as list of statevectors as per the usecase. diff --git a/test/datasets/test_h_molecule_evolution_data.py b/test/datasets/test_h_molecule_evolution_data.py index eb3566b2f..86b643459 100644 --- a/test/datasets/test_h_molecule_evolution_data.py +++ b/test/datasets/test_h_molecule_evolution_data.py @@ -90,7 +90,6 @@ def test_connecting_to_runtime(self): self.assertEqual(y_tr.shape[-1], 1) self.assertEqual(y_te.shape[-1], 1) - def test_error_raises(self): """Check if parameter errors are handled""" valid = dict( From 499939a3bd44491afa31f667dc5e9ca131b4e577 Mon Sep 17 00:00:00 2001 From: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Date: Tue, 20 May 2025 12:20:37 -0400 Subject: [PATCH 8/9] Update qiskit_machine_learning/datasets/h_molecule_evolution.py --- qiskit_machine_learning/datasets/h_molecule_evolution.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index e6b30a356..1fc18c552 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -115,13 +115,6 @@ def h_molecule_evolution_data( npj Quantum Information. 2020 Sep;6(1):82. `arXiv:1910.04292 `_ - delta_t: float, - train_end: int, - test_start: int, - test_end: int, - molecule: str = "H2", - noise_mode: str = "reduced", - formatting: str = "ndarray" Parameters: delta_t : Time step per evolution term (in atomic units). 1 a.u. = 2.42e-17 s train_end : Generate short term evolutions up until math::`U ^ \text{train_end}` From cb31e1bc5b0441d342b462e46731dc559cadf15c Mon Sep 17 00:00:00 2001 From: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Date: Tue, 20 May 2025 12:20:43 -0400 Subject: [PATCH 9/9] Update qiskit_machine_learning/datasets/h_molecule_evolution.py --- qiskit_machine_learning/datasets/h_molecule_evolution.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_machine_learning/datasets/h_molecule_evolution.py b/qiskit_machine_learning/datasets/h_molecule_evolution.py index 1fc18c552..e2778c37f 100644 --- a/qiskit_machine_learning/datasets/h_molecule_evolution.py +++ b/qiskit_machine_learning/datasets/h_molecule_evolution.py @@ -117,9 +117,9 @@ def h_molecule_evolution_data( Parameters: delta_t : Time step per evolution term (in atomic units). 1 a.u. = 2.42e-17 s - train_end : Generate short term evolutions up until math::`U ^ \text{train_end}` - test_start : Generate long term evolution terms from math::`U ^ \text{test_start}` - test_end : Generate long term evolution terms until math::`U ^ \text{test_end}` + train_end : Generate short term evolutions up until :math:`U ^ \text{train_end}` + test_start : Generate long term evolution terms from :math:`U ^ \text{test_start}` + test_end : Generate long term evolution terms until :math:`U ^ \text{test_end}` molecule : Decides which molecule is being simulation. The options are: * ``"H2"``: A linear H2 molecule at 0.735 A bond-length