Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unitary hack #281

Merged
merged 63 commits into from
Jul 17, 2024
Merged
Changes from 8 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
e18afe4
chore: remove unnecessary import
king-p3nguin May 29, 2024
36e3f5e
fix: fix dep warns
king-p3nguin May 29, 2024
33d0e94
fix: fix more depwarns and remove job_monitor
king-p3nguin May 29, 2024
20c2cee
fix: bump up qiskit version
king-p3nguin May 29, 2024
1d56d0b
fix, lint: fix type annotation error for py38, fix lint
king-p3nguin May 29, 2024
30eab91
fix: fix cnot error
king-p3nguin May 29, 2024
0932418
fix: fix examples
king-p3nguin May 29, 2024
c0e7a8a
ci: update workflow
king-p3nguin May 29, 2024
66205f5
test: relax assertion threshold
king-p3nguin May 29, 2024
0167897
Merge branch 'dev' into bump-up-qiskit-version
01110011011101010110010001101111 May 29, 2024
9d544ca
Create QGAN.Py
AbdullahKazi500 May 31, 2024
b0d8446
Added QCBM algorithm with example
Gopal-Dahale May 31, 2024
428be4a
Remove unused imports
Gopal-Dahale May 31, 2024
4bd170c
Updated init py following best practices
Gopal-Dahale May 31, 2024
0b267cf
Add files via upload
AbdullahKazi500 Jun 1, 2024
3a04848
rm density matrix for now
01110011011101010110010001101111 Jun 2, 2024
92d3384
Merge branch 'unitary-hack' into bump-up-qiskit-version
01110011011101010110010001101111 Jun 2, 2024
b7c7f3c
Updated with argparse
Gopal-Dahale Jun 3, 2024
792b766
bump ibm runtime
01110011011101010110010001101111 Jun 5, 2024
b4e6b67
bump qiskit aer
01110011011101010110010001101111 Jun 5, 2024
191934d
[fix] revive paramnum
01110011011101010110010001101111 Jun 5, 2024
b4b2748
change: remove unnessesary cloning
king-p3nguin Jun 6, 2024
dc41acc
Added qcbm gaussian mixture notebook
Gopal-Dahale Jun 6, 2024
134b3c6
support parameter expression in qiskit2tq
king-p3nguin Jun 6, 2024
2ada759
fix tab
Gopal-Dahale Jun 6, 2024
d5ebf7a
fix tab
Gopal-Dahale Jun 6, 2024
f101036
fix spacing
Gopal-Dahale Jun 6, 2024
98b4f36
fix tab
Gopal-Dahale Jun 6, 2024
11e5029
fix tab
Gopal-Dahale Jun 6, 2024
15605d0
Update torchquantum/operator/standard_gates/qubit_unitary.py
king-p3nguin Jun 6, 2024
75955c6
black formatted
Gopal-Dahale Jun 6, 2024
86217e6
added QGan notebook
AbdullahKazi500 Jun 6, 2024
c4ab183
test: add test for qiskit2tq
king-p3nguin Jun 7, 2024
ee6834a
change: print
king-p3nguin Jun 7, 2024
120fc2a
change: remove comments
king-p3nguin Jun 10, 2024
16232df
Create QGan.py
AbdullahKazi500 Jun 11, 2024
7695fd7
Delete examples/Newfolder/QuantumGAN/README.md directory
AbdullahKazi500 Jun 11, 2024
363751b
Create QGan.py
AbdullahKazi500 Jun 11, 2024
aeb213d
Create Readme.md
AbdullahKazi500 Jun 11, 2024
ee7b5f7
Add files via upload
AbdullahKazi500 Jun 12, 2024
37d389b
Update Readme.md
AbdullahKazi500 Jun 12, 2024
945d47c
Add files via upload
AbdullahKazi500 Jun 12, 2024
ad384fa
Merge pull request #267 from king-p3nguin/bump-up-qiskit-version
Hanrui-Wang Jun 12, 2024
60ace1e
Merge pull request #271 from Gopal-Dahale/qcbm
Hanrui-Wang Jun 12, 2024
99b5a18
Merge branch 'unitary-hack' into qiskit2tq-parameterexpression
01110011011101010110010001101111 Jun 12, 2024
2ff1d61
Delete qgan_notebook.ipynb
AbdullahKazi500 Jun 12, 2024
016f598
Delete QGAN.Py
AbdullahKazi500 Jun 12, 2024
64919c2
fix: fix test
king-p3nguin Jun 12, 2024
968c21b
Create quantum_pulse_simulation.py
AbdullahKazi500 Jun 12, 2024
cd2f3d4
fix: fix type annotations
king-p3nguin Jun 12, 2024
0f0de3f
Delete torchQuantumpulse.ipynb
AbdullahKazi500 Jun 12, 2024
6f84504
Rename QGANtorch (2).ipynb to qgan_generated.ipynb
AbdullahKazi500 Jun 12, 2024
f8d2965
Rename QGANPng.png to qgan_generated.png
AbdullahKazi500 Jun 12, 2024
aa1869c
Rename QGANPng2.png to qgan_image.png
AbdullahKazi500 Jun 12, 2024
5fdbf39
Update QGan.py
AbdullahKazi500 Jun 12, 2024
9701b11
Rename Readme.md to README.md
AbdullahKazi500 Jun 12, 2024
79cb93c
Update README.md
AbdullahKazi500 Jun 12, 2024
c940a1e
Update README.md
AbdullahKazi500 Jun 12, 2024
d433bbe
Rename qgan_image.png to qgan_latent_dim.png
AbdullahKazi500 Jun 12, 2024
82cf184
Update quantum_pulse_simulation.py
AbdullahKazi500 Jun 12, 2024
91c80de
Merge pull request #275 from king-p3nguin/qiskit2tq-parameterexpression
01110011011101010110010001101111 Jun 13, 2024
eda17c1
Merge pull request #272 from AbdullahKazi500/AbdullahKazi500-patch-3
01110011011101010110010001101111 Jun 13, 2024
6b30997
Merge pull request #270 from AbdullahKazi500/AbdullahKazi500-patch-2
01110011011101010110010001101111 Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions test/plugin/test_qiskit2tq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""
MIT License

