Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,5 @@ es_runs/
.vscode-upload.json

mnist_data/
.cursor/
.github/
905 changes: 455 additions & 450 deletions examples/ICCAD22_tutorial/sec1_basic.ipynb

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions examples/cuquantum/h2_new.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
h2 bk 2
0.1790005760614067 X0 X1
-1.0439125217729273 I0 I1
-0.42045567978280385 Z0 I1
0.42045567978280385 I0 Z1
-0.011507402176827025 Z0 Z1
122 changes: 122 additions & 0 deletions examples/cuquantum/qaoa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: MIT

import math
import argparse

import torch
from torch import nn
from torchquantum.plugin.cuquantum import *
from torchquantum.operator.standard_gates import *



class MAXCUT(nn.Module):
def __init__(self, n_wires, input_graph, n_layers):
super().__init__()
self.n_wires = n_wires
self.input_graph = input_graph
self.n_layers = n_layers

self.circuit = ParameterizedQuantumCircuit(n_wires=n_wires, n_input_params=0, n_trainable_params=2 * n_layers)
self.circuit.set_trainable_params(torch.randn(2 * n_layers))

for wire in range(self.n_wires):
self.circuit.append_gate(Hadamard, wires=wire)

for l in range(self.n_layers):
# mixer layer
for i in range(self.n_wires):
self.circuit.append_gate(RX, wires=i, trainable_idx=l)

# entangler layer
for edge in self.input_graph:
self.circuit.append_gate(CNOT, wires=[edge[0], edge[1]])
self.circuit.append_gate(RZ, wires=edge[1], trainable_idx=n_layers + l)
self.circuit.append_gate(CNOT, wires=[edge[0], edge[1]])


hamiltonian = {}
for edge in self.input_graph:
pauli_string = ""
for wire in range(self.n_wires):
if wire in edge:
pauli_string += "Z"
else:
pauli_string += "I"
hamiltonian[pauli_string] = 0.5

backend = CuTensorNetworkBackend(TNConfig(num_hyper_samples=10))
self.energy = QuantumExpectation(self.circuit, backend, [hamiltonian])
self.sampling = QuantumSampling(self.circuit, backend, 100)

def forward(self):
start_time = torch.cuda.Event(enable_timing=True)
end_time = torch.cuda.Event(enable_timing=True)

start_time.record()
output = self.energy() - len(self.input_graph) / 2
end_time.record()

torch.cuda.synchronize()
elapsed_time = start_time.elapsed_time(end_time)
print(f"Forward pass took {elapsed_time:.2f} ms")

return output


def optimize(model, n_steps=100, lr=0.1):
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
print(f"The initial parameters are:\n{next(model.parameters()).data.tolist()}")
print("")
for step in range(n_steps):
optimizer.zero_grad()
loss = model()
start_time = torch.cuda.Event(enable_timing=True)
end_time = torch.cuda.Event(enable_timing=True)

start_time.record()
loss.backward()
end_time.record()

torch.cuda.synchronize()
elapsed_time = start_time.elapsed_time(end_time)
print(f"Backward pass took {elapsed_time:.2f} ms")

optimizer.step()

print(f"Step: {step}, Cost Objective: {loss.item()}")

print("")
print(f"The optimal parameters are:\n{next(model.parameters()).data.tolist()}")
print("")

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--n_wires", type=int, default=4, help="number of wires")
parser.add_argument("--n_layers", type=int, default=4, help="number of layers")
parser.add_argument("--steps", type=int, default=100, help="number of steps")
parser.add_argument("--lr", type=float, default=0.01, help="learning rate")
parser.add_argument("--seed", type=int, default=0, help="random seed")
args = parser.parse_args()

torch.manual_seed(args.seed)

# create a fully connected graph
input_graph = []
for i in range(args.n_wires):
for j in range(i):
input_graph.append((i, j))

print(f"Cost Objective Minimum (Analytic Reference Result): {math.floor(args.n_wires**2 // 4)}")

model = MAXCUT(n_wires=args.n_wires, input_graph=input_graph, n_layers=args.n_layers)
optimize(model, n_steps=args.steps, lr=args.lr)
samples = model.sampling()

print(f"Sampling Results: {samples}")


if __name__ == "__main__":
main()
1,815 changes: 1,815 additions & 0 deletions examples/cuquantum/sec1.ipynb

Large diffs are not rendered by default.

44 changes: 25 additions & 19 deletions torchquantum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,36 @@
from .layer import *
from .encoding import *
from .util import *
from .noise_model import *
# Note: noise_model requires qiskit and is not imported by default
# from .noise_model import *
from .algorithm import *
from .dataset import *
from .pulse import *

