diff --git a/.github/workflows/functional_tests.yaml b/.github/workflows/functional_tests.yaml index 4be0f73d..cde7e8ba 100644 --- a/.github/workflows/functional_tests.yaml +++ b/.github/workflows/functional_tests.yaml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 @@ -36,3 +36,34 @@ jobs: - name: Test with pytest run: | python -m pytest -m "not skip" + - name: Install TorchQuantum + run: | + pip install --editable . + - name: Test Examples + run: | + python3 examples/qubit_rotation/qubit_rotation.py --epochs 1 + python3 examples/vqe/vqe.py --epochs 1 --steps_per_epoch 1 + python3 examples/train_unitary_prep/train_unitary_prep.py --epochs 1 + python3 examples/train_state_prep/train_state_prep.py --epochs 1 + python3 examples/superdense_coding/superdense_coding_torchquantum.py + python3 examples/regression/run_regression.py --epochs 1 + python3 examples/param_shift_onchip_training/param_shift.py + python3 examples/mnist/mnist_2qubit_4class.py --epochs 1 + python3 examples/hadamard_grad/circ.py + python3 examples/encoder_examples/encoder_8x2ry.py + python3 examples/converter_tq_qiskit/convert.py + python3 examples/amplitude_encoding_mnist/mnist_new.py --epochs 1 + python3 examples/amplitude_encoding_mnist/mnist_example.py --epochs 1 + python3 examples/PauliSumOp/pauli_sum_op.py + python3 examples/regression/new_run_regression.py --epochs 1 + python3 examples/quanvolution/quanvolution_trainable_quantum_layer.py --epochs 1 + python3 examples/grover/grover_example_sudoku.py + python3 examples/param_shift_onchip_training/param_shift.py + python3 examples/quanvolution/quanvolution.py --epochs 1 + python3 examples/quantum_lstm/qlstm.py --epochs 1 + python3 examples/qaoa/max_cut_backprop.py --steps 1 + python3 examples/optimal_control/optimal_control.py --epochs 1 + python3 examples/optimal_control/optimal_control_gaussian.py --epochs 1 + python3 examples/optimal_control/optimal_control_multi_qubit.py --epochs 1 + python3 examples/save_load_example/save_load.py + python3 examples/mnist/mnist.py --epochs 1 diff --git a/examples/grover/grover_example_sudoku.py b/examples/grover/grover_example_sudoku.py index 25761594..4969eea2 100644 --- a/examples/grover/grover_example_sudoku.py +++ b/examples/grover/grover_example_sudoku.py @@ -28,7 +28,7 @@ """ import torchquantum as tq -from torchquantum.algorithms import Grover +from torchquantum.algorithm import Grover # To simplify the process, we can compile this set of comparisons into a list of clauses for convenience. @@ -90,4 +90,4 @@ def XOR(input0, input1, output): print("b = ", key[1]) print("c = ", key[2]) print("d = ", key[3]) - print("") \ No newline at end of file + print("") diff --git a/examples/mnist/mnist.py b/examples/mnist/mnist.py index 23e5eafe..d8b32e25 100644 --- a/examples/mnist/mnist.py +++ b/examples/mnist/mnist.py @@ -179,6 +179,7 @@ def main(): "--static", action="store_true", help="compute with " "static mode" ) parser.add_argument("--pdb", action="store_true", help="debug with pdb") + parser.add_argument("--qiskit-simulation", action="store_true", help="run on a real quantum computer") parser.add_argument( "--wires-per-block", type=int, default=2, help="wires per block int static mode" ) @@ -243,38 +244,39 @@ def main(): # test valid_test(dataflow, "test", model, device, qiskit=False) - # run on Qiskit simulator and real Quantum Computers - try: - from qiskit import IBMQ - from torchquantum.plugin import QiskitProcessor - - # firstly perform simulate - print(f"\nTest with Qiskit Simulator") - processor_simulation = QiskitProcessor(use_real_qc=False) - model.set_qiskit_processor(processor_simulation) - valid_test(dataflow, "test", model, device, qiskit=True) - - # then try to run on REAL QC - backend_name = "ibmq_lima" - print(f"\nTest on Real Quantum Computer {backend_name}") - # Please specify your own hub group and project if you have the - # IBMQ premium plan to access more machines. - processor_real_qc = QiskitProcessor( - use_real_qc=True, - backend_name=backend_name, - hub="ibm-q", - group="open", - project="main", - ) - model.set_qiskit_processor(processor_real_qc) - valid_test(dataflow, "test", model, device, qiskit=True) - except ImportError: - print( - "Please install qiskit, create an IBM Q Experience Account and " - "save the account token according to the instruction at " - "'https://github.com/Qiskit/qiskit-ibmq-provider', " - "then try again." - ) + if args.qiskit_simulation: + # run on Qiskit simulator and real Quantum Computers + try: + from qiskit import IBMQ + from torchquantum.plugin import QiskitProcessor + + # firstly perform simulate + print(f"\nTest with Qiskit Simulator") + processor_simulation = QiskitProcessor(use_real_qc=False) + model.set_qiskit_processor(processor_simulation) + valid_test(dataflow, "test", model, device, qiskit=True) + + # then try to run on REAL QC + backend_name = "ibmq_lima" + print(f"\nTest on Real Quantum Computer {backend_name}") + # Please specify your own hub group and project if you have the + # IBMQ premium plan to access more machines. + processor_real_qc = QiskitProcessor( + use_real_qc=True, + backend_name=backend_name, + hub="ibm-q", + group="open", + project="main", + ) + model.set_qiskit_processor(processor_real_qc) + valid_test(dataflow, "test", model, device, qiskit=True) + except ImportError: + print( + "Please install qiskit, create an IBM Q Experience Account and " + "save the account token according to the instruction at " + "'https://github.com/Qiskit/qiskit-ibmq-provider', " + "then try again." + ) if __name__ == "__main__": diff --git a/examples/optimal_control/optimal_control.py b/examples/optimal_control/optimal_control.py index 438e135b..2bff28c4 100644 --- a/examples/optimal_control/optimal_control.py +++ b/examples/optimal_control/optimal_control.py @@ -26,11 +26,22 @@ import torch.optim as optim import torchquantum as tq -import pdb +import argparse import numpy as np if __name__ == "__main__": - pdb.set_trace() + parser = argparse.ArgumentParser() + parser.add_argument("--pdb", action="store_true", help="debug with pdb") + parser.add_argument( + "--epochs", type=int, default=1000, help="number of training epochs" + ) + + args = parser.parse_args() + + if args.pdb: + import pdb + pdb.set_trace() + # target_unitary = torch.tensor([[0, 1], [1, 0]], dtype=torch.complex64) theta = 0.6 target_unitary = torch.tensor( @@ -41,11 +52,11 @@ dtype=torch.complex64, ) - pulse = tq.QuantumPulseDirect(n_steps=4, hamil=[[0, 1], [1, 0]]) + pulse = tq.pulse.QuantumPulseDirect(n_steps=4, hamil=[[0, 1], [1, 0]]) optimizer = optim.Adam(params=pulse.parameters(), lr=5e-3) - for k in range(1000): + for k in range(args.epochs): # loss = (abs(pulse.get_unitary() - target_unitary)**2).sum() loss = ( 1 diff --git a/examples/optimal_control/optimal_control_gaussian.py b/examples/optimal_control/optimal_control_gaussian.py index 861cc92a..51192173 100644 --- a/examples/optimal_control/optimal_control_gaussian.py +++ b/examples/optimal_control/optimal_control_gaussian.py @@ -26,11 +26,22 @@ import torch.optim as optim import torchquantum as tq -import pdb +import argparse import numpy as np if __name__ == "__main__": - pdb.set_trace() + parser = argparse.ArgumentParser() + parser.add_argument("--pdb", action="store_true", help="debug with pdb") + parser.add_argument( + "--epochs", type=int, default=1000, help="number of training epochs" + ) + + args = parser.parse_args() + + if args.pdb: + import pdb + pdb.set_trace() + # target_unitary = torch.tensor([[0, 1], [1, 0]], dtype=torch.complex64) theta = 1.1 target_unitary = torch.tensor( @@ -41,11 +52,11 @@ dtype=torch.complex64, ) - pulse = tq.QuantumPulseGaussian(hamil=[[0, 1], [1, 0]]) + pulse = tq.pulse.QuantumPulseGaussian(hamil=[[0, 1], [1, 0]]) optimizer = optim.Adam(params=pulse.parameters(), lr=5e-3) - for k in range(1000): + for k in range(args.epochs): # loss = (abs(pulse.get_unitary() - target_unitary)**2).sum() loss = ( 1 diff --git a/examples/optimal_control/optimal_control_multi_qubit.py b/examples/optimal_control/optimal_control_multi_qubit.py index 023d4f3c..5658f269 100644 --- a/examples/optimal_control/optimal_control_multi_qubit.py +++ b/examples/optimal_control/optimal_control_multi_qubit.py @@ -26,11 +26,22 @@ import torch.optim as optim import torchquantum as tq -import pdb +import argparse import numpy as np if __name__ == "__main__": - pdb.set_trace() + parser = argparse.ArgumentParser() + parser.add_argument("--pdb", action="store_true", help="debug with pdb") + parser.add_argument( + "--epochs", type=int, default=1000, help="number of training epochs" + ) + + args = parser.parse_args() + + if args.pdb: + import pdb + pdb.set_trace() + # target_unitary = torch.tensor([[0, 1], [1, 0]], dtype=torch.complex64) theta = 0.6 target_unitary = torch.tensor( @@ -43,9 +54,9 @@ dtype=torch.complex64, ) - pulse_q0 = tq.QuantumPulseDirect(n_steps=10, hamil=[[0, 1], [1, 0]]) - pulse_q1 = tq.QuantumPulseDirect(n_steps=10, hamil=[[0, 1], [1, 0]]) - pulse_q01 = tq.QuantumPulseDirect( + pulse_q0 = tq.pulse.QuantumPulseDirect(n_steps=10, hamil=[[0, 1], [1, 0]]) + pulse_q1 = tq.pulse.QuantumPulseDirect(n_steps=10, hamil=[[0, 1], [1, 0]]) + pulse_q01 = tq.pulse.QuantumPulseDirect( n_steps=10, hamil=[ [1, 0, 0, 0], @@ -62,7 +73,7 @@ lr=5e-3, ) - for k in range(1000): + for k in range(args.epochs): u_0 = pulse_q0.get_unitary() u_1 = pulse_q1.get_unitary() u_01 = pulse_q01.get_unitary() diff --git a/examples/qaoa/max_cut_backprop.py b/examples/qaoa/max_cut_backprop.py index 803d16c9..2f9b2210 100644 --- a/examples/qaoa/max_cut_backprop.py +++ b/examples/qaoa/max_cut_backprop.py @@ -27,6 +27,7 @@ import random import numpy as np +import argparse from torchquantum.functional import mat_dict @@ -172,6 +173,12 @@ def backprop_optimize(model, n_steps=100, lr=0.1): def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--steps", type=int, default=300, help="number of steps" + ) + args = parser.parse_args() + # create a input_graph input_graph = [(0, 1), (0, 3), (1, 2), (2, 3)] n_wires = 4 @@ -184,7 +191,7 @@ def main(): # print("The circuit is", circ.draw(output="mpl")) # circ.draw(output="mpl") # use backprop - backprop_optimize(model, n_steps=300, lr=0.01) + backprop_optimize(model, n_steps=args.steps, lr=0.01) # use parameter shift rule # param_shift_optimize(model, n_steps=500, step_size=100000) diff --git a/examples/quantum_lstm/qlstm.py b/examples/quantum_lstm/qlstm.py index b2f3e32f..82c9258b 100644 --- a/examples/quantum_lstm/qlstm.py +++ b/examples/quantum_lstm/qlstm.py @@ -31,6 +31,7 @@ import torch.nn as nn import torchquantum as tq import torchquantum.functional as tqf +import argparse class QLSTM(nn.Module): @@ -358,6 +359,19 @@ def plot_history(history_classical, history_quantum): plt.show() def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--pdb", action="store_true", help="debug with pdb") + parser.add_argument("--display", action="store_true", help="display results with matplotlib") + parser.add_argument( + "--epochs", type=int, default=300, help="number of training epochs" + ) + + args = parser.parse_args() + + if args.pdb: + import pdb + pdb.set_trace() + tag_to_ix = {"DET": 0, "NN": 1, "V": 2} # Assign each tag with a unique index ix_to_tag = {i:k for k,i in tag_to_ix.items()} @@ -380,7 +394,7 @@ def main(): embedding_dim = 8 hidden_dim = 6 - n_epochs = 300 + n_epochs = args.epochs model_classical = LSTMTagger(embedding_dim, hidden_dim, @@ -404,10 +418,8 @@ def main(): print_result(model_quantum, training_data, word_to_ix, ix_to_tag) - plot_history(history_classical, history_quantum) + if args.display: + plot_history(history_classical, history_quantum) if __name__ == "__main__": - import pdb - pdb.set_trace() - main() diff --git a/examples/quanvolution/quanvolution.py b/examples/quanvolution/quanvolution.py index ada4561b..28ee592b 100644 --- a/examples/quanvolution/quanvolution.py +++ b/examples/quanvolution/quanvolution.py @@ -30,6 +30,7 @@ import torch.optim as optim import numpy as np import random +import argparse from torchquantum.dataset import MNIST from torch.optim.lr_scheduler import CosineAnnealingLR @@ -150,8 +151,17 @@ def valid_test(dataflow, split, model, device, qiskit=False): def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--epochs", type=int, default=15, help="number of training epochs" + ) + parser.add_argument( + "--qiskit-simulation", action="store_true", help="run the program on a real quantum computer" + ) + args = parser.parse_args() + train_model_without_qf = True - n_epochs = 15 + n_epochs = args.epochs random.seed(42) np.random.seed(42) @@ -220,29 +230,30 @@ def main(): scheduler.step() - # run on real QC - try: - from qiskit import IBMQ - from torchquantum.plugin import QiskitProcessor - - # firstly perform simulate - print(f"\nTest with Qiskit Simulator") - processor_simulation = QiskitProcessor(use_real_qc=False) - model.qf.set_qiskit_processor(processor_simulation) - valid_test(dataflow, "test", model, device, qiskit=True) - # then try to run on REAL QC - backend_name = "ibmq_quito" - print(f"\nTest on Real Quantum Computer {backend_name}") - processor_real_qc = QiskitProcessor(use_real_qc=True, backend_name=backend_name) - model.qf.set_qiskit_processor(processor_real_qc) - valid_test(dataflow, "test", model, device, qiskit=True) - except ImportError: - print( - "Please install qiskit, create an IBM Q Experience Account and " - "save the account token according to the instruction at " - "'https://github.com/Qiskit/qiskit-ibmq-provider', " - "then try again." - ) + if args.qiskit_simulation: + # run on real QC + try: + from qiskit import IBMQ + from torchquantum.plugin import QiskitProcessor + + # firstly perform simulate + print(f"\nTest with Qiskit Simulator") + processor_simulation = QiskitProcessor(use_real_qc=False) + model.qf.set_qiskit_processor(processor_simulation) + valid_test(dataflow, "test", model, device, qiskit=True) + # then try to run on REAL QC + backend_name = "ibmq_quito" + print(f"\nTest on Real Quantum Computer {backend_name}") + processor_real_qc = QiskitProcessor(use_real_qc=True, backend_name=backend_name) + model.qf.set_qiskit_processor(processor_real_qc) + valid_test(dataflow, "test", model, device, qiskit=True) + except ImportError: + print( + "Please install qiskit, create an IBM Q Experience Account and " + "save the account token according to the instruction at " + "'https://github.com/Qiskit/qiskit-ibmq-provider', " + "then try again." + ) if __name__ == "__main__": diff --git a/examples/qubit_rotation/qubit_rotation.py b/examples/qubit_rotation/qubit_rotation.py index be2dfc82..bae1e803 100644 --- a/examples/qubit_rotation/qubit_rotation.py +++ b/examples/qubit_rotation/qubit_rotation.py @@ -6,6 +6,7 @@ import torchquantum as tq import torch from torchquantum.measurement import expval_joint_analytical +import argparse class OptimizationModel(torch.nn.Module): @@ -42,7 +43,7 @@ def train(model, device, optimizer): # main function to run the optimization -def main(): +def main(n_epochs): seed = 0 torch.manual_seed(seed) @@ -50,7 +51,6 @@ def main(): device = torch.device("cuda" if use_cuda else "cpu") model = OptimizationModel() - n_epochs = 200 optimizer = torch.optim.SGD(model.parameters(), lr=0.1) for epoch in range(1, n_epochs + 1): @@ -65,4 +65,12 @@ def main(): if __name__ == "__main__": - main() + parser = argparse.ArgumentParser( + prog="Qubit Rotation", + description="Specify Parameters for Qubit Rotation Optimization Example", + ) + parser.add_argument( + "--epochs", type=int, default=200, help="number of training epochs" + ) + args = parser.parse_args() + main(args.epochs) diff --git a/examples/regression/new_run_regression.py b/examples/regression/new_run_regression.py index fdeb5cd4..30fc3aa7 100644 --- a/examples/regression/new_run_regression.py +++ b/examples/regression/new_run_regression.py @@ -305,12 +305,11 @@ def main(): model.set_qiskit_processor(processor_simulation) valid_test(dataflow, q_device, "test", model, device, qiskit=True) + # final valid + valid_test(dataflow, q_device, "valid", model, device, True) except: pass - # final valid - valid_test(dataflow, q_device, "valid", model, device, True) - if __name__ == "__main__": main() diff --git a/examples/save_load_example/save_load.py b/examples/save_load_example/save_load.py index 1022c3aa..c5a6c57a 100644 --- a/examples/save_load_example/save_load.py +++ b/examples/save_load_example/save_load.py @@ -143,7 +143,7 @@ def save_load3(): # print(model.q_layer.rx0._parameters) traced_cell = torch.jit.trace(model, (x)) - torch.jit.save(traced_cell, "model_trace.pth") + torch.jit.save(traced_cell, "model_trace.pt") loaded_trace = torch.jit.load("model_trace.pt") y2 = loaded_trace(x) diff --git a/examples/vqe/new_simple_vqe.py b/examples/vqe/new_simple_vqe.py index b5a83953..31cab355 100644 --- a/examples/vqe/new_simple_vqe.py +++ b/examples/vqe/new_simple_vqe.py @@ -30,7 +30,7 @@ from torchquantum.plugin import qiskit2tq_op_history if __name__ == "__main__": - hamil = Hamiltonian.from_file("./examples/simple_vqe/h2.txt") + hamil = Hamiltonian.from_file("./examples/vqe/h2.txt") ops = [ {'name': 'u3', 'wires': 0, 'trainable': True}, diff --git a/torchquantum/__init__.py b/torchquantum/__init__.py index c8aed9ed..b1529623 100644 --- a/torchquantum/__init__.py +++ b/torchquantum/__init__.py @@ -38,6 +38,7 @@ 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 diff --git a/torchquantum/algorithm/__init__.py b/torchquantum/algorithm/__init__.py index 623d71b7..7dfb672a 100644 --- a/torchquantum/algorithm/__init__.py +++ b/torchquantum/algorithm/__init__.py @@ -25,3 +25,4 @@ from .vqe import * from .hamiltonian import * from .qft import * +from .grover import * diff --git a/torchquantum/functional/sx.py b/torchquantum/functional/sx.py index 7f991b4a..d35075a3 100644 --- a/torchquantum/functional/sx.py +++ b/torchquantum/functional/sx.py @@ -82,7 +82,7 @@ def sx( """ name = "sx" - mat = mat_dict[name] + mat = _sx_mat_dict[name] gate_wrapper( name=name, mat=mat, @@ -129,7 +129,7 @@ def sxdg( """ name = "sxdg" - mat = mat_dict[name] + mat = _sx_mat_dict[name] gate_wrapper( name=name, mat=mat, @@ -176,7 +176,7 @@ def csx( """ name = "csx" - mat = mat_dict[name] + mat = _sx_mat_dict[name] gate_wrapper( name=name, mat=mat, @@ -220,7 +220,7 @@ def c3sx( None. """ name = "c3sx" - mat = mat_dict[name] + mat = _sx_mat_dict[name] gate_wrapper( name=name, mat=mat, diff --git a/torchquantum/layer/layers/seth_layer.py b/torchquantum/layer/layers/seth_layer.py index c80d095e..82865172 100644 --- a/torchquantum/layer/layers/seth_layer.py +++ b/torchquantum/layer/layers/seth_layer.py @@ -32,8 +32,8 @@ from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger -from .layers import LayerTemplate0 - +from .layers import LayerTemplate0, Op1QAllLayer +from ..entanglement.op2_layer import Op2QAllLayer class SethLayer0(LayerTemplate0): """ diff --git a/torchquantum/layer/layers/u3_layer.py b/torchquantum/layer/layers/u3_layer.py index b46d8b45..f0339ed4 100644 --- a/torchquantum/layer/layers/u3_layer.py +++ b/torchquantum/layer/layers/u3_layer.py @@ -32,8 +32,8 @@ from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger -from .layers import LayerTemplate0 - +from .layers import LayerTemplate0, Op1QAllLayer +from ..entanglement.op2_layer import Op2QAllLayer class U3CU3Layer0(LayerTemplate0): """ diff --git a/torchquantum/pulse/__init__.py b/torchquantum/pulse/__init__.py index 5a4539de..d281a73f 100644 --- a/torchquantum/pulse/__init__.py +++ b/torchquantum/pulse/__init__.py @@ -25,4 +25,5 @@ from .utils import * from .sesolve import sesolve from .mesolve import mesolve +from .pulses import * # from .smesolve import smesolve