diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 00000000..802dd408 --- /dev/null +++ b/.cursorignore @@ -0,0 +1,4 @@ +# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) +docs/ +examples/ +figs/ diff --git a/examples/QuantumNAS/quantumnas.ipynb b/examples/QuantumNAS/quantumnas.ipynb index bc8ec0b8..03c5e3ff 100644 --- a/examples/QuantumNAS/quantumnas.ipynb +++ b/examples/QuantumNAS/quantumnas.ipynb @@ -4056,7 +4056,8 @@ "toc_visible": true }, "kernelspec": { - "display_name": "Python 3", + "display_name": "torchquantum", + "language": "python", "name": "python3" }, "language_info": { @@ -4069,7 +4070,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.16" + "version": "3.9.20" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/test/qiskit_plugin_test.py b/test/qiskit_plugin_test.py index d8b7e94b..20ae4b9e 100644 --- a/test/qiskit_plugin_test.py +++ b/test/qiskit_plugin_test.py @@ -36,7 +36,7 @@ get_expectations_from_counts, find_global_phase, ) -from test.static_mode_test import QLayer as AllRandomLayer +from static_mode_test import QLayer as AllRandomLayer from torchquantum.plugin import tq2qiskit from torchquantum.macro import F_DTYPE @@ -54,11 +54,12 @@ def unitary_tq_vs_qiskit_test(): qiskit_compatible=True, ) - unitary_tq = q_layer.get_unitary(q_dev, x) + # unitary_tq = q_layer.get_unitary(q_dev, x) + unitary_tq = q_layer.get_unitary(x) unitary_tq = switch_little_big_endian_matrix(unitary_tq.data.numpy()) # qiskit - circ = tq2qiskit(q_layer, x) + circ = tq2qiskit(q_dev, q_layer, x) simulator = Aer.get_backend("unitary_simulator") result = execute(circ, simulator).result() unitary_qiskit = result.get_unitary(circ) diff --git a/test/static_mode_test.py b/test/static_mode_test.py index a9629a63..77a0d244 100644 --- a/test/static_mode_test.py +++ b/test/static_mode_test.py @@ -155,7 +155,20 @@ def build_random_funcs(self): cnt = 0 while cnt < self.n_funcs: func = np.random.choice(self.funcs) - n_func_wires = op_name_dict[func]().num_wires + # print(f"Selected function: {func}") + + """ + ORIGINAL: n_func_wires = op_name_dict[func]().num_wires + Changed to avoid initialization error with QubitUnitaryFast which requires + parameters during instantiation. Instead, we access num_wires directly + from the class since it's a class attribute. + """ + + op_class = op_name_dict[func] + # print(f"Operator class: {op_class}") + # print(f"Number of wires: {op_class.num_wires}") + n_func_wires = op_class.num_wires + if n_func_wires > self.n_wires: continue cnt += 1 @@ -191,8 +204,9 @@ def forward(self, q_device: tq.QuantumDevice, x): self.func_list, self.func_wires_list, self.func_inverse ): n_func_wires = len(func_wires) - n_func_params = op_name_dict[func]().num_params - + op_class = op_name_dict[func] + # n_func_params = op_name_dict[func]().num_params + n_func_params = op_class.num_params if n_func_params == 0: if func in ["multicnot", "multixcnot"]: func_name_dict[func]( diff --git a/torchquantum/functional/u1.py b/torchquantum/functional/u1.py index 05a94910..3422b976 100644 --- a/torchquantum/functional/u1.py +++ b/torchquantum/functional/u1.py @@ -110,7 +110,7 @@ def u1( """ name = "u1" - mat = mat_dict[name] + mat = _u1_mat_dict[name] gate_wrapper( name=name, mat=mat, @@ -157,7 +157,7 @@ def cu1( """ name = "cu1" - mat = mat_dict[name] + mat = _u1_mat_dict[name] gate_wrapper( name=name, mat=mat, diff --git a/torchquantum/functional/u2.py b/torchquantum/functional/u2.py index 5a1d9b21..d9a6387a 100644 --- a/torchquantum/functional/u2.py +++ b/torchquantum/functional/u2.py @@ -109,7 +109,7 @@ def u2( """ name = "u2" - mat = mat_dict[name] + mat = _u2_mat_dict[name] gate_wrapper( name=name, mat=mat, @@ -156,7 +156,7 @@ def cu2( """ name = "cu2" - mat = mat_dict[name] + mat = _u2_mat_dict[name] gate_wrapper( name=name, mat=mat, diff --git a/torchquantum/graph/graphs.py b/torchquantum/graph/graphs.py index 45bb13fc..6a54252a 100644 --- a/torchquantum/graph/graphs.py +++ b/torchquantum/graph/graphs.py @@ -156,10 +156,17 @@ def add_func( if not self.is_list_finish: # graph construction is not finished, build a new operation and # add the operation to the graph - op = tq.op_name_dict[name]() - op.params = params - op.n_wires = n_wires - op.wires = wires + print(tq.op_name_dict[name]) + # op = tq.op_name_dict[name]() + op_class = tq.op_name_dict[name] + op = op_class(has_params=True if params is not None else False, + trainable=False, + init_params=params, + n_wires=n_wires, + wires=wires) + # op.params = params + # op.n_wires = n_wires + # op.wires = wires op.graph = tq.QuantumGraph() op.parent_graph = parent_graph op.static_mode = True diff --git a/torchquantum/measurement/measurements.py b/torchquantum/measurement/measurements.py index c3c2daad..1365b71a 100644 --- a/torchquantum/measurement/measurements.py +++ b/torchquantum/measurement/measurements.py @@ -42,8 +42,22 @@ def measure(qdev, n_shots=1024, draw_id=None): Returns: distribution of bitstrings """ - bitstring_candidates = gen_bitstrings(qdev.n_wires) + + """ + In measure function, the statevector is copied to the CPU and + a list of all possible 2^n bitstrings is constructed to do the sampling. + This is again a huge CPU memory and runtime overhead since sampling can done on the GPU directly and efficiently. + Here is a sketch of how that might look like in PyTorch using Inverse transform sampling method: + Calculate squared amplitudes on GPU using troch.abs and troch.square. + Calculate the cumulative distribution function using troch.cumsum. + Generate random numbers (as many as the required number of samples) between 0 and 1 using torch.rand. + Find the index of each random number inside the cumulative distribution using troch.searchsorted. + Copy the indices to the CPU and convert each number to its bitstring binary representation. + """ + + bitstring_candidates = gen_bitstrings(qdev.n_wires) # length is 2 to the power of n_wires if isinstance(qdev, tq.QuantumDevice): + # length is 2 to the power of n_wires state_mag = qdev.get_states_1d().abs().detach().cpu().numpy() elif isinstance(qdev, tq.NoiseDevice): ''' diff --git a/torchquantum/operator/op_types.py b/torchquantum/operator/op_types.py index bdf35337..2d3a9dc9 100644 --- a/torchquantum/operator/op_types.py +++ b/torchquantum/operator/op_types.py @@ -3,11 +3,16 @@ import torchquantum as tq import torchquantum.functional.functionals as tqf import numpy as np +import logging from abc import ABCMeta from ..macro import C_DTYPE, F_DTYPE from typing import Iterable, Union, List from enum import IntEnum + +# Add logging init +logger = logging.getLogger(__name__) + __all__ = [ "Operator", "Operation",