Hi all, I am working on an experiment in which I am trying to implement parallel operations for a quantum-classical hybrid network that uses the qiskit.aer
device instantiated with a noise model.
In my approach to parallel operations, I am trying to distribute part of the process over four individual GPUs. The code for my approach is adapted from the quantum GAN demo:
import math
import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import pennylane as qml
# Pytorch imports
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
from qiskit_aer import AerSimulator
from qiskit_aer.noise import (NoiseModel, QuantumError, ReadoutError,
pauli_error, depolarizing_error, thermal_relaxation_error)
from copy import deepcopy
# Set the random seed for reproducibility
seed = 42
torch.manual_seed(seed)
np.random.seed(seed)
random.seed(seed)
device_list = [torch.device("cuda:" + str(i)) for i in range(torch.cuda.device_count())]
n_devs = len(device_list)
class DigitsDataset(Dataset):
"""Pytorch dataloader for the Optical Recognition of Handwritten Digits Data Set"""
def __init__(self, csv_file, label=0, transform=None):
"""
Args:
csv_file (string): Path to the csv file with annotations.
root_dir (string): Directory with all the images.
transform (callable, optional): Optional transform to be applied
on a sample.
"""
self.csv_file = csv_file
self.transform = transform
self.df = self.filter_by_label(label)
def filter_by_label(self, label):
# Use pandas to return a dataframe of only zeros
df = pd.read_csv(self.csv_file)
df = df.loc[df.iloc[:, -1] == label]
return df
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
if torch.is_tensor(idx):
idx = idx.tolist()
image = self.df.iloc[idx, :-1] / 16
image = np.array(image)
image = image.astype(np.float32).reshape(8, 8)
if self.transform:
image = self.transform(image)
# Return image and label
return image, 0
image_size = 8 # Height / width of the square images
batch_size = 1
transform = transforms.Compose([transforms.ToTensor()])
dataset = DigitsDataset(csv_file="optdigits.tra", transform=transform)
dataloader = torch.utils.data.DataLoader(
dataset, batch_size=batch_size, shuffle=True, drop_last=True
)
plt.figure(figsize=(8,2))
for i in range(8):
image = dataset[i][0].reshape(image_size,image_size)
plt.subplot(1,8,i+1)
plt.axis('off')
plt.imshow(image.numpy(), cmap='gray')
plt.show()
class Discriminator(nn.Module):
"""Fully connected classical discriminator"""
def __init__(self):
super().__init__()
self.model = nn.Sequential(
# Inputs to first hidden layer (num_input_features -> 64)
nn.Linear(image_size * image_size, 64),
nn.ReLU(),
# First hidden layer (64 -> 16)
nn.Linear(64, 16),
nn.ReLU(),
# Second hidden layer (16 -> output)
nn.Linear(16, 1),
nn.Sigmoid(),
)
def forward(self, x):
return self.model(x)
# Quantum variables
n_qubits = 5 # Total number of qubits / N
n_a_qubits = 1 # Number of ancillary qubits / N_A
q_depth = 6 # Depth of the parameterised quantum circuit / D
n_generators = 4 # Number of subgenerators for the patch method / N_G
# Quantum simulator
#dev = qml.device('qiskit.aer', wires=n_qubits, noise_model=None, method="statevector_gpu", analytic=True)
# Enable CUDA device if available
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
def quantum_circuit(noise, weights):
weights = weights.reshape(q_depth, n_qubits)
# Initialise latent vectors
for i in range(n_qubits):
qml.RY(noise[i], wires=i)
# Repeated layer
for i in range(q_depth):
# Parameterised layer
for y in range(n_qubits):
qml.RY(weights[i][y], wires=y)
# Control Z gates
for y in range(n_qubits - 1):
qml.CZ(wires=[y, y + 1])
return qml.probs(wires=list(range(n_qubits)))
# For further info on how the non-linear transform is implemented in Pennylane
# https://discuss.pennylane.ai/t/ancillary-subsystem-measurement-then-trace-out/1532
def partial_measure(probs):
# Non-linear Transform
#probs = quantum_circuit(noise, weights)
probsgiven0 = probs[: (2 ** (n_qubits - n_a_qubits))]
probsgiven0 /= torch.sum(probs)
# Post-Processing
probsgiven = probsgiven0 / torch.max(probsgiven0)
return probsgiven
class PatchQuantumGenerator(nn.Module):
"""Quantum generator class for the patch method"""
def __init__(self, n_generators, q_delta=1):
"""
Args:
n_generators (int): Number of sub-generators to be used in the patch method.
q_delta (float, optional): Spread of the random distribution for parameter initialisation.
"""
super().__init__()
self.q_params = nn.ParameterList(
[
nn.Parameter(q_delta * torch.rand(q_depth * n_qubits), requires_grad=True)
for _ in range(n_generators)
]
)
self.n_generators = n_generators
self.torch_device = None
self.dev = qml.device('qiskit.aer', wires=n_qubits, noise_model=None, method="statevector_gpu", analytic=True)
self.dev.backend.set_options(blocking_enable=True)
self.dev.backend.set_options(shot_branching_enable=True)
self.dev.backend.set_options(shot_branching_sampling_enable=True)
self.dev.backend.set_options(num_threads_per_device=512)
new_noise_model = NoiseModel()
error_prob = np.random.uniform(0, 1)
noise_prob = depolarizing_error(error_prob, 1)
new_noise_model.add_all_qubit_quantum_error(noise_prob, ['ry'])
self.dev.backend.set_options(noise_model=new_noise_model)
self.node = qml.QNode(quantum_circuit, self.dev)
def forward(self, x):
# Size of each sub-generator output
patch_size = 2 ** (n_qubits - n_a_qubits)
# Create a Tensor to 'catch' a batch of images from the for loop. x.size(0) is the batch size.
images = torch.Tensor(x.size(0), 0).to(self.torch_device if self.torch_device is not None else device)
# Iterate over all sub-generators
for params in self.q_params:
# Create a Tensor to 'catch' a batch of the patches from a single sub-generator
patches = torch.Tensor(0, patch_size).to(self.torch_device if self.torch_device is not None else device)
for elem in x:
probs = self.node(elem, params)
q_out = partial_measure(probs).float().unsqueeze(0)
patches = torch.cat((patches, q_out))
# Each batch of patches is concatenated with each other to create a batch of images
images = torch.cat((images, patches), 1)
return images
def set_torch_device(self, torch_device):
self.torch_device = torch_device
lrG = 0.3 # Learning rate for the generator
lrD = 0.01 # Learning rate for the discriminator
num_iter = 500 # Number of training iterations
discriminator = Discriminator().to(device)
generator = PatchQuantumGenerator(n_generators).to(device)
gen_clones = nn.parallel.replicate(generator, device_list)
for i in range(n_devs):
gen_clones[i].set_torch_device(device_list[i])
# Binary cross entropy
criterion = nn.BCELoss()
# Optimisers
optD = optim.SGD(discriminator.parameters(), lr=lrD)
optG = optim.SGD(generator.parameters(), lr=lrG)
real_labels = torch.full((batch_size,), 1.0, dtype=torch.float, device=device)
fake_labels = torch.full((batch_size,), 0.0, dtype=torch.float, device=device)
# Fixed noise allows us to visually track the generated images throughout training
fixed_noise = torch.rand(8, n_qubits, device=device) * math.pi / 2
# Iteration counter
counter = 0
# Collect images for plotting later
results = []
while True:
for i, (data, _) in enumerate(dataloader):
# Data for training the discriminator
data = data.reshape(-1, image_size * image_size)
real_data = data.to(device)
# Noise follwing a uniform distribution in range [0,pi/2)
noise = torch.rand(batch_size, n_qubits, device=device) * math.pi / 2
noise_clones = [noise.clone() for i in range(n_devs)]
noise_data = torch.stack(noise_clones)
noise_inputs = list(nn.parallel.scatter(noise_data, device_list))
for i in range(n_devs):
noise_inputs[i] = noise_inputs[i].view(noise.shape)
fake_data_list = nn.parallel.parallel_apply(gen_clones, noise_inputs)
print("Parallel apply done")
fake_data_gather = nn.parallel.gather(fake_data_list, device_list[0])
fake_data = torch.sum(fake_data_gather) / n_devs
#fake_data = generator(noise)
# Training the discriminator
discriminator.zero_grad()
outD_real = discriminator(real_data).view(-1)
outD_fake = discriminator(fake_data.detach()).view(-1)
errD_real = criterion(outD_real, real_labels)
errD_fake = criterion(outD_fake, fake_labels)
# Propagate gradients
errD_real.backward()
errD_fake.backward()
errD = errD_real + errD_fake
optD.step()
# Training the generator
generator.zero_grad()
outD_fake = discriminator(fake_data).view(-1)
errG = criterion(outD_fake, real_labels)
errG.backward()
optG.step()
counter += 1
# Show loss values
if counter % 10 == 0:
print(f'Iteration: {counter}, Discriminator Loss: {errD:0.3f}, Generator Loss: {errG:0.3f}')
test_images = generator(fixed_noise).view(8,1,image_size,image_size).cpu().detach()
# Save images every 50 iterations
if counter % 50 == 0:
results.append(test_images)
if counter == num_iter:
break
if counter == num_iter:
break
fig = plt.figure(figsize=(10, 5))
outer = gridspec.GridSpec(5, 2, wspace=0.1)
for i, images in enumerate(results):
inner = gridspec.GridSpecFromSubplotSpec(1, images.size(0),
subplot_spec=outer[i])
images = torch.squeeze(images, dim=1)
for j, im in enumerate(images):
ax = plt.Subplot(fig, inner[j])
ax.imshow(im.numpy(), cmap="gray")
ax.set_xticks([])
ax.set_yticks([])
if j==0:
ax.set_title(f'Iteration {50+i*50}', loc='left')
fig.add_subplot(ax)
plt.show()
When I run the code, I get the following error message:
Simulation failed and returned the following error message:
ERROR: Failed to load circuits: Duplicate key "_method_" in save instruction.
Simulation failed and returned the following error message:
ERROR: Failed to load circuits: Duplicate key "_method_" in save instruction.
Traceback (most recent call last):
File "/N/slate/justsing/CausalTensornet/pennylane_qiskit_toy_example.py", line 263, in <module>
fake_data_list = nn.parallel.parallel_apply(gen_clones, noise_inputs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/parallel/parallel_apply.py", line 108, in parallel_apply
output.reraise()
File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/_utils.py", line 705, in reraise
raise exception
qiskit.exceptions.QiskitError: 'Caught QiskitError in replica 0 on device 0.\nOriginal Traceback (most recent call last):\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/parallel/parallel_apply.py", line 83, in _worker\n output = module(*input, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1532, in _wrapped_call_impl\n return self._call_impl(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1541, in _call_impl\n return forward_call(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/slate/justsing/CausalTensornet/pennylane_qiskit_toy_example.py", line 207, in forward\n probs = self.node(elem, params)\n ^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1164, in __call__\n return self._impl_call(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1150, in _impl_call\n res = self._execution_component(args, kwargs, override_shots=override_shots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1103, in _execution_component\n res = qml.execute(\n ^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/execution.py", line 835, in execute\n results = ml_boundary_execute(tapes, execute_fn, jpc, device=device)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 236, in execute\n return ExecuteTapes.apply(kwargs, *parameters)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 89, in new_apply\n flat_out = orig_apply(out_struct_holder, *inp)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/autograd/function.py", line 598, in apply\n return super().apply(*args, **kwargs) # type: ignore[misc]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 93, in new_forward\n out = orig_fw(ctx, *inp)\n ^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 158, in forward\n res = tuple(kwargs["execute_fn"](ctx.tapes))\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/execution.py", line 316, in inner_execute\n results = device_execution(transformed_tapes)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/soft/rhel8/deeplearning/Python-3.11.5/lib/python3.11/contextlib.py", line 81, in inner\n return func(*args, **kwds)\n ^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane_qiskit/qiskit_device.py", line 519, in batch_execute\n self._state = self._get_state(result, experiment=circuit_obj)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane_qiskit/qiskit_device.py", line 416, in _get_state\n state = np.asarray(result.get_statevector(experiment))\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 314, in get_statevector\n self.data(experiment)["statevector"], decimals=decimals\n ^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 187, in data\n return self._get_experiment(experiment).data.to_dict()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 380, in _get_experiment\n raise QiskitError(\'Data for experiment "%s" could not be found.\' % key)\nqiskit.exceptions.QiskitError: \'Data for experiment "circ0" could not be found.\'\n'
[justsing@g4 CausalTensornet]$ ./pennylane_qiskit_run.sh
Simulation failed and returned the following error message:
ERROR: Failed to load circuits: Duplicate key "_method_" in save instruction.
Simulation failed and returned the following error message:
ERROR: Failed to load circuits: Duplicate key "_method_" in save instruction.
Simulation failed and returned the following error message:
ERROR: Failed to load circuits: Duplicate key "_method_" in save instruction.
Traceback (most recent call last):
File "pennylane_qiskit_toy_example.py", line 263, in <module>
fake_data_list = nn.parallel.parallel_apply(gen_clones, noise_inputs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/parallel/parallel_apply.py", line 108, in parallel_apply
output.reraise()
File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/_utils.py", line 705, in reraise
raise exception
qiskit.exceptions.QiskitError: 'Caught QiskitError in replica 1 on device 0.\nOriginal Traceback (most recent call last):\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/parallel/parallel_apply.py", line 83, in _worker\n output = module(*input, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1532, in _wrapped_call_impl\n return self._call_impl(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/nn/modules/module.py", line 1541, in _call_impl\n return forward_call(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/slate/justsing/CausalTensornet/pennylane_qiskit_toy_example.py", line 207, in forward\n probs = self.node(elem, params)\n ^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1164, in __call__\n return self._impl_call(*args, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1150, in _impl_call\n res = self._execution_component(args, kwargs, override_shots=override_shots)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/qnode.py", line 1103, in _execution_component\n res = qml.execute(\n ^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/execution.py", line 835, in execute\n results = ml_boundary_execute(tapes, execute_fn, jpc, device=device)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 236, in execute\n return ExecuteTapes.apply(kwargs, *parameters)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 89, in new_apply\n flat_out = orig_apply(out_struct_holder, *inp)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/torch/autograd/function.py", line 598, in apply\n return super().apply(*args, **kwargs) # type: ignore[misc]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 93, in new_forward\n out = orig_fw(ctx, *inp)\n ^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/interfaces/torch.py", line 158, in forward\n res = tuple(kwargs["execute_fn"](ctx.tapes))\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane/workflow/execution.py", line 316, in inner_execute\n results = device_execution(transformed_tapes)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/soft/rhel8/deeplearning/Python-3.11.5/lib/python3.11/contextlib.py", line 81, in inner\n return func(*args, **kwds)\n ^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane_qiskit/qiskit_device.py", line 519, in batch_execute\n self._state = self._get_state(result, experiment=circuit_obj)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/pennylane_qiskit/qiskit_device.py", line 416, in _get_state\n state = np.asarray(result.get_statevector(experiment))\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 314, in get_statevector\n self.data(experiment)["statevector"], decimals=decimals\n ^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 187, in data\n return self._get_experiment(experiment).data.to_dict()\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/N/u/justsing/Quartz/.local/lib/python3.11/site-packages/qiskit/result/result.py", line 380, in _get_experiment\n raise QiskitError(\'Data for experiment "%s" could not be found.\' % key)\nqiskit.exceptions.QiskitError: \'Data for experiment "circ0" could not be found.\'\n'
I am wondering if this issue has to do with a compatability issue between the pennylane-qiskit plugin and the AerSimulator
backend, because I noticed that the file aer.py
was last updated five months ago, which I think was before the major Qiskit overhaul that happened this year, while the Qiskit Aer backend itself was updated last month. My technical specs are below. Any help that you can provide would be greatly appreciated.
Name: PennyLane
Version: 0.37.0
Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Home-page: https://github.com/PennyLaneAI/pennylane
Author:
Author-email:
License: Apache License 2.0
Location: /N/u/justsing/Quartz/.local/lib/python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane-Cirq, Pennylane-Pyquest, PennyLane-qiskit, pennylane-qulacs, PennyLane-Rigetti, PennyLane_Lightning, PennyLane_Lightning_GPU
Platform info: Linux-4.18.0-553.8.1.el8_10.x86_64-x86_64-with-glibc2.28
Python version: 3.11.5
Numpy version: 1.26.4
Scipy version: 1.14.0
Installed devices:
- qiskit.aer (PennyLane-qiskit-0.37.0)
- qiskit.basicaer (PennyLane-qiskit-0.37.0)
- qiskit.basicsim (PennyLane-qiskit-0.37.0)
- qiskit.ibmq (PennyLane-qiskit-0.37.0)
- qiskit.ibmq.circuit_runner (PennyLane-qiskit-0.37.0)
- qiskit.ibmq.sampler (PennyLane-qiskit-0.37.0)
- qiskit.remote (PennyLane-qiskit-0.37.0)
- qulacs.simulator (pennylane-qulacs-0.36.0)
- lightning.gpu (PennyLane_Lightning_GPU-0.36.0)
- lightning.qubit (PennyLane_Lightning-0.37.0)
- cirq.mixedsimulator (PennyLane-Cirq-0.36.0)
- cirq.pasqal (PennyLane-Cirq-0.36.0)
- cirq.qsim (PennyLane-Cirq-0.36.0)
- cirq.qsimh (PennyLane-Cirq-0.36.0)
- cirq.simulator (PennyLane-Cirq-0.36.0)
- default.clifford (PennyLane-0.37.0)
- default.gaussian (PennyLane-0.37.0)
- default.mixed (PennyLane-0.37.0)
- default.qubit (PennyLane-0.37.0)
- default.qubit.autograd (PennyLane-0.37.0)
- default.qubit.jax (PennyLane-0.37.0)
- default.qubit.legacy (PennyLane-0.37.0)
- default.qubit.tf (PennyLane-0.37.0)
- default.qubit.torch (PennyLane-0.37.0)
- default.qutrit (PennyLane-0.37.0)
- default.qutrit.mixed (PennyLane-0.37.0)
- default.tensor (PennyLane-0.37.0)
- null.qubit (PennyLane-0.37.0)
- rigetti.numpy_wavefunction (PennyLane-Rigetti-0.36.0)
- rigetti.qpu (PennyLane-Rigetti-0.36.0)
- rigetti.qvm (PennyLane-Rigetti-0.36.0)
- rigetti.wavefunction (PennyLane-Rigetti-0.36.0)
- pyquest.mixed (Pennylane-Pyquest-0.1.0)
- pyquest.pure (Pennylane-Pyquest-0.1.0)