Copyright (c) 2020-present TorchQuantum Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import random

import numpy as np
import pytest
import torch
import torch.optim as optim
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter, ParameterVector
from torch.optim.lr_scheduler import CosineAnnealingLR

import torchquantum as tq
from torchquantum.plugin import qiskit2tq

seed = 42
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)


class TQModel(tq.QuantumModule):
def __init__(self, init_params=None):
super().__init__()
self.n_wires = 2
self.rx = tq.RX(has_params=True, trainable=True, init_params=[init_params[0]])
self.u3_0 = tq.U3(has_params=True, trainable=True, init_params=init_params[1:4])
self.u3_1 = tq.U3(
has_params=True,
trainable=True,
init_params=torch.tensor(
[
init_params[4] + init_params[2],
init_params[5] * init_params[3],
init_params[6] * init_params[1],
]
),
)
self.cu3_0 = tq.CU3(
has_params=True,
trainable=True,
init_params=torch.tensor(
[
torch.sin(init_params[7]),
torch.abs(torch.sin(init_params[8])),
torch.abs(torch.sin(init_params[9]))
* torch.exp(init_params[2] + init_params[3]),
]
),
)

def forward(self, q_device: tq.QuantumDevice):
q_device.reset_states(1)
self.rx(q_device, wires=0)
self.u3_0(q_device, wires=0)
self.u3_1(q_device, wires=1)
self.cu3_0(q_device, wires=[0, 1])


