-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: remove unnecessary import * fix: fix dep warns * fix: fix more depwarns and remove job_monitor * fix: bump up qiskit version * fix, lint: fix type annotation error for py38, fix lint * fix: fix cnot error * fix: fix examples * ci: update workflow * test: relax assertion threshold * Create QGAN.Py * Added QCBM algorithm with example * Remove unused imports * Updated init py following best practices * Add files via upload * rm density matrix for now * Updated with argparse * bump ibm runtime Co-authored-by: Kazuki Tsuoka <[email protected]> * bump qiskit aer Co-authored-by: Kazuki Tsuoka <[email protected]> * [fix] revive paramnum * change: remove unnessesary cloning * Added qcbm gaussian mixture notebook * support parameter expression in qiskit2tq * fix tab Co-authored-by: GenericP3rson <[email protected]> * fix tab Co-authored-by: GenericP3rson <[email protected]> * fix spacing Co-authored-by: GenericP3rson <[email protected]> * fix tab Co-authored-by: GenericP3rson <[email protected]> * fix tab Co-authored-by: GenericP3rson <[email protected]> * Update torchquantum/operator/standard_gates/qubit_unitary.py Co-authored-by: GenericP3rson <[email protected]> * black formatted * added QGan notebook * test: add test for qiskit2tq * change: print * change: remove comments * Create QGan.py * Delete examples/Newfolder/QuantumGAN/README.md directory * Create QGan.py * Create Readme.md * Add files via upload * Update Readme.md * Add files via upload * Delete qgan_notebook.ipynb * Delete QGAN.Py * fix: fix test * Create quantum_pulse_simulation.py * fix: fix type annotations * Delete torchQuantumpulse.ipynb * Rename QGANtorch (2).ipynb to qgan_generated.ipynb * Rename QGANPng.png to qgan_generated.png * Rename QGANPng2.png to qgan_image.png * Update QGan.py * Rename Readme.md to README.md * Update README.md * Update README.md * Rename qgan_image.png to qgan_latent_dim.png * Update quantum_pulse_simulation.py --------- Co-authored-by: Kazuki Tsuoka <[email protected]> Co-authored-by: Chanandellar Bong <[email protected]> Co-authored-by: Gopal Dahale <[email protected]> Co-authored-by: Gopal Ramesh Dahale <[email protected]> Co-authored-by: Hanrui Wang <[email protected]>
- Loading branch information
1 parent
c6f8e8b
commit 8555b83
Showing
49 changed files
with
2,662 additions
and
1,796 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,8 +9,8 @@ jobs: | |
pre-commit: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-python@v4 | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-python@v5 | ||
with: | ||
python-version: ${{ env.PYTHON_VERSION }} | ||
- uses: pre-commit/[email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Quantum Circuit Born Machine | ||
(Implementation by: [Gopal Ramesh Dahale](https://github.com/Gopal-Dahale)) | ||
|
||
Quantum Circuit Born Machine (QCBM) [1] is a generative modeling algorithm which uses Born rule from quantum mechanics to sample from a quantum state $|\psi \rangle$ learned by training an ansatz $U(\theta)$ [1][2]. In this tutorial we show how `torchquantum` can be used to model a Gaussian mixture with QCBM. | ||
|
||
## Setup | ||
|
||
Below is the usage of `qcbm_gaussian_mixture.py` which can be obtained by running `python qcbm_gaussian_mixture.py -h`. | ||
|
||
``` | ||
usage: qcbm_gaussian_mixture.py [-h] [--n_wires N_WIRES] [--epochs EPOCHS] [--n_blocks N_BLOCKS] [--n_layers_per_block N_LAYERS_PER_BLOCK] [--plot] [--optimizer OPTIMIZER] [--lr LR] | ||
options: | ||
-h, --help show this help message and exit | ||
--n_wires N_WIRES Number of wires used in the circuit | ||
--epochs EPOCHS Number of training epochs | ||
--n_blocks N_BLOCKS Number of blocks in ansatz | ||
--n_layers_per_block N_LAYERS_PER_BLOCK | ||
Number of layers per block in ansatz | ||
--plot Visualize the predicted probability distribution | ||
--optimizer OPTIMIZER | ||
optimizer class from torch.optim | ||
--lr LR | ||
``` | ||
|
||
For example: | ||
|
||
``` | ||
python qcbm_gaussian_mixture.py --plot --epochs 100 --optimizer RMSprop --lr 0.01 --n_blocks 6 --n_layers_per_block 2 --n_wires 6 | ||
``` | ||
|
||
Using the command above gives an output similar to the plot below. | ||
|
||
<p align="center"> | ||
<img src ='./assets/sample_output.png' width-500 alt='sample output of QCBM'> | ||
</p> | ||
|
||
|
||
## References | ||
|
||
1. Liu, Jin-Guo, and Lei Wang. “Differentiable learning of quantum circuit born machines.” Physical Review A 98.6 (2018): 062324. | ||
2. Gili, Kaitlin, et al. "Do quantum circuit born machines generalize?." Quantum Science and Technology 8.3 (2023): 035021. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
import torch | ||
from torchquantum.algorithm import QCBM, MMDLoss | ||
import torchquantum as tq | ||
import argparse | ||
import os | ||
from pprint import pprint | ||
|
||
|
||
# Reproducibility | ||
def set_seed(seed: int = 42) -> None: | ||
np.random.seed(seed) | ||
torch.manual_seed(seed) | ||
torch.cuda.manual_seed(seed) | ||
# When running on the CuDNN backend, two further options must be set | ||
torch.backends.cudnn.deterministic = True | ||
torch.backends.cudnn.benchmark = False | ||
# Set a fixed value for the hash seed | ||
os.environ["PYTHONHASHSEED"] = str(seed) | ||
print(f"Random seed set as {seed}") | ||
|
||
|
||
def _setup_parser(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"--n_wires", type=int, default=6, help="Number of wires used in the circuit" | ||
) | ||
parser.add_argument( | ||
"--epochs", type=int, default=10, help="Number of training epochs" | ||
) | ||
parser.add_argument( | ||
"--n_blocks", type=int, default=6, help="Number of blocks in ansatz" | ||
) | ||
parser.add_argument( | ||
"--n_layers_per_block", | ||
type=int, | ||
default=1, | ||
help="Number of layers per block in ansatz", | ||
) | ||
parser.add_argument( | ||
"--plot", | ||
action="store_true", | ||
help="Visualize the predicted probability distribution", | ||
) | ||
parser.add_argument( | ||
"--optimizer", type=str, default="Adam", help="optimizer class from torch.optim" | ||
) | ||
parser.add_argument("--lr", type=float, default=1e-2) | ||
return parser | ||
|
||
|
||
# Function to create a gaussian mixture | ||
def gaussian_mixture_pdf(x, mus, sigmas): | ||
mus, sigmas = np.array(mus), np.array(sigmas) | ||
vars = sigmas**2 | ||
values = [ | ||
(1 / np.sqrt(2 * np.pi * v)) * np.exp(-((x - m) ** 2) / (2 * v)) | ||
for m, v in zip(mus, vars) | ||
] | ||
values = np.sum([val / sum(val) for val in values], axis=0) | ||
return values / np.sum(values) | ||
|
||
|
||
def main(): | ||
set_seed() | ||
parser = _setup_parser() | ||
args = parser.parse_args() | ||
|
||
print("Configuration:") | ||
pprint(vars(args)) | ||
|
||
# Create a gaussian mixture | ||
n_wires = args.n_wires | ||
assert n_wires >= 1, "Number of wires must be at least 1" | ||
|
||
x_max = 2**n_wires | ||
x_input = np.arange(x_max) | ||
mus = [(2 / 8) * x_max, (5 / 8) * x_max] | ||
sigmas = [x_max / 10] * 2 | ||
data = gaussian_mixture_pdf(x_input, mus, sigmas) | ||
|
||
# This is the target distribution that the QCBM will learn | ||
target_probs = torch.tensor(data, dtype=torch.float32) | ||
|
||
# Ansatz | ||
layers = tq.RXYZCXLayer0( | ||
{ | ||
"n_blocks": args.n_blocks, | ||
"n_wires": n_wires, | ||
"n_layers_per_block": args.n_layers_per_block, | ||
} | ||
) | ||
|
||
qcbm = QCBM(n_wires, layers) | ||
|
||
# To train QCBMs, we use MMDLoss with radial basis function kernel. | ||
bandwidth = torch.tensor([0.25, 60]) | ||
space = torch.arange(2**n_wires) | ||
mmd = MMDLoss(bandwidth, space) | ||
|
||
# Optimization | ||
optimizer_class = getattr(torch.optim, args.optimizer) | ||
optimizer = optimizer_class(qcbm.parameters(), lr=args.lr) | ||
|
||
for i in range(args.epochs): | ||
optimizer.zero_grad(set_to_none=True) | ||
pred_probs = qcbm() | ||
loss = mmd(pred_probs, target_probs) | ||
loss.backward() | ||
optimizer.step() | ||
print(i, loss.item()) | ||
|
||
# Visualize the results | ||
if args.plot: | ||
with torch.no_grad(): | ||
pred_probs = qcbm() | ||
|
||
plt.plot(x_input, target_probs, linestyle="-.", label=r"$\pi(x)$") | ||
plt.bar(x_input, pred_probs, color="green", alpha=0.5, label="samples") | ||
plt.xlabel("Samples") | ||
plt.ylabel("Prob. Distribution") | ||
|
||
plt.legend() | ||
plt.show() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Quantum Generative Adversarial Network (QGAN) Example | ||
|
||
This repository contains an example implementation of a Quantum Generative Adversarial Network (QGAN) using PyTorch and TorchQuantum. The example is provided in a Jupyter Notebook for interactive exploration. | ||
|
||
## Overview | ||
|
||
A QGAN consists of two main components: | ||
|
||
1. **Generator:** This network generates fake quantum data samples. | ||
2. **Discriminator:** This network tries to distinguish between real and fake quantum data samples. | ||
|
||
The goal is to train the generator to produce quantum data that is indistinguishable from real data, according to the discriminator. This is achieved through an adversarial training process, where the generator and discriminator are trained simultaneously in a competitive manner. | ||
|
||
## Repository Contents | ||
|
||
- `qgan_notebook.ipynb`: Jupyter Notebook demonstrating the QGAN implementation. | ||
- `qgan_script.py`: Python script containing the QGAN model and a main function for initializing the model with command-line arguments. | ||
|
||
## Installation | ||
|
||
To run the examples, you need to have the following dependencies installed: | ||
|
||
- Python 3 | ||
- PyTorch | ||
- TorchQuantum | ||
- Jupyter Notebook | ||
- ipywidgets | ||
|
||
You can install the required Python packages using pip: | ||
|
||
```bash | ||
pip install torch torchquantum jupyter ipywidgets | ||
``` | ||
|
||
|
||
Running the Examples | ||
Jupyter Notebook | ||
Open the qgan_notebook.ipynb file in Jupyter Notebook. | ||
Execute the notebook cells to see the QGAN model in action. | ||
Python Script | ||
You can also run the QGAN model using the Python script. The script uses argparse to handle command-line arguments. | ||
|
||
bash | ||
Copy code | ||
python qgan_script.py <n_qubits> <latent_dim> | ||
Replace <n_qubits> and <latent_dim> with the desired number of qubits and latent dimensions. | ||
|
||
Notebook Details | ||
The Jupyter Notebook is structured as follows: | ||
|
||
Introduction: Provides an overview of the QGAN and its components. | ||
Import Libraries: Imports the necessary libraries, including PyTorch and TorchQuantum. | ||
Generator Class: Defines the quantum generator model. | ||
Discriminator Class: Defines the quantum discriminator model. | ||
QGAN Class: Combines the generator and discriminator into a single QGAN model. | ||
Main Function: Initializes the QGAN model and prints its structure. | ||
Interactive Model Creation: Uses ipywidgets to create an interactive interface for adjusting the number of qubits and latent dimensions. | ||
Understanding QGANs | ||
QGANs are a type of Generative Adversarial Network (GAN) that operate in the quantum domain. They leverage quantum circuits to generate and evaluate data samples. The adversarial training process involves two competing networks: | ||
|
||
The Generator creates fake quantum data samples from a latent space. | ||
The Discriminator attempts to distinguish these fake samples from real quantum data. | ||
Through training, the generator improves its ability to create realistic quantum data, while the discriminator enhances its ability to identify fake data. This process results in a generator that can produce high-quality quantum data samples. | ||
|
||
|
||
## QGAN Implementation for CIFAR-10 Dataset | ||
This implementation trains a QGAN on the CIFAR-10 dataset to generate fake images. It follows a similar structure to the TorchQuantum QGAN, with the addition of data loading and processing specific to the CIFAR-10 dataset. | ||
Generated images can be seen in the folder | ||
|
||
This `README.md` file explains the purpose of the repository, the structure of the notebook, and how to run the examples, along with a brief overview of the QGAN concept for those unfamiliar with it. | ||
|
||
|
||
## Reference | ||
- [ ] https://arxiv.org/abs/2312.09939 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import argparse | ||
import torch | ||
import torch.nn as nn | ||
import torch.optim as optim | ||
import torchquantum as tq | ||
|
||
class Generator(nn.Module): | ||
def __init__(self, n_qubits: int, latent_dim: int): | ||
super().__init__() | ||
self.n_qubits = n_qubits | ||
self.latent_dim = latent_dim | ||
|
||
# Quantum encoder | ||
self.encoder = tq.GeneralEncoder([ | ||
{'input_idx': [i], 'func': 'rx', 'wires': [i]} | ||
for i in range(self.n_qubits) | ||
]) | ||
|
||
# RX gates | ||
self.rxs = nn.ModuleList([ | ||
tq.RX(has_params=True, trainable=True) for _ in range(self.n_qubits) | ||
]) | ||
|
||
def forward(self, x): | ||
qdev = tq.QuantumDevice(n_wires=self.n_qubits, bsz=x.shape[0], device=x.device) | ||
self.encoder(qdev, x) | ||
|
||
for i in range(self.n_qubits): | ||
self.rxs[i](qdev, wires=i) | ||
|
||
return tq.measure(qdev) | ||
|
||
class Discriminator(nn.Module): | ||
def __init__(self, n_qubits: int): | ||
super().__init__() | ||
self.n_qubits = n_qubits | ||
|
||
# Quantum encoder | ||
self.encoder = tq.GeneralEncoder([ | ||
{'input_idx': [i], 'func': 'rx', 'wires': [i]} | ||
for i in range(self.n_qubits) | ||
]) | ||
|
||
# RX gates | ||
self.rxs = nn.ModuleList([ | ||
tq.RX(has_params=True, trainable=True) for _ in range(self.n_qubits) | ||
]) | ||
|
||
# Quantum measurement | ||
self.measure = tq.MeasureAll(tq.PauliZ) | ||
|
||
def forward(self, x): | ||
qdev = tq.QuantumDevice(n_wires=self.n_qubits, bsz=x.shape[0], device=x.device) | ||
self.encoder(qdev, x) | ||
|
||
for i in range(self.n_qubits): | ||
self.rxs[i](qdev, wires=i) | ||
|
||
return self.measure(qdev) | ||
|
||
class QGAN(nn.Module): | ||
def __init__(self, n_qubits: int, latent_dim: int): | ||
super().__init__() | ||
self.generator = Generator(n_qubits, latent_dim) | ||
self.discriminator = Discriminator(n_qubits) | ||
|
||
def forward(self, z): | ||
fake_data = self.generator(z) | ||
fake_output = self.discriminator(fake_data) | ||
return fake_output | ||
|
||
def main(n_qubits, latent_dim): | ||
model = QGAN(n_qubits, latent_dim) | ||
print(model) | ||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser(description="Quantum Generative Adversarial Network (QGAN) Example") | ||
parser.add_argument('n_qubits', type=int, help='Number of qubits') | ||
parser.add_argument('latent_dim', type=int, help='Dimension of the latent space') | ||
|
||
args = parser.parse_args() | ||
|
||
main(args.n_qubits, args.latent_dim) | ||
|
Oops, something went wrong.