diff --git a/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_learning_dynamics_incoherently.png b/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_learning_dynamics_incoherently.png new file mode 100644 index 0000000000..b69cd3524a Binary files /dev/null and b/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_learning_dynamics_incoherently.png differ diff --git a/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_large_LearningDynamicsIncoherently.png b/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_large_LearningDynamicsIncoherently.png new file mode 100644 index 0000000000..e7b94701f4 Binary files /dev/null and b/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_large_LearningDynamicsIncoherently.png differ diff --git a/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_learning_dynamics_incoherently.png b/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_learning_dynamics_incoherently.png new file mode 100644 index 0000000000..ddfbec38d6 Binary files /dev/null and b/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_learning_dynamics_incoherently.png differ diff --git a/demonstrations/tutorial_learning_dynamics_incoherently.metadata.json b/demonstrations/tutorial_learning_dynamics_incoherently.metadata.json new file mode 100644 index 0000000000..e5a3d36578 --- /dev/null +++ b/demonstrations/tutorial_learning_dynamics_incoherently.metadata.json @@ -0,0 +1,66 @@ +{ + "title": "Learning dynamics incoherently", + "authors": [ + { + "username": "Diego" + } + ], + "dateOfPublication": "2024-08-15T00:00:00+00:00", + "dateOfLastModification": "2024-08-15T00:00:00+00:00", + "categories": [ + "Quantum Machine Learning", + "How-to" + ], + "tags": [], + "previewImages": [ + { + "type": "thumbnail", + "uri": "_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_learning_dynamics_incoherently.png" + }, + { + "type": "large_thumbnail", + "uri": "/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_learning_dynamics_incoherently.png" + } + ], + "seoDescription": "Learn how to reproduce an unknown quantum process with classical shadow measurements", + "doi": "", + "canonicalURL": "/qml/demos/learning_dynamics_incoherently", + "references": [ + { + "id": "jerbi2023power", + "type": "article", + "title": "The power and limitations of learning quantum dynamics incoherently", + "authors": "Sofiene Jerbi, Joe Gibbs, Manuel S. Rudolph, Matthias C. Caro, Patrick J. Coles, Hsin-Yuan Huang, and Zoë Holmes", + "year": "2023", + "url": "https://arxiv.org/abs/2303.12834" + }, + { + "id": "Huang2022Quantum", + "type": "article", + "title": "Quantum advantage in learning from experiments", + "journal": "Science", + "authors": "Hsin-Yuan Huang, Michael Broughton, Jordan Cotler, Sitan Chen, Jerry Li, Masoud Mohseni, Hartmut Neven, Ryan Babbush, Richard Kueng, John Preskill, and Jarrod R. McClean", + "year": "2022", + "url": "http://dx.doi.org/10.1126/science.abn7293" + } + ], + "basedOnPapers": ["10.48550/arXiv.2303.12834"], + "referencedByPapers": [], + "relatedContent": [ + { + "type": "demonstration", + "id": "tutorial_haar_measure", + "weight": 1.0 + }, + { + "type": "demonstration", + "id": "tutorial_classical_shadows", + "weight": 1.0 + }, + { + "type": "demonstration", + "id": "tutorial_variational_classifier", + "weight": 1.0 + } + ] +} diff --git a/demonstrations/tutorial_learning_dynamics_incoherently.py b/demonstrations/tutorial_learning_dynamics_incoherently.py new file mode 100644 index 0000000000..c58f168a0a --- /dev/null +++ b/demonstrations/tutorial_learning_dynamics_incoherently.py @@ -0,0 +1,435 @@ +r"""Learning quantum dynamics incoherently: Variational learning using classical shadows +======================================================================================== + +How can we recreate and simulate an unknown quantum process with a quantum circuit? One approach is +to learn the dynamics of this process incoherently, as done by Jerbi et al. [#Jerbi]_. +Here, we'll reproduce the numerical simulations of [#Jerbi]_ using the authors' data, as provided +in the +`Learning Dynamics Incoherently PennyLane Dataset `__. + +This approach differs from learning the quantum process *coherently* [#Huang]_ because it does not +require the model circuit to be connected to the target quantum process. That is, the model circuit +does not receive quantum information from the target process directly. Instead, we train the model +circuit using classical information from the classical shadow measurements. This works well for +low-entangling processes but can require an exponential number of classical shadow measurements, +depending on the unknown quantum process [#Jerbi]_. This is useful because +it's not always possible to port the quantum output of a system directly to hardware without +first measuring it. + +.. figure:: ../_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_large_LearningDynamicsIncoherently.png + :align: center + :width: 80% + +In simple terms, learning dynamics incoherently consists of two steps. First, we measure the output +of the unknown process for many different inputs. In this tutorial, we do this by measuring +:doc:`classical shadows ` of the target process output. + +Then, we adjust a variational quantum circuit +until it produces the same input-output combinations as the unknown process. +Here, we will +simulate the model circuit output and use the classical shadow measurements to estimate the +overlap between the model output states and the unknown process output states. +""" + +###################################################################### +# 1. Creating an unknown target quantum process +# ---------------------------------------------- +# +# For our unknown quantum process, we will use the +# time evolution of a Hamiltonian: +# +# .. math:: U(H, t) = e^{-i H t / \hbar} . +# +# For the Hamiltonian, :math:`H`, we choose a transverse-field Ising Hamiltonian (as in +# [#Jerbi]_): +# +# .. math:: H = \sum_{i=0}^{n-1} Z_iZ_{i+1} + \sum_{i=0}^{n}\alpha_iX_i, +# +# where :math:`n` is the number of qubits and :math:`\alpha` are randomly generated weights. +# +# More specifically, we will approximate :math:`U(H, t)` via +# `Trotterization `_. +# We first create the Hamiltonian and Trotterize later with :class:`~pennylane.TrotterProduct`. +# + +import pennylane as qml +from pennylane import numpy as pnp +import numpy as np +import matplotlib.pyplot as plt + +# number of qubits for the Hamiltonian +n_qubits = 2 + +# set random seed for reproducibility +pnp.random.seed(7) +np.random.seed(7) + +# generate random weights +alphas = np.random.normal(0, 0.5, size=n_qubits) + +# create the Hamiltonian +hamiltonian = qml.sum( + *[qml.PauliZ(wires=i) @ qml.PauliZ(wires=i + 1) for i in range(n_qubits - 1)] +) + qml.dot(alphas, [qml.PauliX(wires=i) for i in range(n_qubits)]) + +###################################################################### +# 2. Creating random initial states +# ----------------------------------- +# +# The next step is to prepare a set of initial states. We will then apply the +# unknown quantum process to each of these states and measure the output to create input-output +# pairs. Later, we will train a model circuit to generate the same input-output pairs, thus +# reproducing the unknown quantum process. +# +# Ideally, our input states should be uniformly distributed over the state space. If they are all +# clustered together, our model circuit will not learn to approximate the unknown quantum process +# behavior for states that are very different from our training set. +# +# For quantum systems, this means we want to sample +# :doc:`Haar-random states `, as done below. +# + +from scipy.stats import unitary_group + +n_random_states = 100 + +# Generate several random unitaries +random_unitaries = unitary_group.rvs(2**n_qubits, n_random_states) +# Take the first column of each unitary as a random state +random_states = [random_unitary[:, 0] for random_unitary in random_unitaries] + +###################################################################### +# .. note :: +# +# On a personal computer, this method becomes slow (>1 second) around 10 qubits. +# + + +###################################################################### +# 3. Time evolution and classical shadow measurements +# ---------------------------------------------------- +# +# Now we can evolve the initial states using a Trotterized version of the +# `Hamiltonian above <#creating-an-unknown-target-quantum-process>`_. This +# will approximate the time evolution of the transverse-field Ising system. +# + +dev = qml.device("default.qubit") + +@qml.qnode(dev) +def target_circuit(input_state): + # prepare training state + qml.StatePrep(input_state, wires=range(n_qubits)) + + # evolve the Hamiltonian for time=2 in n=1 steps with the order 1 formula + qml.TrotterProduct(hamiltonian, time=2, n=1, order=1) + return qml.classical_shadow(wires=range(n_qubits)) + + +qml.draw_mpl(target_circuit)(random_states[0]) +plt.show() + +###################################################################### +# +# Since ``target_circuit`` returns :func:`~pennylane.classical_shadow`, running the circuit with a +# ``shot`` value gives the desired number of classical shadow measurements. +# We use this to create a set of shadows for each initial state. +# + +n_measurements = 10000 + +shadows = [] +for random_state in random_states: + bits, recipes = target_circuit(random_state, shots=n_measurements) + shadow = qml.ClassicalShadow(bits, recipes) + shadows.append(shadow) + + +###################################################################### +# 4. Creating a model circuit that will learn the target process +# ---------------------------------------------------------------- +# +# Now that we have the classical shadow measurements, we need to create a model circuit that +# learns to produce the same output as the target circuit. +# +# As done in [#Jerbi]_, we create a model circuit with the same gate structure as the target +# circuit. If the target quantum process were truly unknown, then we would choose a general +# variational quantum circuit like in the +# :doc:`Variational classifier demo `. +# +# .. note :: +# +# We use *local* measurements to keep the computational complexity low and +# because classical shadows are well-suited to estimating local observables [#Jerbi]_. +# For this reason, the following circuit returns local density matrices for each qubit. In +# hardware, the density matrix is obtained via state tomography using Pauli measurements or classical shadows. + + +@qml.qnode(dev) +def model_circuit(params, random_state): + qml.StatePrep(random_state, wires=range(n_qubits)) + # parameterized quantum circuit with the same gate structure as the target + for i in range(n_qubits): + qml.RX(params[i], wires=i) + + for i in reversed(range(n_qubits - 1)): + qml.IsingZZ(params[n_qubits + i], wires=[i, i + 1]) + return [qml.density_matrix(i) for i in range(n_qubits)] + + +initial_params = pnp.random.random(size=n_qubits*2-1, requires_grad=True) + +qml.draw_mpl(model_circuit)(initial_params, random_states[0]) +plt.show() + +###################################################################### +# 5. Training using classical shadows in a cost function +# ------------------------------------------------------ +# +# We now have to find the optimal parameters for ``model_circuit`` to mirror the ``target_circuit``. +# We can estimate the similarity between the circuits according to this cost function (see +# Appendix B of [#Jerbi]_): +# +# .. math:: C^l_N(\theta) = 1 - \frac{1}{nN}\sum^N_{j=1}\sum^n_{i=1}Tr[U|\psi^{(j)}\rangle\langle\psi^{(j)}|U^\dagger O^{(j)}_i(\theta)], +# +# where :math:`n` is the number of qubits, :math:`N` is the number of initial states, :math:`\psi^{(j)}` +# are random states, :math:`U` is our target unitary operation, and :math:`O_i` is the local density +# matrix for qubit :math:`i` after applying the ``model_circuit``. That is, the local states +# :math:`\rho_{i}^{(j)}` are used as the observables: +# +# .. math:: O_{i}^{(j)}(\theta) := \rho_{i}^{(j)}. +# +# We can calculate this cost for our system by using the +# `shadow measurements <#time-evolution-and-classical-shadow-measurements>`_ to estimate +# the expectation value of :math:`O_i`. Roughly, this cost function measures the fidelity between +# the model circuit and the target circuit, by proxy of the single-qubit reduced states +# :math:`\rho_{i}^{(j)}` of the model over a variety of input-output pairs. + + +def cost(params): + cost = 0.0 + for idx, random_state in enumerate(random_states): + # obtain the density matrices for each qubit + observable_mats = model_circuit(params, random_state) + # convert to a PauliSentence + observable_pauli = [ + qml.pauli_decompose(observable_mat, wire_order=[qubit]) + for qubit, observable_mat in enumerate(observable_mats) + ] + # estimate the overlap for each qubit + cost = cost + qml.math.sum(shadows[idx].expval(observable_pauli)) + cost = 1 - cost / n_qubits / n_random_states + return cost + + +params = initial_params + +optimizer = qml.GradientDescentOptimizer(stepsize=5) +steps = 50 + +costs = [None]*(steps+1) +params_list = [None]*(steps+1) + +params_list[0]=initial_params +for i in range(steps): + params_list[i + 1], costs[i] = optimizer.step_and_cost(cost, params_list[i]) + +costs[-1] = cost(params_list[-1]) + +print("Initial cost:", costs[0]) +print("Final cost:", costs[-1]) + +###################################################################### +# +# We can plot the cost over the iterations and compare it to the ideal cost. +# + + +# find the ideal parameters from the original Trotterized Hamiltonian +ideal_parameters = [ + op.decomposition()[0].parameters[0] + for op in qml.TrotterProduct(hamiltonian, 2, 1, 1).decomposition() +] +ideal_parameters = ideal_parameters[:n_qubits][::-1] + ideal_parameters[n_qubits:] + +ideal_cost = cost(ideal_parameters) + +plt.plot(costs, label="Training") +plt.plot([0, steps], [ideal_cost, ideal_cost], "r--", label="Ideal parameters") +plt.ylabel("Cost") +plt.xlabel("Training iterations") +plt.legend() +plt.show() + +###################################################################### +# In this case, we see +# that the ideal cost is greater than 0. This is because for the ideal parameters, the model outputs +# and target outputs are equal: +# +# .. math:: \rho_{i}^{(j)} := O_i = U|\psi^{(j)}\rangle\langle\psi^{(j)}|U^\dagger. +# +# Since the single-qubit reduced states used in the +# cost function are mixed states, the trace of their square is less than one: +# +# .. math:: Tr[(\rho_{i}^{(j)})^2] < 1. +# +# The ideal cost :math:`C^l_N(\theta)` is therefore greater than 0. +# +# We can also look at the :func:`trace_distance ` between the unitary +# matrix of the target circuit and the model circuit. As the circuits become more similar with each +# training iteration, we should see the trace distance decrease and reach a low value. +# + +import scipy + +target_matrix = qml.matrix( + qml.TrotterProduct(hamiltonian, 2, 1, 1), + wire_order=range(n_qubits), +) + +zero_state = [1] + [0]*(2**n_qubits-1) + +# model matrix using the all-|0> state to negate state preparation effects +model_matrices = [qml.matrix(model_circuit, wire_order=range(n_qubits))(params, zero_state) for params in params_list] +trace_distances = [qml.math.trace_distance(target_matrix, model_matrix) for model_matrix in model_matrices] + +plt.plot(trace_distances) +plt.ylabel("Trace distance") +plt.xlabel("Training iterations") +plt.show() + +print("The final trace distance is: \n", trace_distances[-1]) + + +###################################################################### +# Using the Learning Dynamics Incoherently PennyLane Dataset +# ---------------------------------------------------------- +# +# In Jerbi et al. [#Jerbi]_, the authors perform this procedure to learn dynamics incoherently on a +# larger, 16-qubit transverse-field Ising +# Hamiltonian, and use classical shadow samples from quantum hardware to estimate the cost function. +# The corresponding `Learning Dynamics Incoherently PennyLane Dataset `__ +# can be downloaded via the :mod:`qml.data` module. + +[ds] = qml.data.load("other", name="learning-dynamics-incoherently") + +# print the available data +print(ds.list_attributes()) + +# print more information about the hamiltonian +print(ds.attr_info["hamiltonian"]["doc"]) + +###################################################################### +# +# The unknown target Hamiltonian, Haar-random initial states, and resulting classical shadow +# measurements are all available in the dataset. +# +# .. note :: +# +# We use few shadows to keep the computational time low and the dataset contains only two +# training states. + +random_states = ds.training_states + +n_measurements = 10000 +shadows = [qml.ClassicalShadow(shadow_meas[:n_measurements], shadow_bases[:n_measurements]) for shadow_meas, shadow_bases in zip(ds.shadow_meas,ds.shadow_bases)] + +###################################################################### +# +# We only need to create the model circuit, cost function, and train. +# For these we use the same model circuit as +# `above <#creating-a-model-circuit-that-will-learn-the-target-process>`_, updated to reflect +# the increased number of qubits. +# + +dev = qml.device("default.qubit") + +@qml.qnode(dev) +def model_circuit(params, random_state): + # this is a parameterized quantum circuit with the same gate structure as the target unitary + qml.StatePrep(random_state, wires=range(16)) + for i in range(16): + qml.RX(params[i], wires=i) + + for i in reversed(range(15)): + qml.IsingZZ(params[16 + i], wires=[i, i + 1]) + return [qml.density_matrix(i) for i in range(16)] + + +initial_params = pnp.random.random(size=31) + +qml.draw_mpl(model_circuit)(initial_params, random_states[0]) +plt.show() + +###################################################################### +# +# We can then minimize the cost to train the model to output the same states as the target circuit. +# For this, we can use the cost function from +# `before <#training-using-classical-shadows-in-a-cost-function>`_, +# as long as we update the number of qubits and the number of random states. +# + +n_qubits = 16 +n_random_states = len(ds.training_states) + +optimizer = qml.GradientDescentOptimizer(stepsize=5) +steps = 50 + +costs = [None]*(steps+1) +params_list = [None]*(steps+1) + +params_list[0]=initial_params +for i in range(steps): + params_list[i + 1], costs[i] = optimizer.step_and_cost(cost, params_list[i]) + +costs[-1] = cost(params_list[-1]) + +print("Initial cost:", cost(initial_params)) +print("Final cost:", costs[-1]) + +###################################################################### +# As a quick check, we can take a look at the density matrices +# to see whether the training was successful: +# + +original_matrices = model_circuit(initial_params, random_states[0]) +learned_matrices = model_circuit(params_list[-1], random_states[0]) +target_matrices_shadow = np.mean(shadows[0].local_snapshots(), axis=0) + +print("Untrained example output state\n", original_matrices[0]) +print("Trained example output state\n", learned_matrices[0]) +print("Target output state\n", target_matrices_shadow[0]) + +###################################################################### +# +# After training, the model outputs are closer to the target outputs, but not quite the same. +# This is due to the limitations of this learning method. Even for a simple circuit like the +# short-time evolution of a first order single Trotter step, it requires a large number of +# shadow measurements and training states to faithfully reproduce the underlying quantum process. +# The results can be improved by increasing the number of training states and +# :doc:`classical shadow measurements `. +# + + +############################################################################## +# +# References +# ------------ +# +# .. [#Jerbi] +# +# Sofiene Jerbi, Joe Gibbs, Manuel S. Rudolph, Matthias C. Caro, Patrick J. Coles, Hsin-Yuan Huang, Zoë Holmes +# "The power and limitations of learning quantum dynamics incoherently" +# `arXiv:2303.12834 `__, 2005. +# +# .. [#Huang] +# +# Hsin-Yuan Huang, Michael Broughton, Jordan Cotler, Sitan Chen, Jerry Li, Masoud Mohseni, Hartmut Neven, Ryan Babbush, Richard Kueng, John Preskill, and Jarrod R. McClean +# "Quantum advantage in learning from experiments" +# `Science `__, 2022 +# + +############################################################################## +# About the author +# ------------------ +# diff --git a/poetry.lock b/poetry.lock index 79fb320e14..46d911ff2d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "absl-py" @@ -1267,13 +1267,6 @@ files = [ {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa42a605d099ee7d41ba2b5fb75e21423951fd26e5d50583a00471238fb3021d"}, {file = "dm_tree-0.1.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b7764de0d855338abefc6e3ee9fe40d301668310aa3baea3f778ff051f4393"}, {file = "dm_tree-0.1.8-cp311-cp311-win_amd64.whl", hash = "sha256:a5d819c38c03f0bb5b3b3703c60e4b170355a0fc6b5819325bf3d4ceb3ae7e80"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ea9e59e0451e7d29aece402d9f908f2e2a80922bcde2ebfd5dcb07750fcbfee8"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:94d3f0826311f45ee19b75f5b48c99466e4218a0489e81c0f0167bda50cacf22"}, - {file = "dm_tree-0.1.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:435227cf3c5dc63f4de054cf3d00183790bd9ead4c3623138c74dde7f67f521b"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09964470f76a5201aff2e8f9b26842976de7889300676f927930f6285e256760"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75c5d528bb992981c20793b6b453e91560784215dffb8a5440ba999753c14ceb"}, - {file = "dm_tree-0.1.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a94aba18a35457a1b5cd716fd7b46c5dafdc4cf7869b4bae665b91c4682a8e"}, - {file = "dm_tree-0.1.8-cp312-cp312-win_amd64.whl", hash = "sha256:96a548a406a6fb15fe58f6a30a57ff2f2aafbf25f05afab00c8f5e5977b6c715"}, {file = "dm_tree-0.1.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8c60a7eadab64c2278861f56bca320b2720f163dca9d7558103c3b77f2416571"}, {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af4b3d372f2477dcd89a6e717e4a575ca35ccc20cc4454a8a4b6f8838a00672d"}, {file = "dm_tree-0.1.8-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de287fabc464b8734be251e46e06aa9aa1001f34198da2b6ce07bd197172b9cb"}, @@ -5204,7 +5197,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -6427,7 +6419,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"} [package.extras] aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] @@ -8097,4 +8089,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "~3.10.0" -content-hash = "9d79a3c7864580ff575f8eed195b739cca63167e993e57f175bbe0127496a48f" +content-hash = "3da4543125949b373a49672066a22085ff48f34b7ee490f7c4a242cef1476a2c" diff --git a/pyproject.toml b/pyproject.toml index d7d26ab02f..1f9802257a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,9 @@ zstd = "*" dill = "*" stim = "*" quimb = "1.8.2" +aiohttp = "3.9.5" +fsspec = "2024.6.1" +h5py = "3.11.0" qiskit = ">=1.0.0" qiskit-aer = ">=0.14.0" sphinxcontrib-applehelp = "1.0.8"