def get_qiskit_ansatz():
ansatz = QuantumCircuit(2)
ansatz_param = Parameter("Θ") # parameter
ansatz.rx(ansatz_param, 0)
ansatz_param_vector = ParameterVector("φ", 9) # parameter vector
ansatz.u(ansatz_param_vector[0], ansatz_param_vector[1], ansatz_param_vector[2], 0)
ansatz.u(
ansatz_param_vector[3] + ansatz_param_vector[1], # parameter expression
ansatz_param_vector[4] * ansatz_param_vector[2],
ansatz_param_vector[5] / ansatz_param_vector[0],
1,
)
ansatz.cu(
np.sin(ansatz_param_vector[6]), # numpy functions
np.abs(np.sin(ansatz_param_vector[7])), # nested numpy functions
# complex expression
np.abs(np.sin(ansatz_param_vector[8]))
* np.exp(ansatz_param_vector[1] + ansatz_param_vector[2]),
0.0,
0,
1,
)
return ansatz


def train_step(target_state, device, model, optimizer):
model(device)
result_state = device.get_states_1d()[0]

# compute the state infidelity
loss = 1 - torch.dot(result_state, target_state).abs() ** 2

optimizer.zero_grad()
loss.backward()
optimizer.step()

infidelity = loss.item()
target_state_vector = target_state.detach().cpu().numpy()
result_state_vector = result_state.detach().cpu().numpy()
print(
f"infidelity (loss): {infidelity}, \n target state : "
f"{target_state_vector}, \n "
f"result state : {result_state_vector}\n"
)
return infidelity, target_state_vector, result_state_vector


def train(init_params, backend):
device = torch.device("cpu")

if backend == "qiskit":
ansatz = get_qiskit_ansatz()
model = qiskit2tq(ansatz, initial_parameters=init_params).to(device)
elif backend == "torchquantum":
model = TQModel(init_params).to(device)

print(f"{backend} model:", model)

n_epochs = 10
optimizer = optim.Adam(model.parameters(), lr=1e-2, weight_decay=0)
scheduler = CosineAnnealingLR(optimizer, T_max=n_epochs)

q_device = tq.QuantumDevice(n_wires=2)
target_state = torch.tensor([0, 1, 0, 0], dtype=torch.complex64)

result_list = []
for epoch in range(1, n_epochs + 1):
print(f"Epoch {epoch}, LR: {optimizer.param_groups[0]['lr']}")
result_list.append(train_step(target_state, q_device, model, optimizer))
scheduler.step()

return result_list


@pytest.mark.parametrize(
"init_params",
[
torch.nn.init.uniform_(torch.ones(10), -np.pi, np.pi),
torch.nn.init.uniform_(torch.ones(10), -np.pi, np.pi),
torch.nn.init.uniform_(torch.ones(10), -np.pi, np.pi),
],
)
def test_qiskit2tq(init_params):
qiskit_result = train(init_params, "qiskit")
tq_result = train(init_params, "torchquantum")
for qi_tensor, tq_tensor in zip(qiskit_result, tq_result):
torch.testing.assert_close(qi_tensor[0], tq_tensor[0])
torch.testing.assert_close(qi_tensor[1], tq_tensor[1])
torch.testing.assert_close(qi_tensor[2], tq_tensor[2])


if __name__ == "__main__":
test_qiskit2tq(torch.nn.init.uniform_(torch.ones(10), -np.pi, np.pi))
14 changes: 7 additions & 7 deletions torchquantum/layer/layers/module_from_ops.py
Original file line number Diff line number Diff line change
@@ -22,16 +22,16 @@
SOFTWARE.
"""

from typing import Iterable

import numpy as np
import torch
import torch.nn as nn
from torchpack.utils.logging import logger

import torchquantum as tq
import torchquantum.functional as tqf
import numpy as np


from typing import Iterable
from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES
from torchpack.utils.logging import logger

__all__ = [
"QuantumModuleFromOps",
@@ -61,6 +61,6 @@ def forward(self, q_device: tq.QuantumDevice):
None

"""
self.q_device = q_device
q_device.reset_states(1)
for op in self.ops:
op(q_device)
op(q_device, wires=op.wires)
75 changes: 67 additions & 8 deletions torchquantum/plugin/qiskit/qiskit_plugin.py
Original file line number Diff line number Diff line change
@@ -21,15 +21,20 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
from __future__ import annotations