# here we check whether the Qiskit parameterization bug is fixed, if not, a
# warning message will be printed
import qiskit
import os
# This is only done if qiskit is available
try:
import qiskit
import os

path = os.path.abspath(qiskit.__file__)
# print(path)
# path for aer provider
path_provider = path.replace("__init__.py", "providers/aer/backends/aerbackend.py")
# print(path_provider)
path = os.path.abspath(qiskit.__file__)
# print(path)
# path for aer provider
path_provider = path.replace("__init__.py", "providers/aer/backends/aerbackend.py")
# print(path_provider)

# with open(path_provider, 'r') as fid:
# for line in fid.readlines():
# if 'FIXED' in line:
# # print('The qiskit parameterization bug is already fixed!')
# break
# else:
# print(f'\n\n WARNING: The qiskit parameterization bug is not '
# f'fixed!\n\n'
# f'run python fix_qiskit_parameterization.py to fix it!'
# )
# break
# with open(path_provider, 'r') as fid:
# for line in fid.readlines():
# if 'FIXED' in line:
# # print('The qiskit parameterization bug is already fixed!')
# break
# else:
# print(f'\n\n WARNING: The qiskit parameterization bug is not '
# f'fixed!\n\n'
# f'run python fix_qiskit_parameterization.py to fix it!'
# )
# break
except ImportError:
# qiskit not available, skip the check
pass
2 changes: 1 addition & 1 deletion torchquantum/layer/entanglement/entanglement.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from .op2_layer import Op2QAllLayer

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

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/entanglement/op2_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


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

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/cx_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

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


Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

from typing import Iterable
from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES
from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES
from torchpack.utils.logging import logger
from torchquantum.layer.entanglement.op2_layer import Op2QAllLayer

Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/module_from_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


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

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/op_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


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

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/qft_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np

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

class QFTLayer(tq.QuantumModule):
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/random_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

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

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/ry_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np

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

from .layers import LayerTemplate0
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/seth_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

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

from .layers import LayerTemplate0, Op1QAllLayer
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/swap_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

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

from .layers import LayerTemplate0
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/layer/layers/u3_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import numpy as np

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

from .layers import LayerTemplate0, Op1QAllLayer
Expand Down
2 changes: 1 addition & 1 deletion torchquantum/noise_model/noise_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

from torchpack.utils.logging import logger
from qiskit.providers.aer.noise import NoiseModel
from torchquantum.util import get_provider
from torchquantum.util.qiskit_utils import get_provider


__all__ = [
Expand Down
19 changes: 17 additions & 2 deletions torchquantum/operator/op_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,28 @@ def forward(
else:
self.func(q_device, self.wires, n_wires=self.n_wires, inverse=self.inverse) # type: ignore
else:
if isinstance(self.noise_model_tq, tq.NoiseModelTQPhase):
# Avoid hard dependency on tq.noise_model export; fall back gracefully
try:
is_phase_noise = isinstance(
self.noise_model_tq, tq.noise_model.NoiseModelTQPhase
)
except AttributeError:
# tq.noise_model not exported; treat as no phase noise
is_phase_noise = False

if is_phase_noise:
params = self.noise_model_tq.add_noise(self.params)
else:
params = self.params

if self.clifford_quantization:
params = CliffordQuantizer.quantize_sse(params)
try:
# Local import to avoid circular dependency and undefined name at import time
from torchquantum.util.quantization import CliffordQuantizer
params = CliffordQuantizer.quantize_sse(params)
except Exception:
# If quantizer is unavailable, skip quantization
pass
if self.n_wires is None:
self.func(q_device, self.wires, params=params, inverse=self.inverse)
else:
Expand Down
7 changes: 6 additions & 1 deletion torchquantum/plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@
SOFTWARE.
"""

from .qiskit import *
# Only import qiskit plugin if qiskit is available
try:
from .qiskit import *
except ImportError:
# qiskit not available, skip importing qiskit plugin
pass
9 changes: 9 additions & 0 deletions torchquantum/plugin/cuquantum/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES

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.
19 changes: 19 additions & 0 deletions torchquantum/plugin/cuquantum/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: MIT

from .circuit import ParameterizedQuantumCircuit
from .cutn import CuTensorNetworkBackend, TNConfig, MPSConfig
from .expectation import QuantumExpectation
from .sampling import QuantumSampling
from .amplitude import QuantumAmplitude

__all__ = [
"ParameterizedQuantumCircuit",
"CuTensorNetworkBackend",
"TNConfig",
"MPSConfig",
"QuantumExpectation",
"QuantumSampling",
"QuantumAmplitude",
]
Loading
Loading