Skip to content

Commit 8555b83

Browse files
01110011011101010110010001101111king-p3nguinAbdullahKazi500Gopal-DahaleHanrui-Wang
authored
Unitary hack (#281)
* 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 <kazukitsuoka@g.ecc.u-tokyo.ac.jp> * bump qiskit aer Co-authored-by: Kazuki Tsuoka <kazukitsuoka@g.ecc.u-tokyo.ac.jp> * [fix] revive paramnum * change: remove unnessesary cloning * Added qcbm gaussian mixture notebook * support parameter expression in qiskit2tq * fix tab Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * fix tab Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * fix spacing Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * fix tab Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * fix tab Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * Update torchquantum/operator/standard_gates/qubit_unitary.py Co-authored-by: GenericP3rson <41024739+GenericP3rson@users.noreply.github.com> * 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 <kazukitsuoka@g.ecc.u-tokyo.ac.jp> Co-authored-by: Chanandellar Bong <75779966+AbdullahKazi500@users.noreply.github.com> Co-authored-by: Gopal Dahale <dahalegopal27@gmail.com> Co-authored-by: Gopal Ramesh Dahale <49199003+Gopal-Dahale@users.noreply.github.com> Co-authored-by: Hanrui Wang <hanrui@mit.edu>
1 parent c6f8e8b commit 8555b83

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2662
-1796
lines changed

.github/workflows/functional_tests.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
name: Python package
55

66
on:
7-
push:
7+
push:
88
pull_request:
99

1010
jobs:
@@ -17,16 +17,16 @@ jobs:
1717
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
1818

1919
steps:
20-
- uses: actions/checkout@v3
20+
- uses: actions/checkout@v4
2121
- name: Set up Python ${{ matrix.python-version }}
22-
uses: actions/setup-python@v3
22+
uses: actions/setup-python@v5
2323
with:
2424
python-version: ${{ matrix.python-version }}
2525
- name: Install dependencies
2626
run: |
2727
python -m pip install --upgrade pip
2828
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
29-
python -m pip install flake8 pytest qiskit-aer qiskit_ibm_runtime
29+
python -m pip install flake8 pytest
3030
- name: Lint with flake8
3131
run: |
3232
# stop the build if there are Python syntax errors or undefined names

.github/workflows/lint.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ jobs:
1414
lint:
1515
runs-on: ubuntu-latest
1616
steps:
17-
- uses: actions/checkout@v3
17+
- uses: actions/checkout@v4
1818
- name: Setup Python 3.8
19-
uses: actions/setup-python@v4
19+
uses: actions/setup-python@v5
2020
with:
2121
python-version: ${{ env.PYTHON_VERSION }}
2222
- name: Update pip and install lint utilities

.github/workflows/pull_request.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ jobs:
99
pre-commit:
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v3
13-
- uses: actions/setup-python@v4
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-python@v5
1414
with:
1515
python-version: ${{ env.PYTHON_VERSION }}
1616
- uses: pre-commit/action@v2.0.3

examples/QCBM/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Quantum Circuit Born Machine
2+
(Implementation by: [Gopal Ramesh Dahale](https://github.com/Gopal-Dahale))
3+
4+
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.
5+
6+
## Setup
7+
8+
Below is the usage of `qcbm_gaussian_mixture.py` which can be obtained by running `python qcbm_gaussian_mixture.py -h`.
9+
10+
```
11+
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]
12+
13+
options:
14+
-h, --help show this help message and exit
15+
--n_wires N_WIRES Number of wires used in the circuit
16+
--epochs EPOCHS Number of training epochs
17+
--n_blocks N_BLOCKS Number of blocks in ansatz
18+
--n_layers_per_block N_LAYERS_PER_BLOCK
19+
Number of layers per block in ansatz
20+
--plot Visualize the predicted probability distribution
21+
--optimizer OPTIMIZER
22+
optimizer class from torch.optim
23+
--lr LR
24+
```
25+
26+
For example:
27+
28+
```
29+
python qcbm_gaussian_mixture.py --plot --epochs 100 --optimizer RMSprop --lr 0.01 --n_blocks 6 --n_layers_per_block 2 --n_wires 6
30+
```
31+
32+
Using the command above gives an output similar to the plot below.
33+
34+
<p align="center">
35+
<img src ='./assets/sample_output.png' width-500 alt='sample output of QCBM'>
36+
</p>
37+
38+
39+
## References
40+
41+
1. Liu, Jin-Guo, and Lei Wang. “Differentiable learning of quantum circuit born machines.” Physical Review A 98.6 (2018): 062324.
42+
2. Gili, Kaitlin, et al. "Do quantum circuit born machines generalize?." Quantum Science and Technology 8.3 (2023): 035021.
32.6 KB
Loading

examples/QCBM/qcbm_gaussian_mixture.ipynb

+255
Large diffs are not rendered by default.
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
import torch
4+
from torchquantum.algorithm import QCBM, MMDLoss
5+
import torchquantum as tq
6+
import argparse
7+
import os
8+
from pprint import pprint
9+
10+
11+
# Reproducibility
12+
def set_seed(seed: int = 42) -> None:
13+
np.random.seed(seed)
14+
torch.manual_seed(seed)
15+
torch.cuda.manual_seed(seed)
16+
# When running on the CuDNN backend, two further options must be set
17+
torch.backends.cudnn.deterministic = True
18+
torch.backends.cudnn.benchmark = False
19+
# Set a fixed value for the hash seed
20+
os.environ["PYTHONHASHSEED"] = str(seed)
21+
print(f"Random seed set as {seed}")
22+
23+
24+
def _setup_parser():
25+
parser = argparse.ArgumentParser()
26+
parser.add_argument(
27+
"--n_wires", type=int, default=6, help="Number of wires used in the circuit"
28+
)
29+
parser.add_argument(
30+
"--epochs", type=int, default=10, help="Number of training epochs"
31+
)
32+
parser.add_argument(
33+
"--n_blocks", type=int, default=6, help="Number of blocks in ansatz"
34+
)
35+
parser.add_argument(
36+
"--n_layers_per_block",
37+
type=int,
38+
default=1,
39+
help="Number of layers per block in ansatz",
40+
)
41+
parser.add_argument(
42+
"--plot",
43+
action="store_true",
44+
help="Visualize the predicted probability distribution",
45+
)
46+
parser.add_argument(
47+
"--optimizer", type=str, default="Adam", help="optimizer class from torch.optim"
48+
)
49+
parser.add_argument("--lr", type=float, default=1e-2)
50+
return parser
51+
52+
53+
# Function to create a gaussian mixture
54+
def gaussian_mixture_pdf(x, mus, sigmas):
55+
mus, sigmas = np.array(mus), np.array(sigmas)
56+
vars = sigmas**2
57+
values = [
58+
(1 / np.sqrt(2 * np.pi * v)) * np.exp(-((x - m) ** 2) / (2 * v))
59+
for m, v in zip(mus, vars)
60+
]
61+
values = np.sum([val / sum(val) for val in values], axis=0)
62+
return values / np.sum(values)
63+
64+
65+
def main():
66+
set_seed()
67+
parser = _setup_parser()
68+
args = parser.parse_args()
69+
70+
print("Configuration:")
71+
pprint(vars(args))
72+
73+
# Create a gaussian mixture
74+
n_wires = args.n_wires
75+
assert n_wires >= 1, "Number of wires must be at least 1"
76+
77+
x_max = 2**n_wires
78+
x_input = np.arange(x_max)
79+
mus = [(2 / 8) * x_max, (5 / 8) * x_max]
80+
sigmas = [x_max / 10] * 2
81+
data = gaussian_mixture_pdf(x_input, mus, sigmas)
82+
83+
# This is the target distribution that the QCBM will learn
84+
target_probs = torch.tensor(data, dtype=torch.float32)
85+
86+
# Ansatz
87+
layers = tq.RXYZCXLayer0(
88+
{
89+
"n_blocks": args.n_blocks,
90+
"n_wires": n_wires,
91+
"n_layers_per_block": args.n_layers_per_block,
92+
}
93+
)
94+
95+
qcbm = QCBM(n_wires, layers)
96+
97+
# To train QCBMs, we use MMDLoss with radial basis function kernel.
98+
bandwidth = torch.tensor([0.25, 60])
99+
space = torch.arange(2**n_wires)
100+
mmd = MMDLoss(bandwidth, space)
101+
102+
# Optimization
103+
optimizer_class = getattr(torch.optim, args.optimizer)
104+
optimizer = optimizer_class(qcbm.parameters(), lr=args.lr)
105+
106+
for i in range(args.epochs):
107+
optimizer.zero_grad(set_to_none=True)
108+
pred_probs = qcbm()
109+
loss = mmd(pred_probs, target_probs)
110+
loss.backward()
111+
optimizer.step()
112+
print(i, loss.item())
113+
114+
# Visualize the results
115+
if args.plot:
116+
with torch.no_grad():
117+
pred_probs = qcbm()
118+
119+
plt.plot(x_input, target_probs, linestyle="-.", label=r"$\pi(x)$")
120+
plt.bar(x_input, pred_probs, color="green", alpha=0.5, label="samples")
121+
plt.xlabel("Samples")
122+
plt.ylabel("Prob. Distribution")
123+
124+
plt.legend()
125+
plt.show()
126+
127+
128+
if __name__ == "__main__":
129+
main()

examples/QuantumGan/ README.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Quantum Generative Adversarial Network (QGAN) Example
2+
3+
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.
4+
5+
## Overview
6+
7+
A QGAN consists of two main components:
8+
9+
1. **Generator:** This network generates fake quantum data samples.
10+
2. **Discriminator:** This network tries to distinguish between real and fake quantum data samples.
11+
12+
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.
13+
14+
## Repository Contents
15+
16+
- `qgan_notebook.ipynb`: Jupyter Notebook demonstrating the QGAN implementation.
17+
- `qgan_script.py`: Python script containing the QGAN model and a main function for initializing the model with command-line arguments.
18+
19+
## Installation
20+
21+
To run the examples, you need to have the following dependencies installed:
22+
23+
- Python 3
24+
- PyTorch
25+
- TorchQuantum
26+
- Jupyter Notebook
27+
- ipywidgets
28+
29+
You can install the required Python packages using pip:
30+
31+
```bash
32+
pip install torch torchquantum jupyter ipywidgets
33+
```
34+
35+
36+
Running the Examples
37+
Jupyter Notebook
38+
Open the qgan_notebook.ipynb file in Jupyter Notebook.
39+
Execute the notebook cells to see the QGAN model in action.
40+
Python Script
41+
You can also run the QGAN model using the Python script. The script uses argparse to handle command-line arguments.
42+
43+
bash
44+
Copy code
45+
python qgan_script.py <n_qubits> <latent_dim>
46+
Replace <n_qubits> and <latent_dim> with the desired number of qubits and latent dimensions.
47+
48+
Notebook Details
49+
The Jupyter Notebook is structured as follows:
50+
51+
Introduction: Provides an overview of the QGAN and its components.
52+
Import Libraries: Imports the necessary libraries, including PyTorch and TorchQuantum.
53+
Generator Class: Defines the quantum generator model.
54+
Discriminator Class: Defines the quantum discriminator model.
55+
QGAN Class: Combines the generator and discriminator into a single QGAN model.
56+
Main Function: Initializes the QGAN model and prints its structure.
57+
Interactive Model Creation: Uses ipywidgets to create an interactive interface for adjusting the number of qubits and latent dimensions.
58+
Understanding QGANs
59+
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:
60+
61+
The Generator creates fake quantum data samples from a latent space.
62+
The Discriminator attempts to distinguish these fake samples from real quantum data.
63+
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.
64+
65+
66+
## QGAN Implementation for CIFAR-10 Dataset
67+
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.
68+
Generated images can be seen in the folder
69+
70+
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.
71+
72+
73+
## Reference
74+
- [ ] https://arxiv.org/abs/2312.09939

examples/QuantumGan/QGan.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import argparse
2+
import torch
3+
import torch.nn as nn
4+
import torch.optim as optim
5+
import torchquantum as tq
6+
7+
class Generator(nn.Module):
8+
def __init__(self, n_qubits: int, latent_dim: int):
9+
super().__init__()
10+
self.n_qubits = n_qubits
11+
self.latent_dim = latent_dim
12+
13+
# Quantum encoder
14+
self.encoder = tq.GeneralEncoder([
15+
{'input_idx': [i], 'func': 'rx', 'wires': [i]}
16+
for i in range(self.n_qubits)
17+
])
18+
19+
# RX gates
20+
self.rxs = nn.ModuleList([
21+
tq.RX(has_params=True, trainable=True) for _ in range(self.n_qubits)
22+
])
23+
24+
def forward(self, x):
25+
qdev = tq.QuantumDevice(n_wires=self.n_qubits, bsz=x.shape[0], device=x.device)
26+
self.encoder(qdev, x)
27+
28+
for i in range(self.n_qubits):
29+
self.rxs[i](qdev, wires=i)
30+
31+
return tq.measure(qdev)
32+
33+
class Discriminator(nn.Module):
34+
def __init__(self, n_qubits: int):
35+
super().__init__()
36+
self.n_qubits = n_qubits
37+
38+
# Quantum encoder
39+
self.encoder = tq.GeneralEncoder([
40+
{'input_idx': [i], 'func': 'rx', 'wires': [i]}
41+
for i in range(self.n_qubits)
42+
])
43+
44+
# RX gates
45+
self.rxs = nn.ModuleList([
46+
tq.RX(has_params=True, trainable=True) for _ in range(self.n_qubits)
47+
])
48+
49+
# Quantum measurement
50+
self.measure = tq.MeasureAll(tq.PauliZ)
51+
52+
def forward(self, x):
53+
qdev = tq.QuantumDevice(n_wires=self.n_qubits, bsz=x.shape[0], device=x.device)
54+
self.encoder(qdev, x)
55+
56+
for i in range(self.n_qubits):
57+
self.rxs[i](qdev, wires=i)
58+
59+
return self.measure(qdev)
60+
61+
class QGAN(nn.Module):
62+
def __init__(self, n_qubits: int, latent_dim: int):
63+
super().__init__()
64+
self.generator = Generator(n_qubits, latent_dim)
65+
self.discriminator = Discriminator(n_qubits)
66+
67+
def forward(self, z):
68+
fake_data = self.generator(z)
69+
fake_output = self.discriminator(fake_data)
70+
return fake_output
71+
72+
def main(n_qubits, latent_dim):
73+
model = QGAN(n_qubits, latent_dim)
74+
print(model)
75+
76+
if __name__ == "__main__":
77+
parser = argparse.ArgumentParser(description="Quantum Generative Adversarial Network (QGAN) Example")
78+
parser.add_argument('n_qubits', type=int, help='Number of qubits')
79+
parser.add_argument('latent_dim', type=int, help='Dimension of the latent space')
80+
81+
args = parser.parse_args()
82+
83+
main(args.n_qubits, args.latent_dim)
84+

0 commit comments

Comments
 (0)