from typing import Iterable
from typing import Iterable, Optional

import numpy as np
import qiskit
import qiskit.circuit.library.standard_gates as qiskit_gate
import symengine
import sympy
import torch
from qiskit import ClassicalRegister, QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit import CircuitInstruction, Parameter
from qiskit.circuit.parameter import ParameterExpression
from qiskit.circuit.parametervector import ParameterVectorElement
from qiskit_aer import AerSimulator
from torchpack.utils.logging import logger

@@ -691,7 +696,7 @@ def op_history2qiskit_expand_params(n_wires, op_history, bsz):

# construct a tq QuantumModule object according to the qiskit QuantumCircuit
# object
def qiskit2tq_Operator(circ: QuantumCircuit):
def qiskit2tq_Operator(circ: QuantumCircuit, initial_parameters=None):
if getattr(circ, "_layout", None) is not None:
try:
p2v_orig = circ._layout.final_layout.get_physical_bits().copy()
@@ -711,14 +716,23 @@ def qiskit2tq_Operator(circ: QuantumCircuit):
for p in range(circ.num_qubits):
p2v[p] = p

if initial_parameters is None:
initial_parameters = torch.nn.init.uniform_(
torch.ones(len(circ.parameters)), -np.pi, np.pi
)

param_to_index = {}
for i, param in enumerate(circ.parameters):
param_to_index[param] = i

ops = []
for gate in circ.data:
op_name = gate[0].name
wires = [circ.find_bit(qb).index for qb in gate.qubits]
wires = [p2v[wire] for wire in wires]
# sometimes the gate.params is ParameterExpression class
init_params = (
list(map(float, gate[0].params)) if len(gate[0].params) > 0 else None

init_params = qiskit2tq_translate_qiskit_params(
gate, initial_parameters, param_to_index
)

if op_name in [
@@ -780,8 +794,53 @@ def qiskit2tq_Operator(circ: QuantumCircuit):
return ops


def qiskit2tq(circ: QuantumCircuit):
ops = qiskit2tq_Operator(circ)
def qiskit2tq_translate_qiskit_params(
circuit_instruction: CircuitInstruction, initial_parameters, param_to_index
):
parameters = []
for p in circuit_instruction.operation.params:
if isinstance(p, Parameter) or isinstance(p, ParameterVectorElement):
parameters.append(initial_parameters[param_to_index[p]])
elif isinstance(p, ParameterExpression):
if len(p.parameters) == 0:
parameters.append(float(p))
continue

expr = p.sympify().simplify()
if isinstance(expr, symengine.Expr): # qiskit uses symengine if available
expr = expr._sympy_() # sympy.Expr

for free_symbol in expr.free_symbols:
# replace names: theta[0] -> theta_0
# ParameterVector creates symbols with brackets like theta[0]
# but sympy.lambdify does not allow brackets in symbol names
free_symbol.name = free_symbol.name.replace("[", "_").replace("]", "")

parameter_list = list(p.parameters)
sympy_symbols = [param._symbol_expr for param in parameter_list]
# replace names again: theta[0] -> theta_0
sympy_symbols = [
sympy.Symbol(str(symbol).replace("[", "_").replace("]", ""))
for symbol in sympy_symbols
]
lam_f = sympy.lambdify(sympy_symbols, expr, modules="math")
parameters.append(
lam_f(
*[
initial_parameters[param_to_index[param]]
for param in parameter_list
]
)
)
else: # non-parameterized gate
parameters.append(p)
return parameters


def qiskit2tq(
circ: QuantumCircuit, initial_parameters: Optional[list[torch.nn.Parameter]] = None
):
ops = qiskit2tq_Operator(circ, initial_parameters)
return tq.QuantumModuleFromOps(ops)