From a016ec8fe5aac1cf243fc91e72263d75487620d1 Mon Sep 17 00:00:00 2001 From: Zhaoyilunnn Date: Mon, 17 Jun 2024 16:50:05 +0800 Subject: [PATCH 1/2] fix: autograd not compatible with numpy 2.x --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4389f8c..aeedbd6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ +autograd>=1.6.2 graphviz>=0.14.2 ipython>=8.14.0 matplotlib>=3.5.2 networkx>=2.6.3 -numpy>=1.20.3 +numpy>=1.20.3,<2.0.0 ply~=3.11 pybind11>=2.10.3 requests>=2.26.0 @@ -10,4 +11,3 @@ scikit-build>=0.16.1 scipy>=1.8.1 setuptools>=58.0.4 sparse>=0.13.0 -autograd>=1.6.2 \ No newline at end of file From edd008300e0248dec0541a8a76bbbe1df1333f44 Mon Sep 17 00:00:00 2001 From: Zhaoyilunnn Date: Mon, 17 Jun 2024 17:51:51 +0800 Subject: [PATCH 2/2] chore: run pre-commit to format all files --- doc/source/conf.py | 2 +- quafu/algorithms/hamiltonian.py | 4 +- quafu/algorithms/optimizer.py | 109 ++-- quafu/algorithms/templates/amplitude.py | 63 ++- quafu/elements/element_gates/clifford.py | 6 +- quafu/elements/element_gates/element_gates.py | 262 ++++++---- quafu/elements/element_gates/pauli.py | 9 +- quafu/elements/element_gates/rotation.py | 15 +- quafu/elements/instruction.py | 7 +- quafu/elements/matrices/mat_lib.py | 66 +-- quafu/elements/noise.py | 96 ++-- quafu/elements/oracle.py | 2 +- quafu/elements/quantum_gate.py | 260 ++++++---- quafu/elements/unitary/decomposer.py | 4 +- quafu/elements/utils.py | 9 +- quafu/qfasm/qfasm_parser.py | 46 +- quafu/simulators/default_simulator.py | 7 +- quafu/simulators/simulator.py | 85 ++-- quafu/synthesis/evolution.py | 2 +- setup.py | 2 +- src/qfvm/circuit.hpp | 120 ++--- src/qfvm/instructions.hpp | 464 +++++++++--------- src/qfvm/qasm.hpp | 15 +- src/qfvm/qfvm.cpp | 225 ++++----- src/qfvm/simulator.hpp | 199 ++++---- src/qfvm/statevector.hpp | 77 ++- src/qfvm/util.h | 55 ++- tests/quafu/algorithms/amplitude_test.py | 4 +- tests/quafu/algorithms/estimator_test.py | 1 - tests/quafu/circuits/building_circuit_test.py | 40 +- tests/quafu/circuits/quantum_circuit_test.py | 7 +- tests/quafu/instruction/gates_test.py | 46 +- tests/quafu/qasm/parameter_test.py | 32 +- tests/quafu/qasm/parser_test.py | 2 +- tests/quafu/simulator/basis_test.py | 35 +- tests/quafu/simulator/classic_test.py | 7 +- tests/quafu/simulator/noise_test.py | 18 +- 37 files changed, 1344 insertions(+), 1059 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index d9581d1..9bfcf34 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -48,7 +48,7 @@ "current_version": current_version, "versions": [ ["0.2.x", pages_root + "/0.2.x"], - ["0.3.x", pages_root + "/0.3.x"] + ["0.3.x", pages_root + "/0.3.x"], ], } diff --git a/quafu/algorithms/hamiltonian.py b/quafu/algorithms/hamiltonian.py index 7ea3178..0c23320 100644 --- a/quafu/algorithms/hamiltonian.py +++ b/quafu/algorithms/hamiltonian.py @@ -13,9 +13,9 @@ # limitations under the License. from typing import Iterable -import scipy.sparse as sp -import numpy as np +import numpy as np +import scipy.sparse as sp from quafu.exceptions.quafu_error import QuafuError IMat = sp.coo_matrix(np.array([[1.0, 0.0], [0.0, 1.0]], dtype=complex)) diff --git a/quafu/algorithms/optimizer.py b/quafu/algorithms/optimizer.py index 0169dd5..5893f1a 100644 --- a/quafu/algorithms/optimizer.py +++ b/quafu/algorithms/optimizer.py @@ -1,28 +1,52 @@ import numpy as np -def adam(func, x, gradsf, args=(), call_back=None, eta=0.01,beta1=0.9, beta2=0.999, epsilon=1e-8, maxiter=1000, tol=1e-8, verbose=False): + +def adam( + func, + x, + gradsf, + args=(), + call_back=None, + eta=0.01, + beta1=0.9, + beta2=0.999, + epsilon=1e-8, + maxiter=1000, + tol=1e-8, + verbose=False, +): n_para = len(x) mt = np.zeros(n_para) vt = np.zeros(n_para) traj = [] if verbose: - print((" "*5).join(["step".ljust(10), "loss".ljust(10), "grad_norm".ljust(10)])) - - grads_norm = 0. + print( + (" " * 5).join(["step".ljust(10), "loss".ljust(10), "grad_norm".ljust(10)]) + ) + + grads_norm = 0.0 for j in range(maxiter): traj.append(func(x, *args)) if verbose: - print((" "*5).join([("%d" %j).ljust(10), ("%.5f" %traj[j]).ljust(10), ("%.5f" %grads_norm).ljust(10)])) - if j > 0 and abs(traj[j]-traj[j-1]) <= tol: + print( + (" " * 5).join( + [ + ("%d" % j).ljust(10), + ("%.5f" % traj[j]).ljust(10), + ("%.5f" % grads_norm).ljust(10), + ] + ) + ) + if j > 0 and abs(traj[j] - traj[j - 1]) <= tol: return x, traj[-1], traj - + grads = np.array(gradsf(x, *args)) grads_norm = np.linalg.norm(grads) - mt = beta1 * mt + (1.-beta1) * grads - vt = beta1 * vt + (1.-beta2) * grads**2 - mtt = mt / (1-beta1**(j+2)) - vtt = vt / (1-beta2**(j+2)) + mt = beta1 * mt + (1.0 - beta1) * grads + vt = beta1 * vt + (1.0 - beta2) * grads**2 + mtt = mt / (1 - beta1 ** (j + 2)) + vtt = vt / (1 - beta2 ** (j + 2)) x = x - eta * mtt / (np.sqrt(vtt) + epsilon) if call_back: call_back(x, *args) @@ -31,37 +55,61 @@ def adam(func, x, gradsf, args=(), call_back=None, eta=0.01,beta1=0.9, beta2=0.9 def spsa_grad(func, x, k, args=(), spsa_iter=10, c=0.1, gamma=0.101): - dim = len(x) - ck = c/(k)**gamma + ck = c / (k) ** gamma gx = 0.0 for i in range(spsa_iter): - Delta = 2*np.round(np.random.rand(dim))-1 - x1 = x + ck*Delta - x2 = x - ck*Delta - y1 = func(x1, *args) + Delta = 2 * np.round(np.random.rand(dim)) - 1 + x1 = x + ck * Delta + x2 = x - ck * Delta + y1 = func(x1, *args) y2 = func(x2, *args) - gx += (y1 - y2) / (2*ck*Delta) + gx += (y1 - y2) / (2 * ck * Delta) gx = gx / spsa_iter return gx -def spsa(func, x, args=(), call_back=None, spsa_iter=10, max_iter=1000, a=0.1, c=0.1, A=100, alpha=0.602, gamma=0.101, tol=1e-8, verbose=False): - '''SPSA minimize - c: at a level of standard deviation of func - A: <=10% of max_iter ''' + +def spsa( + func, + x, + args=(), + call_back=None, + spsa_iter=10, + max_iter=1000, + a=0.1, + c=0.1, + A=100, + alpha=0.602, + gamma=0.101, + tol=1e-8, + verbose=False, +): + """SPSA minimize + c: at a level of standard deviation of func + A: <=10% of max_iter""" traj = [func(x, *args)] if verbose: - print((" "*5).join(["step".ljust(10), "loss".ljust(10), "grad_norm".ljust(10)])) - - grads_norm = 0. + print( + (" " * 5).join(["step".ljust(10), "loss".ljust(10), "grad_norm".ljust(10)]) + ) + + grads_norm = 0.0 for k in range(max_iter): if verbose: - print((" "*5).join([("%d" %k).ljust(10), ("%.5f" %traj[k]).ljust(10), ("%.5f" %grads_norm).ljust(10)])) - if k > 0 and abs(traj[k]-traj[k-1]) <= tol: + print( + (" " * 5).join( + [ + ("%d" % k).ljust(10), + ("%.5f" % traj[k]).ljust(10), + ("%.5f" % grads_norm).ljust(10), + ] + ) + ) + if k > 0 and abs(traj[k] - traj[k - 1]) <= tol: return x, traj[-1], traj - - ak = a/(k+1+A)**alpha - grads = spsa_grad(func, x, k+1, args, spsa_iter, c=c, gamma=gamma) + + ak = a / (k + 1 + A) ** alpha + grads = spsa_grad(func, x, k + 1, args, spsa_iter, c=c, gamma=gamma) grads_norm = np.linalg.norm(grads) x = x - ak * grads if call_back: @@ -69,4 +117,3 @@ def spsa(func, x, args=(), call_back=None, spsa_iter=10, max_iter=1000, a=0.1, c traj.append(func(x, *args)) return x, traj[-1], traj - diff --git a/quafu/algorithms/templates/amplitude.py b/quafu/algorithms/templates/amplitude.py index 8756633..65c9a05 100644 --- a/quafu/algorithms/templates/amplitude.py +++ b/quafu/algorithms/templates/amplitude.py @@ -13,11 +13,10 @@ # limitations under the License. """Amplitude Embedding by a decomposition into gates""" -import quafu.elements.element_gates as qeg import numpy as np +import quafu.elements.element_gates as qeg from quafu.elements import QuantumGate - # from .basic_entangle import BasicEntangleLayers @@ -41,7 +40,7 @@ def __iter__(self): def __getitem__(self, index): return self.gate_list[index] - + def __add__(self, gates): """Addition operator.""" out = [] @@ -51,7 +50,7 @@ def __add__(self, gates): else: raise TypeError("Contains unsupported gate") return out - + def __radd__(self, other): out = [] if all(isinstance(gate, QuantumGate) for gate in other): @@ -75,10 +74,12 @@ def _preprocess(self, state, num_qubits, pad_with, normalize): # check shape if len(shape) != 1: - raise ValueError(f"state must be a one-dimensional tensor; got shape {shape}.") + raise ValueError( + f"state must be a one-dimensional tensor; got shape {shape}." + ) n_state = shape[0] - dim = 2 ** num_qubits + dim = 2**num_qubits if pad_with is None and n_state != dim: raise ValueError( f"The length of state should be {dim}; got length {n_state}.Please check num_qubits " @@ -111,10 +112,13 @@ def _preprocess(self, state, num_qubits, pad_with, normalize): ) new_state_batch.append(feature_set) - return np.stack(new_state_batch).astype(np.complex128) if batched else new_state_batch[0].astype(np.complex128) - - def _build(self): + return ( + np.stack(new_state_batch).astype(np.complex128) + if batched + else new_state_batch[0].astype(np.complex128) + ) + def _build(self): a = np.abs(self.state) omega = np.angle(self.state) # change order of qubits, since original code was written for IBM machines @@ -126,7 +130,9 @@ def _build(self): alpha_y_k = _get_alpha_y(a, len(qubits_reverse), k) control = qubits_reverse[k:] target = qubits_reverse[k - 1] - gate_list.extend(_apply_uniform_rotation_dagger(qeg.RYGate, alpha_y_k, control, target)) + gate_list.extend( + _apply_uniform_rotation_dagger(qeg.RYGate, alpha_y_k, control, target) + ) # If necessary, apply inverse z rotation cascade to prepare correct phases of amplitudes if not np.allclose(omega, 0): @@ -136,11 +142,14 @@ def _build(self): target = qubits_reverse[k - 1] if len(alpha_z_k) > 0: gate_list.extend( - _apply_uniform_rotation_dagger(qeg.RZGate, alpha_z_k, control, target) + _apply_uniform_rotation_dagger( + qeg.RZGate, alpha_z_k, control, target + ) ) return gate_list - + + ## MottonenStatePreparation related functions. def gray_code(rank): """Generates the Gray code of given rank. @@ -167,18 +176,19 @@ def gray_code_recurse(g, rank): return g + def _matrix_M_entry(row, col): - - # (col >> 1) ^ col is the Gray code of col - b_and_g = row & ((col >> 1) ^ col) - sum_of_ones = 0 - while b_and_g > 0: - if b_and_g & 0b1: - sum_of_ones += 1 + # (col >> 1) ^ col is the Gray code of col + b_and_g = row & ((col >> 1) ^ col) + sum_of_ones = 0 + while b_and_g > 0: + if b_and_g & 0b1: + sum_of_ones += 1 + + b_and_g = b_and_g >> 1 - b_and_g = b_and_g >> 1 + return (-1) ** sum_of_ones - return (-1) ** sum_of_ones def compute_theta(alpha): ln = alpha.shape[-1] @@ -195,7 +205,6 @@ def compute_theta(alpha): def _apply_uniform_rotation_dagger(gate, alpha, control_wires, target_wire): - gate_list = [] theta = compute_theta(alpha) @@ -220,8 +229,8 @@ def _apply_uniform_rotation_dagger(gate, alpha, control_wires, target_wire): gate_list.append(qeg.CXGate(control_wires[control_index], target_wire)) return gate_list -def _get_alpha_z(omega, n, k): +def _get_alpha_z(omega, n, k): indices1 = [ [(2 * j - 1) * 2 ** (k - 1) + l - 1 for l in range(1, 2 ** (k - 1) + 1)] for j in range(1, 2 ** (n - k) + 1) @@ -237,8 +246,8 @@ def _get_alpha_z(omega, n, k): return np.sum(diff, axis=-1) -def _get_alpha_y(a, n, k): +def _get_alpha_y(a, n, k): indices_numerator = [ [(2 * (j + 1) - 1) * 2 ** (k - 1) + l for l in range(2 ** (k - 1))] for j in range(2 ** (n - k)) @@ -246,7 +255,9 @@ def _get_alpha_y(a, n, k): numerator = np.take(a, indices=indices_numerator, axis=-1) numerator = np.sum(np.abs(numerator) ** 2, axis=-1) - indices_denominator = [[j * 2**k + l for l in range(2**k)] for j in range(2 ** (n - k))] + indices_denominator = [ + [j * 2**k + l for l in range(2**k)] for j in range(2 ** (n - k)) + ] denominator = np.take(a, indices=indices_denominator, axis=-1) denominator = np.sum(np.abs(denominator) ** 2, axis=-1) @@ -262,4 +273,4 @@ def _get_alpha_y(a, n, k): division = np.where(denominator != 0.0, division, 0.0) - return 2 * np.arcsin(np.sqrt(division)) \ No newline at end of file + return 2 * np.arcsin(np.sqrt(division)) diff --git a/quafu/elements/element_gates/clifford.py b/quafu/elements/element_gates/clifford.py index c8fa0b9..c045daa 100644 --- a/quafu/elements/element_gates/clifford.py +++ b/quafu/elements/element_gates/clifford.py @@ -1,5 +1,3 @@ -from .element_gates import HGate, SGate, CXGate - -__all__ = ['HGate', 'SGate', 'CXGate'] - +from .element_gates import CXGate, HGate, SGate +__all__ = ["HGate", "SGate", "CXGate"] diff --git a/quafu/elements/element_gates/element_gates.py b/quafu/elements/element_gates/element_gates.py index 8526374..5f2c813 100644 --- a/quafu/elements/element_gates/element_gates.py +++ b/quafu/elements/element_gates/element_gates.py @@ -19,56 +19,67 @@ """ -from typing import Dict, List from abc import ABC +from typing import List import quafu.elements.matrices as mat -from quafu.elements.quantum_gate import QuantumGate, ControlledGate +from quafu.elements.quantum_gate import ControlledGate, QuantumGate + from ..parameters import ParameterType # # # # # # # # # # # # # # # Internal helper classes # # # # # # # # # # # # # # # + class _C11Gate(ControlledGate, ABC): ct_dims = (1, 1, 2) + def wrap_para(matfunc): - def wrap_func(paras:List[ParameterType]): - return matfunc(*paras) + def wrap_func(paras: List[ParameterType]): + return matfunc(*paras) + return wrap_func + # # # # # # # # # # # # # # # Paulis # # # # # # # # # # # # # # # @QuantumGate.register() class IdGate(QuantumGate): name = "ID" _raw_matrix = mat.IdMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] @QuantumGate.register() class XGate(QuantumGate): name = "X" _raw_matrix = mat.XMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class YGate(QuantumGate): name = "Y" _raw_matrix = mat.YMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class ZGate(QuantumGate): name = "Z" _raw_matrix = mat.ZMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] # # # # # # # # # # # # # # # Sqrt Paulis # # # # # # # # # # # # # # # @@ -76,184 +87,213 @@ def __init__(self, pos: int): class SGate(QuantumGate): name = "S" _raw_matrix = mat.SMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class SdgGate(QuantumGate): name = "Sdg" _raw_matrix = mat.SMatrix.conj().T - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class TGate(QuantumGate): name = "T" _raw_matrix = mat.TMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class TdgGate(QuantumGate): name = "Tdg" _raw_matrix = mat.TMatrix.conj().T - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class SXGate(QuantumGate): name = "SX" _raw_matrix = mat.SXMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class SXdgGate(QuantumGate): name = "SXdg" _raw_matrix = mat.SXMatrix.conj().T - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class SYGate(QuantumGate): name = "SY" _raw_matrix = mat.SYMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] def to_qasm(self, with_para): - return "ry(pi/2) q[%d]" %(self.pos[0]) + return "ry(pi/2) q[%d]" % (self.pos[0]) + @QuantumGate.register() class SYdgGate(QuantumGate): name = "SYdg" _raw_matrix = mat.SYMatrix.conj().T - paras = [] - def __init__(self, pos: int): - self.pos = [pos] + paras = [] + def __init__(self, pos: int): + self.pos = [pos] - # # # # # # # # # # # # # Pauli Linear Combinations # # # # # # # # # # # # # @QuantumGate.register() class HGate(QuantumGate): name = "H" _raw_matrix = mat.HMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] + @QuantumGate.register() class WGate(QuantumGate): name = "W" _raw_matrix = mat.WMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] - + self.pos = [pos] + def to_qasm(self, with_para): q = self.pos[0] - return "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d]" %(q, q, q) + return "rz(-pi/4) q[%d];\nrx(pi) q[%d];\nrz(pi/4) q[%d]" % (q, q, q) + @QuantumGate.register() class SWGate(QuantumGate): name = "SW" _raw_matrix = mat.SWMatrix - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] def to_qasm(self, with_para): q = self.pos[0] - return "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d]" %(q, q, q) + return "rz(-pi/4) q[%d];\nrx(pi/2) q[%d];\nrz(pi/4) q[%d]" % (q, q, q) + @QuantumGate.register() class SWdgGate(QuantumGate): name = "SWdg" _raw_matrix = mat.SWMatrix.conj().T - paras = [] + paras = [] + def __init__(self, pos: int): - self.pos = [pos] + self.pos = [pos] def to_qasm(self, with_para): q = self.pos[0] - return "rz(-pi/4) q[%d];\nrx(-pi/2) q[%d];\nrz(pi/4) q[%d]" %(q, q, q) - + return "rz(-pi/4) q[%d];\nrx(-pi/2) q[%d];\nrz(pi/4) q[%d]" % (q, q, q) # # # # # # # # # # # # # Rotations # # # # # # # # # # # # # @QuantumGate.register() class RXGate(QuantumGate): - def __init__(self, pos: int, theta:ParameterType): + def __init__(self, pos: int, theta: ParameterType): super().__init__("RX", [pos], [theta], wrap_para(mat.rx_mat)) + @QuantumGate.register() class RYGate(QuantumGate): - def __init__(self, pos: int, theta:ParameterType): - super().__init__("RY", [pos], [theta], wrap_para(mat.ry_mat)) + def __init__(self, pos: int, theta: ParameterType): + super().__init__("RY", [pos], [theta], wrap_para(mat.ry_mat)) + @QuantumGate.register() class RZGate(QuantumGate): - def __init__(self, pos: int, theta:ParameterType): + def __init__(self, pos: int, theta: ParameterType): super().__init__("RZ", [pos], [theta], wrap_para(mat.rz_mat)) + # @QuantumGate.register() # class U2(QuantumGate): # def __init__(self, pos: int, phi: float, _lambda: float): # super().__init__("U2", [pos], [phi, _lambda], u2matrix(phi, _lambda)) + @QuantumGate.register() class U3Gate(QuantumGate): - def __init__(self, pos: int, theta : ParameterType, phi: ParameterType, _lambda: ParameterType): - super().__init__("U3", [pos], [theta, phi, _lambda], matrix = wrap_para(mat.u3matrix)) + def __init__( + self, pos: int, theta: ParameterType, phi: ParameterType, _lambda: ParameterType + ): + super().__init__( + "U3", [pos], [theta, phi, _lambda], matrix=wrap_para(mat.u3matrix) + ) + -@QuantumGate.register(name='p') +@QuantumGate.register(name="p") class PhaseGate(QuantumGate): - def __init__(self, pos: int, _lambda:ParameterType): + def __init__(self, pos: int, _lambda: ParameterType): super().__init__("P", [pos], [_lambda], wrap_para(mat.pmatrix)) + @QuantumGate.register() class RXXGate(QuantumGate): - def __init__(self, q1:int, q2:int, theta:ParameterType): + def __init__(self, q1: int, q2: int, theta: ParameterType): super().__init__("RXX", [q1, q2], [theta], wrap_para(mat.rxx_mat)) - - + + @QuantumGate.register() class RYYGate(QuantumGate): - def __init__(self, q1:int, q2:int, theta:ParameterType): + def __init__(self, q1: int, q2: int, theta: ParameterType): super().__init__("RYY", [q1, q2], [theta], wrap_para(mat.ryy_mat)) - + @QuantumGate.register() class RZZGate(QuantumGate): - def __init__(self, q1:int, q2:int, theta:ParameterType): + def __init__(self, q1: int, q2: int, theta: ParameterType): super().__init__("RZZ", [q1, q2], [theta], wrap_para(mat.rzz_mat)) - -#--------------------ControalledGate---------- +# --------------------ControalledGate---------- # TODO: implement using ControllU class # # # # # # # # # # # # # Ctrl-Paulis # # # # # # # # # # # # # @QuantumGate.register() class CXGate(ControlledGate): - name = "CX" + name = "CX" _targ_name = "X" _targ_matrix = mat.XMatrix _raw_matrix = mat.CXMatrix paras = [] - def __init__(self, ctrl:int, targ:int): + + def __init__(self, ctrl: int, targ: int): assert ctrl != targ - self.ctrls = [ctrl] + self.ctrls = [ctrl] self.targs = [targ] self.pos = self.ctrls + self.targs @@ -261,58 +301,66 @@ def __init__(self, ctrl:int, targ:int): def symbol(self): return "+" + @QuantumGate.register() class CYGate(ControlledGate): - name = "CY" + name = "CY" _targ_name = "Y" _targ_matrix = mat.YMatrix _raw_matrix = mat.CYMatrix paras = [] - def __init__(self, ctrl:int, targ:int): + + def __init__(self, ctrl: int, targ: int): assert ctrl != targ - self.ctrls = [ctrl] + self.ctrls = [ctrl] self.targs = [targ] self.pos = self.ctrls + self.targs + @QuantumGate.register() class CZGate(ControlledGate): - name = "CZ" + name = "CZ" _targ_name = "Z" _targ_matrix = mat.ZMatrix _raw_matrix = mat.CZMatrix paras = [] - def __init__(self, ctrl:int, targ:int): + + def __init__(self, ctrl: int, targ: int): assert ctrl != targ - self.ctrls = [ctrl] + self.ctrls = [ctrl] self.targs = [targ] self.pos = self.ctrls + self.targs + @QuantumGate.register() class CSGate(ControlledGate): - name = "CS" + name = "CS" _targ_name = "S" _targ_matrix = mat.XMatrix _raw_matrix = mat.CXMatrix paras = [] - def __init__(self, ctrl:int, targ:int): + + def __init__(self, ctrl: int, targ: int): assert ctrl != targ - self.ctrls = [ctrl] + self.ctrls = [ctrl] self.targs = [targ] self.pos = self.ctrls + self.targs def to_qasm(self, with_para): return "cp(pi/2) " + "q[%d],q[%d]" % (self.pos[0], self.pos[1]) - + + @QuantumGate.register() class CTGate(ControlledGate): - name = "CT" + name = "CT" _targ_name = "T" _targ_matrix = mat.TMatrix _raw_matrix = mat.CTMatrix paras = [] - def __init__(self, ctrl:int, targ:int): + + def __init__(self, ctrl: int, targ: int): assert ctrl != targ - self.ctrls = [ctrl] + self.ctrls = [ctrl] self.targs = [targ] self.pos = self.ctrls + self.targs @@ -324,83 +372,92 @@ def to_qasm(self, with_para): # note: this is the only ctrl-gate that is not a FixedGate @QuantumGate.register() class CPGate(ControlledGate): - def __init__(self, ctrl:int, targ:int, _lambda:ParameterType): + def __init__(self, ctrl: int, targ: int, _lambda: ParameterType): super().__init__("CP", "P", [ctrl], [targ], [_lambda], wrap_para(mat.pmatrix)) - @QuantumGate.register() class CRXGate(ControlledGate): - def __init__(self, ctrl:int, targ:int, theta:ParameterType): + def __init__(self, ctrl: int, targ: int, theta: ParameterType): super().__init__("CRX", "RX", [ctrl], [targ], [theta], wrap_para(mat.rx_mat)) + @QuantumGate.register() class CRYGate(ControlledGate): - def __init__(self, ctrl:int, targ:int, theta:ParameterType): + def __init__(self, ctrl: int, targ: int, theta: ParameterType): super().__init__("CRY", "RY", [ctrl], [targ], [theta], wrap_para(mat.ry_mat)) + @QuantumGate.register() class CRZGate(ControlledGate): - def __init__(self, ctrl:int, targ:int, theta:ParameterType): + def __init__(self, ctrl: int, targ: int, theta: ParameterType): super().__init__("CRZ", "RZ", [ctrl], [targ], [theta], wrap_para(mat.rz_mat)) + # # # # # # # # # # # # # MultiCtrl-Paulis # # # # # # # # # # # # # @QuantumGate.register() class MCXGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int): + def __init__(self, ctrls: List[int], targ: int): super().__init__("MCX", "X", ctrls, [targ], [], mat.XMatrix) - + @property def symbol(self): return "+" + @QuantumGate.register() class MCYGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int): + def __init__(self, ctrls: List[int], targ: int): super().__init__("MCY", "Y", ctrls, [targ], [], mat.YMatrix) + @QuantumGate.register() class MCZGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int): + def __init__(self, ctrls: List[int], targ: int): super().__init__("MCZ", "Z", ctrls, [targ], [], mat.ZMatrix) + @QuantumGate.register() class MCRXGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int, theta:ParameterType): + def __init__(self, ctrls: List[int], targ: int, theta: ParameterType): super().__init__("MCRX", "RX", ctrls, [targ], [theta], wrap_para(mat.rx_mat)) + @QuantumGate.register() class MCRYGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int, theta:ParameterType): + def __init__(self, ctrls: List[int], targ: int, theta: ParameterType): super().__init__("MCRY", "RY", ctrls, [targ], [theta], wrap_para(mat.ry_mat)) + @QuantumGate.register() class MCRZGate(ControlledGate): - def __init__(self, ctrls:List[int], targ:int, theta:ParameterType): + def __init__(self, ctrls: List[int], targ: int, theta: ParameterType): super().__init__("MCRZ", "RZ", ctrls, [targ], [theta], wrap_para(mat.rz_mat)) - @QuantumGate.register() class CCXGate(ControlledGate): - def __init__(self, ctrl1:int, ctrl2:int, targ:int): + def __init__(self, ctrl1: int, ctrl2: int, targ: int): super().__init__("CCX", "X", [ctrl1, ctrl2], [targ], [], mat.XMatrix) + @QuantumGate.register() class CSwapGate(ControlledGate): - def __init__(self, ctrl:int, targ1:int, targ2:int): + def __init__(self, ctrl: int, targ1: int, targ2: int): super().__init__("CSWAP", "SWAP", [ctrl], [targ1, targ2], [], mat.SwapMatrix) + # # # # # # # # # # # # # SWAPs # # # # # # # # # # # # # @QuantumGate.register() class SwapGate(QuantumGate): name = "SWAP" matrix = mat.SwapMatrix _raw_matrix = mat.SwapMatrix - paras = [] - def __init__(self, q1:int, q2:int): - self.pos = [q1, q2] - + paras = [] + + def __init__(self, q1: int, q2: int): + self.pos = [q1, q2] + @property def symbol(self): return "x" @@ -411,13 +468,14 @@ class ISwapGate(QuantumGate): name = "ISWAP" matrix = mat.ISwapMatrix _raw_matrix = mat.ISwapMatrix - paras = [] - def __init__(self, q1:int, q2:int): - self.pos = [q1, q2] + paras = [] + + def __init__(self, q1: int, q2: int): + self.pos = [q1, q2] -QuantumGate.gate_classes['cnot'] = CXGate -QuantumGate.gate_classes['toffoli'] = CCXGate -QuantumGate.gate_classes['fredon'] = CSwapGate +QuantumGate.gate_classes["cnot"] = CXGate +QuantumGate.gate_classes["toffoli"] = CCXGate +QuantumGate.gate_classes["fredon"] = CSwapGate FredkinGate = CSwapGate -ToffoliGate = CCXGate \ No newline at end of file +ToffoliGate = CCXGate diff --git a/quafu/elements/element_gates/pauli.py b/quafu/elements/element_gates/pauli.py index 8272b48..0ab90a7 100644 --- a/quafu/elements/element_gates/pauli.py +++ b/quafu/elements/element_gates/pauli.py @@ -1,3 +1,8 @@ -from .element_gates import XGate, YGate, ZGate, IdGate +from .element_gates import IdGate, XGate, YGate, ZGate -__all__ = ['IdGate', 'XGate', 'YGate', 'ZGate'] # hint: "SZ" gate is S contained in Clifford gates +__all__ = [ + "IdGate", + "XGate", + "YGate", + "ZGate", +] # hint: "SZ" gate is S contained in Clifford gates diff --git a/quafu/elements/element_gates/rotation.py b/quafu/elements/element_gates/rotation.py index 62efdf6..6294a2d 100644 --- a/quafu/elements/element_gates/rotation.py +++ b/quafu/elements/element_gates/rotation.py @@ -1,3 +1,12 @@ -from .element_gates import RXGate, RYGate, RZGate,RXXGate, RYYGate, RZZGate, PhaseGate, CRXGate, CRYGate, CRZGate - - +from .element_gates import ( + CRXGate, + CRYGate, + CRZGate, + PhaseGate, + RXGate, + RXXGate, + RYGate, + RYYGate, + RZGate, + RZZGate, +) diff --git a/quafu/elements/instruction.py b/quafu/elements/instruction.py index de795ca..23e3258 100644 --- a/quafu/elements/instruction.py +++ b/quafu/elements/instruction.py @@ -13,14 +13,13 @@ # limitations under the License. from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Union +from typing import Dict, List from .parameters import ParameterType __all__ = ["Instruction", "Barrier", "Measure", "Reset"] - class Instruction(ABC): """Base class for ALL the possible instructions on Quafu superconducting quantum circuits. @@ -81,12 +80,12 @@ def register_ins(cls, subclass, name: str = None): raise ValueError(f"Name {name} already exists.") cls.ins_classes[name] = subclass return subclass - + @classmethod def register(cls, name: str = None): def wrapper(subclass): return cls.register_ins(subclass, name) - + return wrapper @abstractmethod diff --git a/quafu/elements/matrices/mat_lib.py b/quafu/elements/matrices/mat_lib.py index f3b5bf7..97235ee 100644 --- a/quafu/elements/matrices/mat_lib.py +++ b/quafu/elements/matrices/mat_lib.py @@ -1,31 +1,17 @@ import numpy as np IdMatrix = np.eye(2, dtype=complex) -XMatrix = np.array( - [[0.0, 1.0], - [1.0, 0.0]], dtype=complex) -YMatrix = np.array( - [[0.0, -1.0j], - [1.0j, 0.0]], dtype=complex) -ZMatrix = np.array( - [[1.0, 0.0], - [0.0, -1.0]], dtype=complex) -SMatrix = np.array( - [[1.0, 0.0], - [0.0, 1.0j]], dtype=complex) -SXMatrix = np.array( - [[1.0, 1.0j], - [1.0j, 1.0]], dtype=complex) / np.sqrt(2) -SYMatrix = np.array( - [[1.0, -1.0], - [1.0, 1.0]], dtype=complex) / np.sqrt(2) -TMatrix = np.array( - [[1.0, 0.0], - [0.0, np.exp(1.0j * np.pi / 4)]], dtype=complex) +XMatrix = np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex) +YMatrix = np.array([[0.0, -1.0j], [1.0j, 0.0]], dtype=complex) +ZMatrix = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex) +SMatrix = np.array([[1.0, 0.0], [0.0, 1.0j]], dtype=complex) +SXMatrix = np.array([[1.0, 1.0j], [1.0j, 1.0]], dtype=complex) / np.sqrt(2) +SYMatrix = np.array([[1.0, -1.0], [1.0, 1.0]], dtype=complex) / np.sqrt(2) +TMatrix = np.array([[1.0, 0.0], [0.0, np.exp(1.0j * np.pi / 4)]], dtype=complex) WMatrix = (XMatrix + YMatrix) / np.sqrt(2) SWMatrix = np.array( - [[0.5 + 0.5j, -np.sqrt(0.5) * 1j], - [np.sqrt(0.5), 0.5 + 0.5j]], dtype=complex) + [[0.5 + 0.5j, -np.sqrt(0.5) * 1j], [np.sqrt(0.5), 0.5 + 0.5j]], dtype=complex +) HMatrix = (XMatrix + ZMatrix) / np.sqrt(2) SwapMatrix = np.array( [ @@ -196,12 +182,28 @@ def u3matrix(_theta=0.0, _phi=0.0, _lambda=0.0): ) -mat_dict = {'id': IdMatrix, 'x': XMatrix, 'y': YMatrix, 'z': ZMatrix, - 'h': HMatrix, 'w': WMatrix, - 's': SMatrix, 't': TMatrix, - 'cx': CXMatrix, 'cnot': CXMatrix, 'cy': CYMatrix, 'cz': CZMatrix, - 'swap': SwapMatrix, 'iswap': ISwapMatrix, - 'rx': rx_mat, 'ry': ry_mat, 'rz': rz_mat, 'p': pmatrix, - 'rxx': rxx_mat, 'ryy': ryy_mat, 'rzz': rzz_mat, - 'u2': u2matrix, 'u3': u3matrix - } +mat_dict = { + "id": IdMatrix, + "x": XMatrix, + "y": YMatrix, + "z": ZMatrix, + "h": HMatrix, + "w": WMatrix, + "s": SMatrix, + "t": TMatrix, + "cx": CXMatrix, + "cnot": CXMatrix, + "cy": CYMatrix, + "cz": CZMatrix, + "swap": SwapMatrix, + "iswap": ISwapMatrix, + "rx": rx_mat, + "ry": ry_mat, + "rz": rz_mat, + "p": pmatrix, + "rxx": rxx_mat, + "ryy": ryy_mat, + "rzz": rzz_mat, + "u2": u2matrix, + "u3": u3matrix, +} diff --git a/quafu/elements/noise.py b/quafu/elements/noise.py index cb83c78..91932bb 100644 --- a/quafu/elements/noise.py +++ b/quafu/elements/noise.py @@ -1,27 +1,30 @@ -from .quantum_gate import QuantumGate -from .instruction import Instruction -from .element_gates import XGate, YGate, ZGate, IdGate -import numpy as np import copy +import numpy as np + +from .element_gates import IdGate, XGate, YGate, ZGate +from .instruction import Instruction +from .quantum_gate import QuantumGate + + class KrausChannel(Instruction): - def __init__(self, name, pos:int, gatelist:list[QuantumGate]=[]): + def __init__(self, name, pos: int, gatelist: list[QuantumGate] = []): self._name = name self._pos = [pos] self.gatelist = gatelist @property def name(self): - return self._name - + return self._name + @name.setter def name(self, __name): self._name = __name - + @property def pos(self): - return self._pos - + return self._pos + @pos.setter def pos(self, __pos): self._pos = copy.deepcopy(__pos) @@ -29,33 +32,38 @@ def pos(self, __pos): @property def named_pos(self): return {"pos": self.pos} - - @property + + @property def named_paras(self): return {"paras": self.paras} - + def to_qasm(self, with_para): raise ValueError("Can not convert noise channel to qasm") @property def symbol(self): if len(self.paras) > 0: - symbol = "%s(" %self.name + ",".join(["%.3f" %para for para in self.paras]) + ")" + symbol = ( + "%s(" % self.name + + ",".join(["%.3f" % para for para in self.paras]) + + ")" + ) return symbol else: - return "%s" %self.name - + return "%s" % self.name + def __repr__(self): return self.symbol - + + class UnitaryChannel(KrausChannel): - def __init__(self, name, pos:int, gatelist:list[QuantumGate]=[], probs = []): + def __init__(self, name, pos: int, gatelist: list[QuantumGate] = [], probs=[]): super().__init__(name, pos, gatelist) self.probs = probs - + def gen_gate(self): """ - Randomly choose a gate + Randomly choose a gate """ return np.random.choice(self.gatelist, p=self.probs) @@ -63,57 +71,71 @@ def gen_gate(self): @Instruction.register() class BitFlip(UnitaryChannel): name = "BitFlip" - def __init__(self, pos:int, p): + + def __init__(self, pos: int, p): self.pos = [pos] - self.paras = [p] + self.paras = [p] self.gatelist = [XGate(pos), IdGate(pos)] self.probs = [p, 1 - p] - + + @Instruction.register() class Dephasing(UnitaryChannel): name = "Dephasing" - def __init__(self, pos:int, p): + + def __init__(self, pos: int, p): self.pos = [pos] self.paras = [p] self.gatelist = [ZGate(pos), IdGate(pos)] self.probs = [p, 1 - p] + @Instruction.register() class Depolarizing(UnitaryChannel): name = "Depolarizing" - def __init__(self, pos:int, p): + + def __init__(self, pos: int, p): self.pos = [pos] self.paras = [p] self.gatelist = [XGate(pos), YGate(pos), ZGate(pos), IdGate(pos)] self.probs = [p / 3, p / 3, p / 3, 1 - p] + @Instruction.register() class AmplitudeDamping(KrausChannel): name = "AmpDamping" - def __init__(self, pos:int, p): + + def __init__(self, pos: int, p): self.pos = [pos] self.paras = [p] - dampmat0 = np.array([[1.0, 0.0], [0.0, np.sqrt(1-p)]], dtype=complex) - dampgate0 = QuantumGate("AMPDAMP0", [pos], [], dampmat0) + dampmat0 = np.array([[1.0, 0.0], [0.0, np.sqrt(1 - p)]], dtype=complex) + dampgate0 = QuantumGate("AMPDAMP0", [pos], [], dampmat0) dampmat1 = np.array([[0.0, np.sqrt(p)], [0.0, 0.0]], dtype=complex) - dampgate1 = QuantumGate("AMPDAMP1", [pos], [], dampmat1) + dampgate1 = QuantumGate("AMPDAMP1", [pos], [], dampmat1) self.gatelist = [dampgate0, dampgate1] + @Instruction.register() class Decoherence(KrausChannel): name = "Decoherence" + def __init__(self, pos, t, T1, T2): """ t: time of quantum operation T1: energy decay time - T2: dephasing time + T2: dephasing time """ self.pos = [pos] self.paras = [t, T1, T2] - kmat0 = np.array([[1.0, 0.0], [0.0, np.exp(-t/T2)]], dtype=complex) - kmat1 = np.array([[0.0, np.sqrt(1-np.exp(-t/T1))], [0.0, 0.0]], dtype=complex) - kmat2 = np.array([[1.0, 0.0], [0.0, np.sqrt(np.exp(-t/T1)-np.exp(-2*t/T2))]], dtype=complex) - kgate0 = QuantumGate("DECAY0", [pos], [], kmat0) - kgate1 = QuantumGate("DECAY1", [pos], [], kmat1) - kgate2 = QuantumGate("DECAY2", [pos], [], kmat2) - self.gatelist = [kgate0, kgate1, kgate2] \ No newline at end of file + kmat0 = np.array([[1.0, 0.0], [0.0, np.exp(-t / T2)]], dtype=complex) + kmat1 = np.array( + [[0.0, np.sqrt(1 - np.exp(-t / T1))], [0.0, 0.0]], dtype=complex + ) + kmat2 = np.array( + [[1.0, 0.0], [0.0, np.sqrt(np.exp(-t / T1) - np.exp(-2 * t / T2))]], + dtype=complex, + ) + kgate0 = QuantumGate("DECAY0", [pos], [], kmat0) + kgate1 = QuantumGate("DECAY1", [pos], [], kmat1) + kgate2 = QuantumGate("DECAY2", [pos], [], kmat2) + self.gatelist = [kgate0, kgate1, kgate2] diff --git a/quafu/elements/oracle.py b/quafu/elements/oracle.py index 1075f01..eefe0e1 100644 --- a/quafu/elements/oracle.py +++ b/quafu/elements/oracle.py @@ -16,7 +16,7 @@ from abc import ABCMeta from typing import Dict, Iterable, List -from quafu.elements import Instruction, QuantumGate, ControlledGate +from quafu.elements import ControlledGate, Instruction, QuantumGate class OracleGateMeta(ABCMeta): diff --git a/quafu/elements/quantum_gate.py b/quafu/elements/quantum_gate.py index e9f5262..50b6d99 100644 --- a/quafu/elements/quantum_gate.py +++ b/quafu/elements/quantum_gate.py @@ -14,8 +14,7 @@ import copy -from abc import ABC, abstractmethod -from typing import Callable, Dict, Iterable, List, Optional, Union +from typing import Callable, Dict, List, Optional, Union import numpy as np from numpy import ndarray @@ -25,11 +24,7 @@ from .parameters import ParameterType from .utils import extract_float, handle_expression -__all__ = [ - "QuantumGate", - "ControlledGate", - "ControlledU" -] +__all__ = ["QuantumGate", "ControlledGate", "ControlledU"] HERMITIAN = [ "id", @@ -76,11 +71,11 @@ class QuantumGate(Instruction): gate_classes = {} def __init__( - self, - name: str, - pos: List[int], - paras: List[ParameterType] = [], - matrix: Optional[Union[ndarray, Callable]] = None, + self, + name: str, + pos: List[int], + paras: List[ParameterType] = [], + matrix: Optional[Union[ndarray, Callable]] = None, ): super().__init__(pos, paras) self._name = name @@ -109,16 +104,16 @@ def __repr__(self): @property def name(self): - return self._name - + return self._name + @name.setter def name(self, __name): self._name = __name - + @property def pos(self): - return self._pos - + return self._pos + @pos.setter def pos(self, __pos): self._pos = copy.deepcopy(__pos) @@ -126,11 +121,11 @@ def pos(self, __pos): @property def named_pos(self) -> Dict: return {"pos": self.pos} - - @property + + @property def named_paras(self) -> Dict: return {"paras": self.paras} - + @classmethod def register_gate(cls, subclass, name: str = None): """Register a new gate class into gate_classes. @@ -167,10 +162,14 @@ def symbol(self) -> str: """Symbol used in text-drawing.""" # TODO: Use latex repr for Parameter if len(self.paras) > 0: - symbol = "%s(" %self.name + ",".join(["%.3f" %para for para in self._paras]) + ")" + symbol = ( + "%s(" % self.name + + ",".join(["%.3f" % para for para in self._paras]) + + ")" + ) return symbol else: - return "%s" %self.name + return "%s" % self.name @symbol.setter def symbol(self, symbol: str): @@ -186,7 +185,7 @@ def matrix(self): return reorder_matrix(raw_mat, self.pos) else: return raw_mat - + def _get_raw_matrix(self, reverse_order=False): raw_mat = self._raw_matrix if isinstance(self._raw_matrix, Callable): @@ -194,16 +193,19 @@ def _get_raw_matrix(self, reverse_order=False): if reverse_order and len(self.pos) > 1: return reorder_matrix(raw_mat, np.arange(len(self.pos))[::-1]) else: - return raw_mat - - + return raw_mat + def to_qasm(self, with_para=False) -> str: """OPENQASM 2.0""" # TODO: support register naming - qstr = "%s" %self.name.lower() + qstr = "%s" % self.name.lower() if self.paras: if with_para: - qstr += "(" + ",".join(["%s" % handle_expression(para) for para in self.paras]) + ")" + qstr += ( + "(" + + ",".join(["%s" % handle_expression(para) for para in self.paras]) + + ")" + ) else: qstr += "(" + ",".join(["%s" % para for para in self._paras]) + ")" qstr += " " @@ -217,16 +219,16 @@ def update_params(self, paras: Union[ParameterType, List[ParameterType]]): if isinstance(paras, list): self.paras = paras else: - self.paras = [paras] + self.paras = [paras] # # # # # # # # # # # # algebraic operations # # # # # # # # # # # # def power(self, n) -> "QuantumGate": """Return another gate equivalent to n-times operation of the present gate.""" name = self.name.lower() - name = 'sz' if name == 's' else name + name = "sz" if name == "s" else name - order4 = ["sx", "sy", "s", 'sw'] - order4 += [_+'dg' for _ in order4] + order4 = ["sx", "sy", "s", "sw"] + order4 += [_ + "dg" for _ in order4] order8 = ["t", "tdg"] if name in HERMITIAN: @@ -240,7 +242,7 @@ def power(self, n) -> "QuantumGate": elif n % 4 == 1: return copy.deepcopy(self) elif n % 4 == 2: - _square_name = 'z' if name == 's' else name[1:] + _square_name = "z" if name == "s" else name[1:] return self.gate_classes[_square_name](self.pos) elif n % 4 == 3: _conj_name = PAIRED[name] @@ -256,10 +258,12 @@ def power(self, n) -> "QuantumGate": args = self.pos + [self.paras[0] * n] return self.gate_classes[name](*args) else: - name = self.name + "^%d" %n - raw_matrix = self._raw_matrix + name = self.name + "^%d" % n + raw_matrix = self._raw_matrix if isinstance(self._raw_matrix, Callable): - raw_matrix = lambda paras: np.linalg.matrix_power(self._raw_matrix(paras), n) + raw_matrix = lambda paras: np.linalg.matrix_power( + self._raw_matrix(paras), n + ) else: raw_matrix = np.linalg.matrix_power(self._raw_matrix, n) return QuantumGate(name, self.pos, self.paras, raw_matrix) @@ -277,7 +281,7 @@ def dagger(self) -> "QuantumGate": return self.gate_classes[_conj_name](self.pos) else: name = self.name + "^†" - raw_matrix = self._raw_matrix + raw_matrix = self._raw_matrix if isinstance(self._raw_matrix, Callable): raw_matrix = lambda paras: self._raw_matrix(paras).conj().T else: @@ -299,10 +303,10 @@ def ctrl_by(self, ctrls: Union[int, List[int]]) -> "ControlledGate": ctrls = list(set(self.ctrls) | set(ctrls)) elif set(ctrls) & set(pos): raise ValueError("Control qubits should not be overlap with target qubits.") - - #named controlled gate + + # named controlled gate args = self.pos + self.paras - if self.name in ["X" ,"Y" ,"Z", "RX", "RY", "RZ"]: + if self.name in ["X", "Y", "Z", "RX", "RY", "RZ"]: args = self.pos + self.paras if len(ctrls) == 1: # single-ctrl gate cname = "c" + name @@ -312,55 +316,65 @@ def ctrl_by(self, ctrls: Union[int, List[int]]) -> "ControlledGate": cname = "mc" + name cop = QuantumGate.gate_classes[cname](ctrls, *args) return cop - elif self.name in ["CX" ,"CY" ,"CZ", "CRX", "CRY", "CRZ"]: - args= self.targs + self.paras + elif self.name in ["CX", "CY", "CZ", "CRX", "CRY", "CRZ"]: + args = self.targs + self.paras cname = "m" + name cop = QuantumGate.gate_classes[cname](ctrls, *args) return cop - else: #unnamed controlled gate + else: # unnamed controlled gate if isinstance(self, ControlledGate): - cop = ControlledGate("MC"+self._targ_name, self._targ_name, ctrls+self.ctrls, self.targs, self.paras, self._targ_matrix) + cop = ControlledGate( + "MC" + self._targ_name, + self._targ_name, + ctrls + self.ctrls, + self.targs, + self.paras, + self._targ_matrix, + ) return cop else: if len(ctrls) == 1: - return ControlledU("MC"+name, ctrls, self) + return ControlledU("MC" + name, ctrls, self) else: - return ControlledU("MC"+name, ctrls, self) + return ControlledU("MC" + name, ctrls, self) class ControlledGate(QuantumGate): """Controlled gate class, where the matrix act non-trivially on target qubits""" def __init__( - self, - name: str, - targ_name: str, - ctrls: List[int], - targs: List[int], - paras: List[float] = [], - targ_matrix: MatrixType = None, + self, + name: str, + targ_name: str, + ctrls: List[int], + targs: List[int], + paras: List[float] = [], + targ_matrix: MatrixType = None, ): self.ctrls = copy.deepcopy(ctrls) self.targs = copy.deepcopy(targs) self._targ_name = targ_name - super().__init__(name, ctrls+targs, paras, targ_matrix) + super().__init__(name, ctrls + targs, paras, targ_matrix) self._targ_matrix = targ_matrix self._raw_matrix = self._rawmatfunc @property def symbol(self): if len(self.paras) > 0: - symbol = "%s(" %self._targ_name + ",".join(["%.3f" %para for para in self._paras]) + ")" + symbol = ( + "%s(" % self._targ_name + + ",".join(["%.3f" % para for para in self._paras]) + + ")" + ) return symbol else: - return "%s" %self._targ_name - - - def _rawmatfunc(self, paras:List[float]): - targ_dim = 2**(len(self.targs)) + return "%s" % self._targ_name + + def _rawmatfunc(self, paras: List[float]): + targ_dim = 2 ** (len(self.targs)) qnum = len(self.pos) - dim = 2**(qnum) - raw_matrix = np.zeros((dim , dim), dtype=complex) + dim = 2 ** (qnum) + raw_matrix = np.zeros((dim, dim), dtype=complex) targ_matrix = self._targ_matrix if isinstance(self._targ_matrix, Callable): targ_matrix = self._targ_matrix(paras) @@ -368,52 +382,84 @@ def _rawmatfunc(self, paras:List[float]): if targ_matrix.shape[0] != targ_dim: raise ValueError("Dimension dismatch") else: - control_dim = 2**len(self.pos) - targ_dim + control_dim = 2 ** len(self.pos) - targ_dim for i in range(control_dim): - raw_matrix[i, i] = 1. - + raw_matrix[i, i] = 1.0 + raw_matrix[control_dim:, control_dim:] = targ_matrix return raw_matrix - + def power(self, n) -> "ControlledGate": - #TODO:Use implementation in QuantumGate via Controlled + # TODO:Use implementation in QuantumGate via Controlled name = self.name if self._targ_name in ["RX", "RY", "RZ", "P", "RZZ", "RXX", "RYY"]: - return ControlledGate(name, self._targ_name, self.ctrls, self.targs, [self.paras[0] * n], self._targ_matrix) + return ControlledGate( + name, + self._targ_name, + self.ctrls, + self.targs, + [self.paras[0] * n], + self._targ_matrix, + ) else: - name = self.name + "^%d" %n - targ_matrix = self._targ_matrix + name = self.name + "^%d" % n + targ_matrix = self._targ_matrix if isinstance(self._targ_matrix, Callable): - targ_matrix = lambda paras: np.linalg.matrix_power(self._targ_matrix(paras), n) + targ_matrix = lambda paras: np.linalg.matrix_power( + self._targ_matrix(paras), n + ) else: targ_matrix = np.linalg.matrix_power(self._targ_matrix, n) - return ControlledGate(name, self._targ_name+"^%d" %n, self.ctrls, self.targs, self.paras, targ_matrix) - - + return ControlledGate( + name, + self._targ_name + "^%d" % n, + self.ctrls, + self.targs, + self.paras, + targ_matrix, + ) + def dagger(self) -> "ControlledGate": - #TODO:Use implementation in QuantumGate via ControlledU + # TODO:Use implementation in QuantumGate via ControlledU name = self.name if self._targ_name in ["RX", "RY", "RZ", "P", "RZZ", "RXX", "RYY"]: - return ControlledGate(name, self._targ_name, self.ctrls, self.targs, [-self.paras[0]], self._targ_matrix) + return ControlledGate( + name, + self._targ_name, + self.ctrls, + self.targs, + [-self.paras[0]], + self._targ_matrix, + ) else: name = self.name + "^†" - targ_matrix = self._targ_matrix + targ_matrix = self._targ_matrix if isinstance(self._targ_matrix, Callable): targ_matrix = lambda paras: self._targ_matrix(paras).conj().T else: targ_matrix = targ_matrix.conj().T - return ControlledGate(name, self._targ_name+"^†", self.ctrls, self.targs, self.paras, targ_matrix) - + return ControlledGate( + name, + self._targ_name + "^†", + self.ctrls, + self.targs, + self.paras, + targ_matrix, + ) @property def symbol(self): if len(self.paras) > 0: - symbol = "%s(" %self._targ_name + ",".join(["%.3f" %para for para in self._paras]) + ")" + symbol = ( + "%s(" % self._targ_name + + ",".join(["%.3f" % para for para in self._paras]) + + ")" + ) return symbol else: - return "%s" %self._targ_name + return "%s" % self._targ_name @property def ct_nums(self): @@ -421,16 +467,15 @@ def ct_nums(self): ctrl_num = len(self.ctrls) num = targ_num + ctrl_num return ctrl_num, targ_num, num - + def _get_targ_matrix(self, reverse_order=False): targ_mat = self._targ_matrix if isinstance(self._targ_matrix, Callable): targ_mat = self._targ_matrix(self._paras) - if reverse_order and (len(self.targs) > 1): + if reverse_order and (len(self.targs) > 1): return reorder_matrix(targ_mat, np.array(range(len(self.targs))[::-1])) else: return targ_mat - @property def named_pos(self) -> Dict: @@ -445,36 +490,39 @@ def from_target(cls, targ: QuantumGate, ctrls: List[int]): """Shoud use controlledU""" return cls(targ.name, ctrls, targ.pos, targ.paras, targ._raw_matrix) + class ControlledU(ControlledGate): def __init__(self, name, ctrls: List[int], U: QuantumGate): self.targ_gate = U targs = U.pos - super().__init__(name, U.name, ctrls, targs, U.paras, targ_matrix=self.targ_gate._raw_matrix) + super().__init__( + name, U.name, ctrls, targs, U.paras, targ_matrix=self.targ_gate._raw_matrix + ) + class CircuitWrapper(QuantumGate): - def __init__(self, name:str, circ, qbits=[]): - self.name = name + def __init__(self, name: str, circ, qbits=[]): + self.name = name self.pos = list(range(circ.num)) self.circuit = copy.deepcopy(circ) - if qbits: self._reallocate(qbits) - + def _reallocate(self, qbits): - num = max(self.circuit.num-1, max(qbits))+1 + num = max(self.circuit.num - 1, max(qbits)) + 1 self.pos = qbits self.circuit._reallocate(num, qbits) - + @property def symbol(self): - return "%s" %self.name + return "%s" % self.name - def add_controls(self, ctrls:List[int]=[]) -> QuantumGate: - return ControlledCircuitWrapper("MC"+self.name, self, ctrls) + def add_controls(self, ctrls: List[int] = []) -> QuantumGate: + return ControlledCircuitWrapper("MC" + self.name, self, ctrls) - def power(self, n:int): - self.name +="^%d" %n + def power(self, n: int): + self.name += "^%d" % n self.circuit = self.circuit.power(n) return self @@ -484,13 +532,14 @@ def dagger(self): return self def to_qasm(self, with_para=False): - qasm = '' + qasm = "" for operation in self.circuit.operations: qasm += operation.to_qasm(with_para) + ";\n" return qasm - + + class ControlledCircuitWrapper(CircuitWrapper): - def __init__(self, name:str, circwrp:CircuitWrapper, ctrls:List[int]): + def __init__(self, name: str, circwrp: CircuitWrapper, ctrls: List[int]): self.name = name self.ctrls = ctrls self.targs = circwrp.pos @@ -498,26 +547,25 @@ def __init__(self, name:str, circwrp:CircuitWrapper, ctrls:List[int]): self.pos = list(range(self.circuit.num)) self._targ_name = circwrp.name - @property def symbol(self): - return "%s" %self._targ_name + return "%s" % self._targ_name def power(self, n: int): - self._targ_name += "^%d" %n + self._targ_name += "^%d" % n return super().power(n) def dagger(self): self.name += "^†" return super().dagger() - + def _reallocate(self, qbits): - num = max(self.circuit.num-1, max(qbits))+1 + num = max(self.circuit.num - 1, max(qbits)) + 1 self.pos = qbits self.circuit._reallocate(num, qbits) qbits_map = dict(zip(range(len(qbits)), qbits)) for i in range(len(self.ctrls)): self.ctrls[i] = qbits_map[self.ctrls[i]] - + for i in range(len(self.targs)): - self.targs[i] = qbits_map[self.targs[i]] \ No newline at end of file + self.targs[i] = qbits_map[self.targs[i]] diff --git a/quafu/elements/unitary/decomposer.py b/quafu/elements/unitary/decomposer.py index 45d0b58..55977be 100644 --- a/quafu/elements/unitary/decomposer.py +++ b/quafu/elements/unitary/decomposer.py @@ -4,9 +4,9 @@ import numpy as np import scipy from numpy import ndarray - -from quafu.elements.matrices import rz_mat, ry_mat, CXMatrix +from quafu.elements.matrices import CXMatrix from quafu.elements.matrices import mat_utils as mu +from quafu.elements.matrices import ry_mat, rz_mat class UnitaryDecomposer(object): diff --git a/quafu/elements/utils.py b/quafu/elements/utils.py index 19c4b79..7442ea9 100644 --- a/quafu/elements/utils.py +++ b/quafu/elements/utils.py @@ -13,11 +13,13 @@ # limitations under the License. -from typing import Iterable, List, Union -import numpy as np -from quafu.elements.parameters import ParameterType, Parameter, ParameterExpression +from typing import Iterable, List + import _operator import autograd.numpy as anp +import numpy as np +from quafu.elements.parameters import Parameter, ParameterExpression, ParameterType + def reorder_matrix(matrix: np.ndarray, pos: List): """Reorder the input sorted matrix to the pos order""" @@ -40,6 +42,7 @@ def extract_float(paras): paras_f.append(para.get_value()) return paras_f + def handle_expression(param: ParameterType): if isinstance(param, float) or isinstance(param, int): return param diff --git a/quafu/qfasm/qfasm_parser.py b/quafu/qfasm/qfasm_parser.py index 0be90e3..818b048 100644 --- a/quafu/qfasm/qfasm_parser.py +++ b/quafu/qfasm/qfasm_parser.py @@ -19,16 +19,26 @@ from quafu.circuits.classical_register import ClassicalRegister from quafu.circuits.quantum_register import QuantumRegister from quafu.elements import * +from quafu.elements import Parameter, ParameterExpression from quafu.elements.classical_element import Cif from quafu.qfasm.exceptions import ParserError from quafu import QuantumCircuit -from quafu.elements import Parameter, ParameterExpression + from .qfasm_lexer import QfasmLexer from .qfasm_utils import * -unaryop = {"sin": "sin", "cos": "cos", "tan": "tan", "exp": "exp", - "ln": "log", "sqrt": "sqrt", "acos": "arccos", "atan": "arctan", "asin": "arcsin"} +unaryop = { + "sin": "sin", + "cos": "cos", + "tan": "tan", + "exp": "exp", + "ln": "log", + "sqrt": "sqrt", + "acos": "arccos", + "atan": "arctan", + "asin": "arcsin", +} unarynp = { "sin": np.sin, "cos": np.cos, @@ -192,7 +202,9 @@ def handle_gateins(self, gateins: GateInstruction): gate_list.append(gate_classes["ry"](*[*oneargs, gateins.cargs[0]])) gate_list.append(gate_classes["rz"](*[*oneargs, gateins.cargs[1]])) elif gateins.name in self.mulctrl: - gate_list.append(gate_classes[gateins.name](oneargs[:-1], oneargs[-1])) + gate_list.append( + gate_classes[gateins.name](oneargs[:-1], oneargs[-1]) + ) else: # add carg to args if there is if gateins.cargs is not None and len(gateins.cargs) > 0: @@ -266,9 +278,13 @@ def handle_gateins(self, gateins: GateInstruction): return gate_list - def compute_exp(self, carg, cargdict: dict={}): + def compute_exp(self, carg, cargdict: dict = {}): # recurse - if isinstance(carg, int) or isinstance(carg, float) or isinstance(carg, ParameterExpression): + if ( + isinstance(carg, int) + or isinstance(carg, float) + or isinstance(carg, ParameterExpression) + ): return carg # if it's id, should get real number from gateins elif isinstance(carg, Id): @@ -299,7 +315,7 @@ def compute_exp(self, carg, cargdict: dict={}): elif carg.type == "/": return cargl / cargr elif carg.type == "^": - return cargl ** cargr + return cargl**cargr def addInstruction(self, qc: QuantumCircuit, ins): if ins is None: @@ -422,7 +438,9 @@ def check_param(self, carg): if isinstance(carg, int) or isinstance(carg, float): return elif isinstance(carg, Id) and carg.name not in self.params: - raise ParserError(f"The parameter {carg.name} is undefined at line {carg.lineno} file {carg.filename}") + raise ParserError( + f"The parameter {carg.name} is undefined at line {carg.lineno} file {carg.filename}" + ) elif isinstance(carg, UnaryExpr): self.check_param(carg.children[0]) elif isinstance(carg, BinaryExpr): @@ -590,7 +608,9 @@ def p_statement_qop(self, p): | qif error """ if p[2] != ";": - raise ParserError(f"Expecting ';' behind statement at line {p[1].lineno} file {p[1].filename}") + raise ParserError( + f"Expecting ';' behind statement at line {p[1].lineno} file {p[1].filename}" + ) p[0] = p[1] def p_statement_empty(self, p): @@ -1011,10 +1031,14 @@ def p_statement_defparam(self, p): | id EQUAL error """ if not isinstance(p[3], int) and not isinstance(p[3], float): - raise ParserError(f"Expecting 'INT' or 'FLOAT behind '=' at line {p[1].lineno} file {p[1].filename}") + raise ParserError( + f"Expecting 'INT' or 'FLOAT behind '=' at line {p[1].lineno} file {p[1].filename}" + ) param_name = p[1].name if param_name in self.params: - raise ParserError(f"Duplicate declaration for parameter {p[1].name} at line {p[1].lineno} file {p[1].filename}") + raise ParserError( + f"Duplicate declaration for parameter {p[1].name} at line {p[1].lineno} file {p[1].filename}" + ) self.params[param_name] = Parameter(param_name, p[3]) p[0] = None diff --git a/quafu/simulators/default_simulator.py b/quafu/simulators/default_simulator.py index 25da5da..53edf65 100644 --- a/quafu/simulators/default_simulator.py +++ b/quafu/simulators/default_simulator.py @@ -21,12 +21,7 @@ from scipy.sparse import coo_matrix, eye, kron from sparse import COO, SparseArray -from ..elements import ( - Barrier, - Delay, - QuantumGate, - XYResonance, -) +from ..elements import Barrier, Delay, QuantumGate, XYResonance from ..results.results import SimuResult diff --git a/quafu/simulators/simulator.py b/quafu/simulators/simulator.py index f8ce1db..2d53235 100644 --- a/quafu/simulators/simulator.py +++ b/quafu/simulators/simulator.py @@ -13,23 +13,33 @@ # limitations under the License. """simulator for quantum circuit""" -from ..elements import CircuitWrapper, QuantumGate, KrausChannel, UnitaryChannel -from ..circuits import QuantumCircuit -from abc import ABC, abstractmethod -from .qfvm import simulate_circuit, applyop_statevec, expect_statevec, sampling_statevec,simulate_circuit_clifford +from abc import ABC, abstractmethod + import numpy as np + +from ..algorithms.hamiltonian import Hamiltonian +from ..circuits import QuantumCircuit +from ..elements import CircuitWrapper, KrausChannel, QuantumGate, UnitaryChannel from ..exceptions import QuafuError from ..results.results import SimuResult -from ..algorithms.hamiltonian import Hamiltonian +from .qfvm import ( + applyop_statevec, + expect_statevec, + sampling_statevec, + simulate_circuit, + simulate_circuit_clifford, +) + class Simulator(ABC): @abstractmethod def run(self): raise NotImplementedError - + + class SVSimulator(Simulator): - def __init__(self, use_gpu:bool=False, use_custatevec:bool=False): - self.use_gpu = use_gpu + def __init__(self, use_gpu: bool = False, use_custatevec: bool = False): + self.use_gpu = use_gpu self.use_custatevec = use_custatevec def config(self, **kwargs): @@ -40,8 +50,8 @@ def config(self, **kwargs): raise ValueError("No such attribute") def _apply_op(self, op, psi): - #TODO: support GPU - if isinstance(op,CircuitWrapper): + # TODO: support GPU + if isinstance(op, CircuitWrapper): psi = self.run(op.circuit, psi)["statevector"] return psi elif isinstance(op, QuantumGate): @@ -50,7 +60,7 @@ def _apply_op(self, op, psi): elif isinstance(op, KrausChannel): temppsi = np.copy(psi) norm0 = np.linalg.norm(psi) - s = 0. + s = 0.0 r = np.random.rand() for kop in op.gatelist: temppsi = applyop_statevec(kop, temppsi) @@ -65,9 +75,9 @@ def _apply_op(self, op, psi): return psi else: raise NotImplementedError - + def _apply_hamil(self, hamil, psi): - psi_out = np.zeros(len(psi), dtype=complex) + psi_out = np.zeros(len(psi), dtype=complex) for pauli in hamil.paulis: psi1 = np.copy(psi) for name, pos in zip(pauli.paulistr, pauli.pos): @@ -75,14 +85,22 @@ def _apply_hamil(self, hamil, psi): psi1 = self._apply_op(op, psi1) psi1 = psi1 * pauli.coeff psi_out += psi1 - + return psi_out - def run(self, qc : QuantumCircuit, psi : np.ndarray= np.array([]), shots:int=0, hamiltonian:Hamiltonian=None): + def run( + self, + qc: QuantumCircuit, + psi: np.ndarray = np.array([]), + shots: int = 0, + hamiltonian: Hamiltonian = None, + ): res_info = {} if qc.noised: - raise QuafuError("Can not run noisy circuits with statevector simulator, please use the noisy version.") - + raise QuafuError( + "Can not run noisy circuits with statevector simulator, please use the noisy version." + ) + if self.use_gpu: if qc.executable_on_backend == False: raise QuafuError("classical operation do not support gpu currently") @@ -117,15 +135,16 @@ def run(self, qc : QuantumCircuit, psi : np.ndarray= np.array([]), shots:int=0, else: res_info["pauli_expects"] = [] res_info["qbitnum"] = qc.num - res_info["measures"] = qc.measures + res_info["measures"] = qc.measures res_info["simulator"] = "statevector" return SimuResult(res_info) + class NoiseSVSimulator(Simulator): - def __init__(self, use_gpu:bool=False, use_custatevec:bool=False): + def __init__(self, use_gpu: bool = False, use_custatevec: bool = False): self.backend = SVSimulator(use_gpu=use_gpu, use_custatevec=use_custatevec) - def run_once(self, qc : QuantumCircuit, psi, hamiltonian=None): + def run_once(self, qc: QuantumCircuit, psi, hamiltonian=None): newqc = self.gen_circuit(qc) for op in newqc.instructions: psi = self.backend._apply_op(op, psi) @@ -139,10 +158,16 @@ def run_once(self, qc : QuantumCircuit, psi, hamiltonian=None): return sample, res return sample, None - def run(self, qc:QuantumCircuit, psi : np.ndarray= np.array([]), shots:int=0, hamiltonian:Hamiltonian=None): + def run( + self, + qc: QuantumCircuit, + psi: np.ndarray = np.array([]), + shots: int = 0, + hamiltonian: Hamiltonian = None, + ): counts = {} - pauli_expects = 0. - + pauli_expects = 0.0 + for _ in range(shots): if not psi: tpsi = np.zeros(2**qc.num, dtype=complex) @@ -161,9 +186,9 @@ def run(self, qc:QuantumCircuit, psi : np.ndarray= np.array([]), shots:int=0, h pauli_expects = [] else: pauli_expects = list(pauli_expects) - res_info = {"counts":counts, "pauli_expects": pauli_expects} + res_info = {"counts": counts, "pauli_expects": pauli_expects} res_info["qbitnum"] = qc.num - res_info["measures"] = qc.measures + res_info["measures"] = qc.measures res_info["simulator"] = "noisy statevector" return SimuResult(res_info) @@ -171,7 +196,7 @@ def run(self, qc:QuantumCircuit, psi : np.ndarray= np.array([]), shots:int=0, h def gen_circuit(qc): """ sample circuit from noise circuit - """ + """ num = qc.num new_qc = QuantumCircuit(num) temp_qc = QuantumCircuit(num) @@ -188,12 +213,13 @@ def gen_circuit(qc): elif isinstance(op, KrausChannel): new_qc << temp_qc.wrap() new_qc << op - temp_qc = QuantumCircuit(num) + temp_qc = QuantumCircuit(num) new_qc << temp_qc.wrap() return new_qc - + + class CliffordSimulator(Simulator): - def run(self, qc : QuantumCircuit, shots:int=0): + def run(self, qc: QuantumCircuit, shots: int = 0): res_info = {} count_dict = simulate_circuit_clifford(qc, shots) res_info["qbitnum"] = qc.num @@ -201,4 +227,3 @@ def run(self, qc : QuantumCircuit, shots:int=0): res_info["measures"] = qc.measures res_info["simuator"] = "clifford" return SimuResult(res_info) - diff --git a/quafu/synthesis/evolution.py b/quafu/synthesis/evolution.py index 900ac2c..795aa03 100644 --- a/quafu/synthesis/evolution.py +++ b/quafu/synthesis/evolution.py @@ -16,8 +16,8 @@ from abc import ABC, abstractmethod import numpy as np -from quafu.algorithms.hamiltonian import PauliOp import quafu.elements.element_gates as qeg +from quafu.algorithms.hamiltonian import PauliOp def single_qubit_evol(pauli: PauliOp, time: float): diff --git a/setup.py b/setup.py index 665fbf4..b99fa7a 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ long_description=long_description, long_description_content_type="text/markdown", extras_require={"test": ["pytest"]}, - cmake_args=['-DCMAKE_BUILD_TYPE:STRING=Debug'], + cmake_args=["-DCMAKE_BUILD_TYPE:STRING=Debug"], python_requires=">=3.8", zip_safe=False, setup_cfg=True, diff --git a/src/qfvm/circuit.hpp b/src/qfvm/circuit.hpp index ccc4bd0..b1d0ec7 100644 --- a/src/qfvm/circuit.hpp +++ b/src/qfvm/circuit.hpp @@ -6,36 +6,35 @@ #include #include - namespace py = pybind11; using namespace pybind11::literals; - class Circuit { - private: - uint qubit_num_; - vector> instructions_; - uint max_targe_num_; - uint cbit_num_; - // to sample count - vector> measure_vec_; - bool final_measure_ = true; +private: + uint qubit_num_; + vector> instructions_; + uint max_targe_num_; + uint cbit_num_; + // to sample count + vector> measure_vec_; + bool final_measure_ = true; - public: - Circuit(); - explicit Circuit(uint qubit_num); - explicit Circuit(vector> & ops); - Circuit(py::object const&pycircuit, bool get_full_mat=false, bool reverse=true); +public: + Circuit(); + explicit Circuit(uint qubit_num); + explicit Circuit(vector>& ops); + Circuit(py::object const& pycircuit, bool get_full_mat = false, + bool reverse = true); - void add_op(std::unique_ptr op); - void compress_instructions(); - uint qubit_num() const { return qubit_num_; } - uint cbit_num() const { return cbit_num_; } - uint max_targe_num() const { return max_targe_num_; } - bool final_measure() const { return final_measure_; } - vector gates(); - vector> measure_vec() { return measure_vec_; } - vector>& instructions() { return instructions_; } + void add_op(std::unique_ptr op); + void compress_instructions(); + uint qubit_num() const { return qubit_num_; } + uint cbit_num() const { return cbit_num_; } + uint max_targe_num() const { return max_targe_num_; } + bool final_measure() const { return final_measure_; } + vector gates(); + vector> measure_vec() { return measure_vec_; } + vector>& instructions() { return instructions_; } }; void Circuit::add_op(std::unique_ptr op) { @@ -72,54 +71,55 @@ vector Circuit::gates() { for (auto& op : instructions_) { if (std::find(classics.begin(), classics.end(), op->name()) == classics.end()) { - QuantumOperator *gate_ptr = dynamic_cast(op.get()); - if (gate_ptr != nullptr){ - QuantumOperator gate = *gate_ptr; //copy.may use shared_ptr - gates.push_back(gate); - } - else{ - std::cout << "Dynamic cast failed." << std::endl; - } + QuantumOperator* gate_ptr = dynamic_cast(op.get()); + if (gate_ptr != nullptr) { + QuantumOperator gate = *gate_ptr; // copy.may use shared_ptr + gates.push_back(gate); + } else { + std::cout << "Dynamic cast failed." << std::endl; + } } } return gates; } - -Circuit::Circuit(py::object const& pycircuit, bool get_full_mat, bool reverse) : max_targe_num_(0) { +Circuit::Circuit(py::object const& pycircuit, bool get_full_mat, bool reverse) + : max_targe_num_(0) { // auto pygates = pycircuit.attr("gates"); - auto pyops = pycircuit.attr("instructions"); - qubit_num_ = pycircuit.attr("num").cast(); //To consist with other simulators (e.g. qiskit) -// auto used_qubits = pycircuit.attr("used_qubits").cast>(); -// qubit_num_ = *std::max_element(used_qubits.begin(), used_qubits.end()) + 1; + auto pyops = pycircuit.attr("instructions"); + qubit_num_ = + pycircuit.attr("num") + .cast(); // To consist with other simulators (e.g. qiskit) + // auto used_qubits = pycircuit.attr("used_qubits").cast>(); + // qubit_num_ = *std::max_element(used_qubits.begin(), used_qubits.end()) + 1; cbit_num_ = pycircuit.attr("cbits_num").cast(); // judge wheather op qubit after measure bool measured = false; for (auto pyop_h : pyops) { py::object pyop = py::reinterpret_borrow(pyop_h); - if (py::hasattr(pyop, "circuit")){ //handle oracle - auto wrap_circuit = Circuit(pyop.attr("circuit"), get_full_mat, reverse); - for (auto& op : wrap_circuit.instructions()){ - instructions_.push_back(std::move(op)); - } - } - else{ - std::unique_ptr ins = from_pyops(pyop, get_full_mat, reverse); - // check_operator(*(ins)); - if (*ins){ - if (ins->name() == "measure"){ - measured = true; - // record qbit-cbit measure map - for (uint i = 0; i < ins->qbits().size(); i++) { - measure_vec_.push_back(std::make_pair(ins->qbits()[i], ins->cbits()[i])); - } - } - else{ - if (ins->targe_num() > max_targe_num_) - max_targe_num_ = ins->targe_num(); - if (measured == true) - final_measure_ = false; + if (py::hasattr(pyop, "circuit")) { // handle oracle + auto wrap_circuit = Circuit(pyop.attr("circuit"), get_full_mat, reverse); + for (auto& op : wrap_circuit.instructions()) { + instructions_.push_back(std::move(op)); + } + } else { + std::unique_ptr ins = + from_pyops(pyop, get_full_mat, reverse); + // check_operator(*(ins)); + if (*ins) { + if (ins->name() == "measure") { + measured = true; + // record qbit-cbit measure map + for (uint i = 0; i < ins->qbits().size(); i++) { + measure_vec_.push_back( + std::make_pair(ins->qbits()[i], ins->cbits()[i])); + } + } else { + if (ins->targe_num() > max_targe_num_) + max_targe_num_ = ins->targe_num(); + if (measured == true) + final_measure_ = false; } instructions_.push_back(std::move(ins)); } diff --git a/src/qfvm/instructions.hpp b/src/qfvm/instructions.hpp index d478906..68be044 100644 --- a/src/qfvm/instructions.hpp +++ b/src/qfvm/instructions.hpp @@ -1,15 +1,15 @@ #pragma once -#include "statevector.hpp" #include "qasm.hpp" +#include "statevector.hpp" +#include "types.hpp" +#include "util.h" +#include #include #include #include #include #include -#include -#include "types.hpp" -#include "util.h" namespace py = pybind11; using namespace pybind11::literals; @@ -18,256 +18,266 @@ class QuantumOperator; class Instruction { protected: - string name_ = "empty"; - vector positions_; - -public: - Instruction(){ }; - Instruction(string const&name, vector const& positions): - name_(name), - positions_(positions) { } - string name() const { return name_; } - vector positions() const { return positions_; } - explicit operator bool() const { - return !(name_ == "empty"); - } - - //interface - virtual vector paras() const {return {};} - virtual bool has_control() const{ return false; } - virtual bool is_real() const{ return false; } - virtual bool is_diag() const{ return false; } - virtual RowMatrixXcd targ_mat() const { return RowMatrixXcd(0,0);} - virtual RowMatrixXcd full_mat() const { return RowMatrixXcd(0,0); } - virtual uint control_num() const { return 0; } - virtual uint targe_num() const { return 0; } - - virtual vector qbits() const { return {}; } - virtual vector cbits() const { return {}; } - - virtual uint condition() const { return 0; } - virtual vector>& instructions() { vector> empty = {}; - return empty; } + string name_ = "empty"; + vector positions_; +public: + Instruction(){}; + Instruction(string const& name, vector const& positions) + : name_(name), positions_(positions) {} + string name() const { return name_; } + vector positions() const { return positions_; } + explicit operator bool() const { return !(name_ == "empty"); } + + // interface + virtual vector paras() const { return {}; } + virtual bool has_control() const { return false; } + virtual bool is_real() const { return false; } + virtual bool is_diag() const { return false; } + virtual RowMatrixXcd targ_mat() const { return RowMatrixXcd(0, 0); } + virtual RowMatrixXcd full_mat() const { return RowMatrixXcd(0, 0); } + virtual uint control_num() const { return 0; } + virtual uint targe_num() const { return 0; } + + virtual vector qbits() const { return {}; } + virtual vector cbits() const { return {}; } + + virtual uint condition() const { return 0; } + virtual vector>& instructions() { + vector> empty = {}; + return empty; + } }; - -class QuantumOperator : public Instruction { +class QuantumOperator : public Instruction { protected: - vector paras_; - uint control_num_; - uint targe_num_; - bool diag_; - bool real_; - RowMatrixXcd targ_mat_; - RowMatrixXcd full_mat_; - - + vector paras_; + uint control_num_; + uint targe_num_; + bool diag_; + bool real_; + RowMatrixXcd targ_mat_; + RowMatrixXcd full_mat_; + public: - //Constructor - QuantumOperator() { }; - QuantumOperator(string const name, vector paras, vector const &control_qubits, vector const &targe_qubits, RowMatrixXcd const &mat = RowMatrixXcd(0, 0), RowMatrixXcd const &full_mat = RowMatrixXcd(0, 0), bool diag=false, bool real=false); - - QuantumOperator(string const name,vector paras, vector const &positions, uint control_num, RowMatrixXcd const &mat=RowMatrixXcd(0, 0), RowMatrixXcd const &full_mat = RowMatrixXcd(0, 0), bool diag=false, bool real=false); - - QuantumOperator(string const name, vector const &positions, RowMatrixXcd const &mat=RowMatrixXcd(0, 0), RowMatrixXcd const &full_mat = RowMatrixXcd(0, 0)); - // virtual ~QuantumOperator(); - - //data accessor - virtual vector paras() const override {return paras_;} - virtual bool has_control() const override {return control_num_ == 0 ? false : true;} - virtual bool is_real() const override { return real_; } - virtual bool is_diag() const override { return diag_; } - virtual RowMatrixXcd targ_mat() const override { return targ_mat_;} - virtual RowMatrixXcd full_mat() const override {return full_mat_;} - virtual uint control_num() const override { return control_num_; } - virtual uint targe_num() const override { return targe_num_; } + // Constructor + QuantumOperator(){}; + QuantumOperator(string const name, vector paras, + vector const& control_qubits, + vector const& targe_qubits, + RowMatrixXcd const& mat = RowMatrixXcd(0, 0), + RowMatrixXcd const& full_mat = RowMatrixXcd(0, 0), + bool diag = false, bool real = false); + + QuantumOperator(string const name, vector paras, + vector const& positions, uint control_num, + RowMatrixXcd const& mat = RowMatrixXcd(0, 0), + RowMatrixXcd const& full_mat = RowMatrixXcd(0, 0), + bool diag = false, bool real = false); + + QuantumOperator(string const name, vector const& positions, + RowMatrixXcd const& mat = RowMatrixXcd(0, 0), + RowMatrixXcd const& full_mat = RowMatrixXcd(0, 0)); + // virtual ~QuantumOperator(); + + // data accessor + virtual vector paras() const override { return paras_; } + virtual bool has_control() const override { + return control_num_ == 0 ? false : true; + } + virtual bool is_real() const override { return real_; } + virtual bool is_diag() const override { return diag_; } + virtual RowMatrixXcd targ_mat() const override { return targ_mat_; } + virtual RowMatrixXcd full_mat() const override { return full_mat_; } + virtual uint control_num() const override { return control_num_; } + virtual uint targe_num() const override { return targe_num_; } }; -QuantumOperator::QuantumOperator(string const name, vector const &positions, RowMatrixXcd const &mat, RowMatrixXcd const &full_mat) -: -Instruction(name, positions), -paras_(vector(0)), -control_num_(-1), -targe_num_(0), -diag_(false), -real_(false), -targ_mat_(mat), -full_mat_(full_mat){ } - -QuantumOperator::QuantumOperator(string const name, vector paras, vector const &positions, uint control_num, RowMatrixXcd const &mat, RowMatrixXcd const &full_mat, bool diag, bool real) -: -Instruction(name, positions), -paras_(paras), -control_num_(control_num), -targe_num_(positions.size()-control_num), -diag_(diag), -real_(real), -targ_mat_(mat), -full_mat_(full_mat){ } - -QuantumOperator::QuantumOperator(string const name, vector paras, vector const &control_qubits, vector const &targe_qubits, RowMatrixXcd const &mat, RowMatrixXcd const &full_mat, bool diag, bool real) -: -paras_(paras), -diag_(diag), -real_(real), -targ_mat_(mat), -full_mat_(full_mat){ - Instruction::name_ = name; - Instruction::positions_ = control_qubits; - Instruction::positions_.insert(Instruction::positions_.end(), targe_qubits.begin(), targe_qubits.end()); - control_num_ = control_qubits.size(); - targe_num_ = targe_qubits.size(); +QuantumOperator::QuantumOperator(string const name, + vector const& positions, + RowMatrixXcd const& mat, + RowMatrixXcd const& full_mat) + : Instruction(name, positions), paras_(vector(0)), control_num_(-1), + targe_num_(0), diag_(false), real_(false), targ_mat_(mat), + full_mat_(full_mat) {} + +QuantumOperator::QuantumOperator(string const name, vector paras, + vector const& positions, + uint control_num, RowMatrixXcd const& mat, + RowMatrixXcd const& full_mat, bool diag, + bool real) + : Instruction(name, positions), paras_(paras), control_num_(control_num), + targe_num_(positions.size() - control_num), diag_(diag), real_(real), + targ_mat_(mat), full_mat_(full_mat) {} + +QuantumOperator::QuantumOperator(string const name, vector paras, + vector const& control_qubits, + vector const& targe_qubits, + RowMatrixXcd const& mat, + RowMatrixXcd const& full_mat, bool diag, + bool real) + : paras_(paras), diag_(diag), real_(real), targ_mat_(mat), + full_mat_(full_mat) { + Instruction::name_ = name; + Instruction::positions_ = control_qubits; + Instruction::positions_.insert(Instruction::positions_.end(), + targe_qubits.begin(), targe_qubits.end()); + control_num_ = control_qubits.size(); + targe_num_ = targe_qubits.size(); } class Measures : public Instruction { protected: - vector qbits_; - vector cbits_; - + vector qbits_; + vector cbits_; + public: - Measures(vector &qbits, vector &cbits) : - qbits_(qbits), - cbits_(cbits) - { - Instruction:name_ = "measure"; - } - virtual vector qbits() const override { return qbits_; } - virtual vector cbits() const override { return cbits_; } + Measures(vector& qbits, vector& cbits) + : qbits_(qbits), cbits_(cbits) { + Instruction: + name_ = "measure"; + } + virtual vector qbits() const override { return qbits_; } + virtual vector cbits() const override { return cbits_; } }; - -class Reset : public Instruction{ +class Reset : public Instruction { protected: - vector qbits_; + vector qbits_; + public: - Reset(vector const& qbits) : - qbits_(qbits) - { - Instruction:name_ = "reset"; - } - virtual vector qbits() const override { return qbits_; } + Reset(vector const& qbits) : qbits_(qbits) { + Instruction: + name_ = "reset"; + } + virtual vector qbits() const override { return qbits_; } }; -class Cif : public Instruction{ +class Cif : public Instruction { protected: - vector cbits_; - uint condition_; - vector> instructions_; + vector cbits_; + uint condition_; + vector> instructions_; + public: - Cif(vector &cbits, uint condition, vector> &instructions) - : - cbits_(cbits), - condition_(condition), - instructions_(std::move(instructions)) - { - Instruction:name_ = "cif"; - } - virtual uint condition() const override{ return condition_; } - virtual vector>& instructions() override { return instructions_; } - virtual vector cbits() const override { return cbits_; } + Cif(vector& cbits, uint condition, + vector>& instructions) + : cbits_(cbits), condition_(condition), + instructions_(std::move(instructions)) { + Instruction: + name_ = "cif"; + } + virtual uint condition() const override { return condition_; } + virtual vector>& instructions() override { + return instructions_; + } + virtual vector cbits() const override { return cbits_; } }; - // Construct C++ operators from pygates -std::unique_ptr from_pyops(py::object const& obj, bool get_full_mat=false, bool reverse=true) { - string name; - vector positions; - vector qbits; - vector cbits; - vector paras; - uint control_num = 0; - RowMatrixXcd targ_mat; - RowMatrixXcd full_mat; - - name = obj.attr("name").attr("lower")().cast(); - if (!(name == "barrier" || name == "delay" || name == "id" || +std::unique_ptr from_pyops(py::object const& obj, + bool get_full_mat = false, + bool reverse = true) { + string name; + vector positions; + vector qbits; + vector cbits; + vector paras; + uint control_num = 0; + RowMatrixXcd targ_mat; + RowMatrixXcd full_mat; + + name = obj.attr("name").attr("lower")().cast(); + if (!(name == "barrier" || name == "delay" || name == "id" || name == "measure" || name == "reset" || name == "cif")) { - //QuantumGate - positions = obj.attr("pos").cast>(); - paras = obj.attr("_paras").cast>(); - - if (py::hasattr(obj, "ctrls")) { - control_num = py::len(obj.attr("ctrls")); - } - - if (OPMAP.count(name) == 0){ - if (py::hasattr(obj, "_targ_matrix")){ - targ_mat = obj.attr("_get_targ_matrix")("reverse_order"_a=true).cast(); - }else{ //Single gate - targ_mat = obj.attr("matrix").cast(); - } - } - else{ - targ_mat = RowMatrixXcd(0, 0); - } - - if (get_full_mat){ - full_mat = obj.attr("_get_raw_matrix")("reverse_order"_a=reverse).cast(); - }else{ - full_mat = RowMatrixXcd(0, 0); - } - return std::make_unique(name, paras, positions, control_num, std::move(targ_mat), std::move(full_mat)); - - } else if (name == "measure") { - qbits = obj.attr("qbits").cast>(); - cbits = obj.attr("cbits").cast>(); - - return std::make_unique(qbits, cbits); - - } else if (name == "reset") { - positions = obj.attr("pos").cast>(); - return std::make_unique(positions); - - } else if (name == "cif") { - uint condition = 0; - vector> instructions; - cbits = obj.attr("cbits").cast>(); - condition = obj.attr("condition").cast(); - - // Recursively handdle instruction - if (py::isinstance(obj.attr("instructions"))) { - auto pyops = obj.attr("instructions"); - for (auto pyop_h : pyops) { - py::object pyop = py::reinterpret_borrow(pyop_h); - std::unique_ptr ins = from_pyops(pyop); - if (*ins) - instructions.push_back(std::move(ins)); - } - } - return std::make_unique(cbits, condition, instructions); + // QuantumGate + positions = obj.attr("pos").cast>(); + paras = obj.attr("_paras").cast>(); - } else { - return std::make_unique(); + if (py::hasattr(obj, "ctrls")) { + control_num = py::len(obj.attr("ctrls")); } -} -void check_operator(Instruction &op){ - std::cout << "-------------" << std::endl; - - std::cout << "name: " << op.name() << std::endl; - std::cout << "pos: "; - Qfutil::printVector(op.positions()); - - std::cout << "paras: "; - Qfutil::printVector(op.paras()); - - std::cout << "control number: "; - std::cout << op.control_num() << std::endl; + if (OPMAP.count(name) == 0) { + if (py::hasattr(obj, "_targ_matrix")) { + targ_mat = obj.attr("_get_targ_matrix")("reverse_order"_a = true) + .cast(); + } else { // Single gate + targ_mat = obj.attr("matrix").cast(); + } + } else { + targ_mat = RowMatrixXcd(0, 0); + } - std::cout << "matrix: " << std::endl; - std::cout << op.targ_mat() << std::endl; - std::cout << "flatten matrix: " << std::endl; - auto mat = op.targ_mat(); - // Eigen::Map v1(mat.data(), mat.size()); - // std::cout << "v1: " << v1 << std::endl; - auto matv = mat.data(); - for (auto i = 0;i < mat.size();i++){ - std::cout << matv[i] << " "; + if (get_full_mat) { + full_mat = obj.attr("_get_raw_matrix")("reverse_order"_a = reverse) + .cast(); + } else { + full_mat = RowMatrixXcd(0, 0); + } + return std::make_unique(name, paras, positions, + control_num, std::move(targ_mat), + std::move(full_mat)); + + } else if (name == "measure") { + qbits = obj.attr("qbits").cast>(); + cbits = obj.attr("cbits").cast>(); + + return std::make_unique(qbits, cbits); + + } else if (name == "reset") { + positions = obj.attr("pos").cast>(); + return std::make_unique(positions); + + } else if (name == "cif") { + uint condition = 0; + vector> instructions; + cbits = obj.attr("cbits").cast>(); + condition = obj.attr("condition").cast(); + + // Recursively handdle instruction + if (py::isinstance(obj.attr("instructions"))) { + auto pyops = obj.attr("instructions"); + for (auto pyop_h : pyops) { + py::object pyop = py::reinterpret_borrow(pyop_h); + std::unique_ptr ins = from_pyops(pyop); + if (*ins) + instructions.push_back(std::move(ins)); + } } - std::cout << std::endl; + return std::make_unique(cbits, condition, instructions); + + } else { + return std::make_unique(); + } +} - std::cout << "full matrix: " << std::endl; - std::cout << op.full_mat() << std::endl; - std::cout << "-------------" << std::endl; -} \ No newline at end of file +void check_operator(Instruction& op) { + std::cout << "-------------" << std::endl; + + std::cout << "name: " << op.name() << std::endl; + std::cout << "pos: "; + Qfutil::printVector(op.positions()); + + std::cout << "paras: "; + Qfutil::printVector(op.paras()); + + std::cout << "control number: "; + std::cout << op.control_num() << std::endl; + + std::cout << "matrix: " << std::endl; + std::cout << op.targ_mat() << std::endl; + std::cout << "flatten matrix: " << std::endl; + auto mat = op.targ_mat(); + // Eigen::Map v1(mat.data(), mat.size()); + // std::cout << "v1: " << v1 << std::endl; + auto matv = mat.data(); + for (auto i = 0; i < mat.size(); i++) { + std::cout << matv[i] << " "; + } + std::cout << std::endl; + + std::cout << "full matrix: " << std::endl; + std::cout << op.full_mat() << std::endl; + std::cout << "-------------" << std::endl; +} diff --git a/src/qfvm/qasm.hpp b/src/qfvm/qasm.hpp index 697d143..365a76f 100644 --- a/src/qfvm/qasm.hpp +++ b/src/qfvm/qasm.hpp @@ -11,8 +11,10 @@ using Qfutil::split_string; #name, Opname::name \ } - -/****This is C++-statevector-native gate set, it has not to be consistent with pyquafu. It is used to avoid matrix copy from py at present. Of course providing more native gate is needed in the future, e.g. efficient swap-like gate.****/ +/****This is C++-statevector-native gate set, it has not to be consistent with + * pyquafu. It is used to avoid matrix copy from py at present. Of course + * providing more native gate is needed in the future, e.g. efficient swap-like + * gate.****/ enum class Opname { creg, x, @@ -40,11 +42,10 @@ enum class Opname { }; std::unordered_map OPMAP{ - Pair(creg), Pair(x), Pair(y), Pair(z), Pair(h), Pair(s), - Pair(sdg), Pair(t), Pair(tdg), Pair(p), Pair(rx), Pair(ry), - Pair(rz), Pair(cnot), Pair(cx), Pair(cz), Pair(cp), - Pair(ccx), Pair(rzz), - Pair(measure), Pair(reset), Pair(cif)}; + Pair(creg), Pair(x), Pair(y), Pair(z), Pair(h), Pair(s), + Pair(sdg), Pair(t), Pair(tdg), Pair(p), Pair(rx), Pair(ry), + Pair(rz), Pair(cnot), Pair(cx), Pair(cz), Pair(cp), Pair(ccx), + Pair(rzz), Pair(measure), Pair(reset), Pair(cif)}; struct Operation { string name; diff --git a/src/qfvm/qfvm.cpp b/src/qfvm/qfvm.cpp index 51c57e4..c82bcff 100644 --- a/src/qfvm/qfvm.cpp +++ b/src/qfvm/qfvm.cpp @@ -1,5 +1,5 @@ -#include "simulator.hpp" #include "instructions.hpp" +#include "simulator.hpp" #include #include #include @@ -32,109 +32,108 @@ py::array_t to_numpy(const std::tuple& src) { return py::array_t(src_size, src_ptr, capsule); } -py::object applyop_statevec(py::object const& pyop, py::array_t> &np_inputstate){ - py::buffer_info buf = np_inputstate.request(); - auto* data_ptr = reinterpret_cast*>(buf.ptr); - size_t data_size = buf.size; - - auto op = from_pyops(pyop); - if (data_size == 0){ - return np_inputstate; - } - else{ - StateVector state(data_ptr, buf.size); - apply_op(*op, state); - state.move_data_to_python(); - return np_inputstate; - } -} +py::object applyop_statevec(py::object const& pyop, + py::array_t>& np_inputstate) { + py::buffer_info buf = np_inputstate.request(); + auto* data_ptr = reinterpret_cast*>(buf.ptr); + size_t data_size = buf.size; -py::dict sampling_statevec(py::dict const& pymeas, py::array_t> &np_inputstate, int shots){ - std::vector > measures; - for (auto item : pymeas){ - int qbit = item.first.cast(); - int cbit = item.second.cast(); - measures.push_back(std::pair(qbit, cbit)); - } - py::buffer_info buf = np_inputstate.request(); - auto* data_ptr = reinterpret_cast*>(buf.ptr); - size_t data_size = buf.size; + auto op = from_pyops(pyop); + if (data_size == 0) { + return np_inputstate; + } else { StateVector state(data_ptr, buf.size); - auto counts = state.measure_samples(measures, shots); + apply_op(*op, state); state.move_data_to_python(); - return py::cast(counts); + return np_inputstate; + } +} + +py::dict sampling_statevec(py::dict const& pymeas, + py::array_t>& np_inputstate, + int shots) { + std::vector> measures; + for (auto item : pymeas) { + int qbit = item.first.cast(); + int cbit = item.second.cast(); + measures.push_back(std::pair(qbit, cbit)); + } + py::buffer_info buf = np_inputstate.request(); + auto* data_ptr = reinterpret_cast*>(buf.ptr); + size_t data_size = buf.size; + StateVector state(data_ptr, buf.size); + auto counts = state.measure_samples(measures, shots); + state.move_data_to_python(); + return py::cast(counts); } std::pair, py::array_t>> simulate_circuit(py::object const& pycircuit, py::array_t>& np_inputstate, const int& shots) { - auto circuit = Circuit(pycircuit); - py::buffer_info buf = np_inputstate.request(); - auto* data_ptr = reinterpret_cast*>(buf.ptr); - size_t data_size = buf.size; - // If measure all at the end, simulate once - uint actual_shots = shots; - if (circuit.final_measure()) - actual_shots = 1; - vector> measures = circuit.measure_vec(); - std::map cbit_measured; - for (auto& pair : measures) { - cbit_measured[pair.second] = true; - } + auto circuit = Circuit(pycircuit); + py::buffer_info buf = np_inputstate.request(); + auto* data_ptr = reinterpret_cast*>(buf.ptr); + size_t data_size = buf.size; + // If measure all at the end, simulate once + uint actual_shots = shots; + if (circuit.final_measure()) + actual_shots = 1; + vector> measures = circuit.measure_vec(); + std::map cbit_measured; + for (auto& pair : measures) { + cbit_measured[pair.second] = true; + } - // Store outcome's count - std::map outcount; - StateVector state; - if (data_size != 0){ - state.load_data(data_ptr, data_size); + // Store outcome's count + std::map outcount; + StateVector state; + if (data_size != 0) { + state.load_data(data_ptr, data_size); + } + if (circuit.final_measure()) { + simulate(circuit, state); + if (!measures.empty()) { + auto countstr = state.measure_samples(circuit.measure_vec(), shots); + for (auto it : countstr) { + uint si = std::stoi(it.first, nullptr, 2); + outcount[si] = it.second; + } } - if (circuit.final_measure()){ - simulate(circuit, state); - if (!measures.empty()){ - auto countstr = state.measure_samples(circuit.measure_vec(), shots); - for (auto it : countstr){ - uint si = std::stoi(it.first, nullptr, 2); - outcount[si] = it.second; - } - } - if (data_size == 0) - return std::make_pair(outcount, - to_numpy(state.move_data_to_python())); - else - state.move_data_to_python(); - return std::make_pair(outcount, np_inputstate); + if (data_size == 0) + return std::make_pair(outcount, to_numpy(state.move_data_to_python())); + else + state.move_data_to_python(); + return std::make_pair(outcount, np_inputstate); + } else { + for (uint i = 0; i < shots; i++) { + StateVector buffer; + if (data_size != 0) { + auto buffer_ptr = std::make_unique[]>(data_size); + std::copy(data_ptr, data_ptr + data_size, buffer_ptr.get()); + buffer.load_data(buffer_ptr, data_size); + } else { + buffer = StateVector(); + } + simulate(circuit, buffer); + // store reg + vector tmpcreg = buffer.creg(); + uint outcome = 0; + for (uint j = 0; j < tmpcreg.size(); j++) { + if (cbit_measured.find(j) == cbit_measured.end()) + continue; + outcome *= 2; + outcome += tmpcreg[j]; + } + if (outcount.find(outcome) != outcount.end()) + outcount[outcome]++; + else + outcount[outcome] = 1; + + if (i == shots - 1) { + return std::make_pair(outcount, to_numpy(buffer.move_data_to_python())); + } } - else{ - for (uint i = 0; i < shots; i++) { - StateVector buffer; - if (data_size != 0){ - auto buffer_ptr = std::make_unique[]>(data_size); - std::copy(data_ptr, data_ptr + data_size, buffer_ptr.get()); - buffer.load_data(buffer_ptr, data_size); - }else{ - buffer = StateVector(); - } - simulate(circuit, buffer); - // store reg - vector tmpcreg = buffer.creg(); - uint outcome = 0; - for (uint j = 0; j < tmpcreg.size(); j++) { - if (cbit_measured.find(j) == cbit_measured.end()) - continue; - outcome *= 2; - outcome += tmpcreg[j]; - } - if (outcount.find(outcome) != outcount.end()) - outcount[outcome]++; - else - outcount[outcome] = 1; - - if (i == shots-1){ - return std::make_pair(outcount, - to_numpy(buffer.move_data_to_python())); - } - } } } @@ -257,22 +256,22 @@ simulate_circuit_custate(py::object const& pycircuit, } #endif -py::object expect_statevec(py::array_t> const&np_inputstate, py::list const paulis) -{ - py::buffer_info buf = np_inputstate.request(); - auto* data_ptr = reinterpret_cast*>(buf.ptr); - size_t data_size = buf.size; - StateVector state(data_ptr, buf.size); - py::list pyres; - for (auto pauli_h : paulis){ - py::object pypauli = py::reinterpret_borrow(pauli_h); - std::vector posv = pypauli.attr("pos").cast>(); - string paulistr = pypauli.attr("paulistr").cast(); - double expec = state.expect_pauli(paulistr, posv); - pyres.attr("append")(expec); - } - state.move_data_to_python(); - return pyres; +py::object expect_statevec(py::array_t> const& np_inputstate, + py::list const paulis) { + py::buffer_info buf = np_inputstate.request(); + auto* data_ptr = reinterpret_cast*>(buf.ptr); + size_t data_size = buf.size; + StateVector state(data_ptr, buf.size); + py::list pyres; + for (auto pauli_h : paulis) { + py::object pypauli = py::reinterpret_borrow(pauli_h); + std::vector posv = pypauli.attr("pos").cast>(); + string paulistr = pypauli.attr("paulistr").cast(); + double expec = state.expect_pauli(paulistr, posv); + pyres.attr("append")(expec); + } + state.move_data_to_python(); + return pyres; } PYBIND11_MODULE(qfvm, m) { @@ -285,12 +284,14 @@ PYBIND11_MODULE(qfvm, m) { "Simulate with circuit using clifford", py::arg("circuit"), py::arg("shots")); - m.def("expect_statevec", &expect_statevec, "Calculate paulis expectation", py::arg("inputstate"), py::arg("paulis")); - - m.def("applyop_statevec", &applyop_statevec, "Apply single operator to state", py::arg("operation"), py::arg("inputstate")); + m.def("expect_statevec", &expect_statevec, "Calculate paulis expectation", + py::arg("inputstate"), py::arg("paulis")); - m.def("sampling_statevec", &sampling_statevec, "sampling state", py::arg("measures"), py::arg("inputstate"), py::arg("shots")); + m.def("applyop_statevec", &applyop_statevec, "Apply single operator to state", + py::arg("operation"), py::arg("inputstate")); + m.def("sampling_statevec", &sampling_statevec, "sampling state", + py::arg("measures"), py::arg("inputstate"), py::arg("shots")); #ifdef _USE_GPU m.def("simulate_circuit_gpu", &simulate_circuit_gpu, "Simulate with circuit", diff --git a/src/qfvm/simulator.hpp b/src/qfvm/simulator.hpp index aacf852..e36c656 100644 --- a/src/qfvm/simulator.hpp +++ b/src/qfvm/simulator.hpp @@ -8,113 +8,111 @@ #include #include -void apply_op_general(StateVector & state, Instruction const& op){ - if (op.targe_num() == 1){ - auto mat_temp = op.targ_mat(); - complex *mat = mat_temp.data(); - if (op.control_num() == 0){ - state.apply_one_targe_gate_general<0>(op.positions(), mat); - }else if (op.control_num() == 1) - { - state.apply_one_targe_gate_general<1>(op.positions(), mat); - }else{ - state.apply_one_targe_gate_general<2>(op.positions(), mat); - } - } - else if(op.targe_num() > 1){ - state.apply_multi_targe_gate_general(op.positions(), op.control_num(), op.targ_mat()); - } - else{ - throw "Invalid target number"; +void apply_op_general(StateVector& state, Instruction const& op) { + if (op.targe_num() == 1) { + auto mat_temp = op.targ_mat(); + complex* mat = mat_temp.data(); + if (op.control_num() == 0) { + state.apply_one_targe_gate_general<0>(op.positions(), mat); + } else if (op.control_num() == 1) { + state.apply_one_targe_gate_general<1>(op.positions(), mat); + } else { + state.apply_one_targe_gate_general<2>(op.positions(), mat); } + } else if (op.targe_num() > 1) { + state.apply_multi_targe_gate_general(op.positions(), op.control_num(), + op.targ_mat()); + } else { + throw "Invalid target number"; + } } void apply_op(Instruction& op, StateVector& state) { - bool matched = false; - if (OPMAP.count(op.name()) == 0){ - apply_op_general(state,op); - }else{ + bool matched = false; + if (OPMAP.count(op.name()) == 0) { + apply_op_general(state, op); + } else { switch (OPMAP.at(op.name())) { - // Named gate - case Opname::x: - state.apply_x(op.positions()[0]); - break; - case Opname::y: - state.apply_y(op.positions()[0]); - break; - case Opname::z: - state.apply_z(op.positions()[0]); - break; - case Opname::h: - state.apply_h(op.positions()[0]); - break; - case Opname::s: - state.apply_s(op.positions()[0]); - break; - case Opname::sdg: - state.apply_sdag(op.positions()[0]); - break; - case Opname::t: - state.apply_t(op.positions()[0]); - break; - case Opname::tdg: - state.apply_tdag(op.positions()[0]); - break; - case Opname::p: - state.apply_p(op.positions()[0], op.paras()[0]); - break; - case Opname::rx: - state.apply_rx(op.positions()[0], op.paras()[0]); - break; - case Opname::ry: - state.apply_ry(op.positions()[0], op.paras()[0]); - break; - case Opname::rz: - state.apply_rz(op.positions()[0], op.paras()[0]); - break; - case Opname::cx: - state.apply_cnot(op.positions()[0], op.positions()[1]); - break; - case Opname::cnot: - state.apply_cnot(op.positions()[0], op.positions()[1]); - break; - case Opname::cp: - state.apply_cp(op.positions()[0], op.positions()[1], op.paras()[0]); - break; - case Opname::cz: - state.apply_cz(op.positions()[0], op.positions()[1]); - break; - case Opname::ccx: - state.apply_ccx(op.positions()[0], op.positions()[1], op.positions()[2]); - break; - case Opname::toffoli: - state.apply_ccx(op.positions()[0], op.positions()[1], op.positions()[2]); - case Opname::rzz: - state.apply_cnot(op.positions()[0], op.positions()[1]); - state.apply_rz(op.positions()[1], op.paras()[0]); - state.apply_cnot(op.positions()[0], op.positions()[1]); - break; - case Opname::measure: - state.apply_measure(op.qbits(), op.cbits()); - break; - case Opname::reset: - state.apply_reset(op.qbits()); - break; - case Opname::cif: - // check cbits and condition - matched = state.check_cif(op.cbits(), op.condition()); - // apply op in instructions - if (matched) { - for (const auto& op_h : op.instructions()) { - apply_op(*op_h, state); - } - } - break; + // Named gate + case Opname::x: + state.apply_x(op.positions()[0]); + break; + case Opname::y: + state.apply_y(op.positions()[0]); + break; + case Opname::z: + state.apply_z(op.positions()[0]); + break; + case Opname::h: + state.apply_h(op.positions()[0]); + break; + case Opname::s: + state.apply_s(op.positions()[0]); + break; + case Opname::sdg: + state.apply_sdag(op.positions()[0]); + break; + case Opname::t: + state.apply_t(op.positions()[0]); + break; + case Opname::tdg: + state.apply_tdag(op.positions()[0]); + break; + case Opname::p: + state.apply_p(op.positions()[0], op.paras()[0]); + break; + case Opname::rx: + state.apply_rx(op.positions()[0], op.paras()[0]); + break; + case Opname::ry: + state.apply_ry(op.positions()[0], op.paras()[0]); + break; + case Opname::rz: + state.apply_rz(op.positions()[0], op.paras()[0]); + break; + case Opname::cx: + state.apply_cnot(op.positions()[0], op.positions()[1]); + break; + case Opname::cnot: + state.apply_cnot(op.positions()[0], op.positions()[1]); + break; + case Opname::cp: + state.apply_cp(op.positions()[0], op.positions()[1], op.paras()[0]); + break; + case Opname::cz: + state.apply_cz(op.positions()[0], op.positions()[1]); + break; + case Opname::ccx: + state.apply_ccx(op.positions()[0], op.positions()[1], op.positions()[2]); + break; + case Opname::toffoli: + state.apply_ccx(op.positions()[0], op.positions()[1], op.positions()[2]); + case Opname::rzz: + state.apply_cnot(op.positions()[0], op.positions()[1]); + state.apply_rz(op.positions()[1], op.paras()[0]); + state.apply_cnot(op.positions()[0], op.positions()[1]); + break; + case Opname::measure: + state.apply_measure(op.qbits(), op.cbits()); + break; + case Opname::reset: + state.apply_reset(op.qbits()); + break; + case Opname::cif: + // check cbits and condition + matched = state.check_cif(op.cbits(), op.condition()); + // apply op in instructions + if (matched) { + for (const auto& op_h : op.instructions()) { + apply_op(*op_h, state); } + } + break; } + } } -void simulate(Circuit & circuit, StateVector& state) { +void simulate(Circuit& circuit, StateVector& state) { state.set_num(circuit.qubit_num()); state.set_creg(circuit.cbit_num()); // skip measure and handle it in qfvm.cpp @@ -126,7 +124,6 @@ void simulate(Circuit & circuit, StateVector& state) { } } - //--------clifford simulator----------------- template void apply_measure(circuit_simulator& cs, const vector& qbits, @@ -159,10 +156,10 @@ void apply_op(Instruction& op, circuit_simulator& cs) { } template -void simulate(Circuit & circuit, circuit_simulator& cs) { +void simulate(Circuit& circuit, circuit_simulator& cs) { // skip measure and handle it in qfvm.cpp bool skip_measure = circuit.final_measure(); - for (auto &op : circuit.instructions()) { + for (auto& op : circuit.instructions()) { if (skip_measure == true && op->name() == "measure") continue; apply_op(*op, cs); diff --git a/src/qfvm/statevector.hpp b/src/qfvm/statevector.hpp index 3de78b6..4026e2d 100644 --- a/src/qfvm/statevector.hpp +++ b/src/qfvm/statevector.hpp @@ -32,19 +32,19 @@ template class StateVector { StateVector(); explicit StateVector(uint num); StateVector(complex* data, size_t data_size); -// StateVector(std::unique_ptr[]> & data, size_t data_size): -// data_(std::move(data)), -// size_(data_size), -// num_(static_cast(std::log2(size_))) -// { } + // StateVector(std::unique_ptr[]> & data, size_t data_size): + // data_(std::move(data)), + // size_(data_size), + // num_(static_cast(std::log2(size_))) + // { } - void load_data(complex* data, size_t data_size){ + void load_data(complex* data, size_t data_size) { data_.reset(data); size_ = data_size; num_ = static_cast(std::log2(size_)); } - void load_data(std::unique_ptr[]> & data, size_t data_size){ + void load_data(std::unique_ptr[]>& data, size_t data_size) { data_ = std::move(data); size_ = data_size; num_ = static_cast(std::log2(size_)); @@ -92,8 +92,9 @@ template class StateVector { // Expectation and measurement double expect_pauli(string paulistr, vector const& posv); - std::unordered_map measure_samples(vector> meas, int shots); - + std::unordered_map + measure_samples(vector> meas, int shots); + // Measure and Reset std::pair sample_measure_probs(vector const& qbits); vector probabilities() const; @@ -1047,8 +1048,6 @@ vector StateVector::probabilities() const { return probs; } - - vector> convert(const vector>& v) { vector> ret(v.size(), 0.); for (size_t i = 0; i < v.size(); ++i) @@ -1408,38 +1407,38 @@ void StateVector::apply_reset(vector const& qbits) { update(qbits, 0, meas.first, meas.second); } - - //sample results +// sample results template -std::unordered_map StateVector::measure_samples(vector> meas, int shots){ - std::unordered_map counts; - auto rands = Qfutil::randomDoubleArr(shots); +std::unordered_map +StateVector::measure_samples(vector> meas, + int shots) { + std::unordered_map counts; + auto rands = Qfutil::randomDoubleArr(shots); #pragma omp for - for (int i = 0;i < shots;++i){ - std::string bitstring(meas.size(), '0'); - double rand = rands[i]; - double p = .0; - size_t sample; - for (sample = 0;sample < size_;++sample){ - p += std::real(data_[sample] * std::conj(data_[sample])); - if (rand < p){ - break; - } - } - for (auto it : meas){ - size_t bit = (sample >> it.first) & 1; - bitstring.replace(it.second, 1, std::to_string(bit).c_str()); - } - - if (counts.count(bitstring) > 0){ + for (int i = 0; i < shots; ++i) { + std::string bitstring(meas.size(), '0'); + double rand = rands[i]; + double p = .0; + size_t sample; + for (sample = 0; sample < size_; ++sample) { + p += std::real(data_[sample] * std::conj(data_[sample])); + if (rand < p) { + break; + } + } + for (auto it : meas) { + size_t bit = (sample >> it.first) & 1; + bitstring.replace(it.second, 1, std::to_string(bit).c_str()); + } + + if (counts.count(bitstring) > 0) { #pragma omp atomic - counts[bitstring] += 1; - }else{ + counts[bitstring] += 1; + } else { #pragma omp critical - counts[bitstring] = 1; - } + counts[bitstring] = 1; } - return counts; + } + return counts; } - diff --git a/src/qfvm/util.h b/src/qfvm/util.h index 66476fb..f5f07c4 100644 --- a/src/qfvm/util.h +++ b/src/qfvm/util.h @@ -1,19 +1,23 @@ #pragma once +#include "types.hpp" #include +#include #include +#include #include +#include #include #include -#include -#include -#include -#include #include -#include "types.hpp" #define TICK(x) auto bench_##x = std::chrono::steady_clock::now(); -#define TOCK(x) std::cout << #x ": " << std::chrono::duration_cast>(std::chrono::steady_clock::now() - bench_##x).count() << "s" << std::endl; +#define TOCK(x) \ + std::cout << #x ": " \ + << std::chrono::duration_cast>( \ + std::chrono::steady_clock::now() - bench_##x) \ + .count() \ + << "s" << std::endl; namespace Qfutil { @@ -26,16 +30,16 @@ std::vector randomArr(size_t length, size_t max) { return arr; } -std::vector randomDoubleArr(size_t length){ - std::random_device rd; - std::default_random_engine eng(rd()); - std::uniform_real_distribution distr(0., 1.); - - std::vector randarr; - for (auto n = 0; n < length;++n){ - randarr.push_back(distr(eng)); - } - return randarr; +std::vector randomDoubleArr(size_t length) { + std::random_device rd; + std::default_random_engine eng(rd()); + std::uniform_real_distribution distr(0., 1.); + + std::vector randarr; + for (auto n = 0; n < length; ++n) { + randarr.push_back(distr(eng)); + } + return randarr; } int randomint(int min, int max) { @@ -116,20 +120,17 @@ std::vector find_numbers(const std::string& str) { return res; } - - - /*----------------bit function------------------*/ const std::complex PHASE_YZ[4] = {1, imag_I, -1, -imag_I}; inline static uint popcount(uint x) { - x = ((x & 0xaaaaaaaaaaaaaaaaUL) >> 1) + (x & 0x5555555555555555UL); - x = ((x & 0xccccccccccccccccUL) >> 2) + (x & 0x3333333333333333UL); - x = ((x & 0xf0f0f0f0f0f0f0f0UL) >> 4) + (x & 0x0f0f0f0f0f0f0f0fUL); - x = ((x & 0xff00ff00ff00ff00UL) >> 8) + (x & 0x00ff00ff00ff00ffUL); - x = ((x & 0xffff0000ffff0000UL) >> 16) + (x & 0x0000ffff0000ffffUL); - x = ((x & 0xffffffff00000000UL) >> 32) + (x & 0x00000000ffffffffUL); - return (uint)x; + x = ((x & 0xaaaaaaaaaaaaaaaaUL) >> 1) + (x & 0x5555555555555555UL); + x = ((x & 0xccccccccccccccccUL) >> 2) + (x & 0x3333333333333333UL); + x = ((x & 0xf0f0f0f0f0f0f0f0UL) >> 4) + (x & 0x0f0f0f0f0f0f0f0fUL); + x = ((x & 0xff00ff00ff00ff00UL) >> 8) + (x & 0x00ff00ff00ff00ffUL); + x = ((x & 0xffff0000ffff0000UL) >> 16) + (x & 0x0000ffff0000ffffUL); + x = ((x & 0xffffffff00000000UL) >> 32) + (x & 0x00000000ffffffffUL); + return (uint)x; } -}//namespace Qfutil +} // namespace Qfutil diff --git a/tests/quafu/algorithms/amplitude_test.py b/tests/quafu/algorithms/amplitude_test.py index 5019ed3..e194e53 100644 --- a/tests/quafu/algorithms/amplitude_test.py +++ b/tests/quafu/algorithms/amplitude_test.py @@ -11,10 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from quafu.circuits import QuantumCircuit +import numpy as np import quafu.elements.element_gates as qeg from quafu.algorithms import AmplitudeEmbedding -import numpy as np +from quafu.circuits import QuantumCircuit class TestAmplitudeEmbedding: diff --git a/tests/quafu/algorithms/estimator_test.py b/tests/quafu/algorithms/estimator_test.py index 9003bae..6e9eeb5 100644 --- a/tests/quafu/algorithms/estimator_test.py +++ b/tests/quafu/algorithms/estimator_test.py @@ -20,7 +20,6 @@ import pytest from quafu.algorithms.estimator import Estimator from quafu.algorithms.hamiltonian import Hamiltonian, PauliOp - from quafu.circuits.quantum_circuit import QuantumCircuit from quafu.tasks.tasks import Task diff --git a/tests/quafu/circuits/building_circuit_test.py b/tests/quafu/circuits/building_circuit_test.py index 2601565..c278b4b 100644 --- a/tests/quafu/circuits/building_circuit_test.py +++ b/tests/quafu/circuits/building_circuit_test.py @@ -1,12 +1,15 @@ -from quafu import QuantumCircuit, simulate +import math + +import numpy as np from quafu.elements.element_gates import * from quafu.elements.parameters import Parameter -import numpy as np -import math + +from quafu import QuantumCircuit, simulate + class TestBuildingCircuit: def test_control(self): - q = QuantumCircuit(3) + q = QuantumCircuit(3) q << (XGate(1)) q << (CXGate(0, 2)) q << (RXGate(0, 0.1)) @@ -20,10 +23,10 @@ def test_control(self): print(e) def test_join(self): - q = QuantumCircuit(3) + q = QuantumCircuit(3) q << (XGate(1)) q << (CXGate(0, 2)) - q1 = QuantumCircuit(2) + q1 = QuantumCircuit(2) q1 << HGate(1) << CXGate(1, 0) q.join(q1, [2, 3]) q.draw_circuit() @@ -35,7 +38,7 @@ def test_power(self): q << U3Gate(1, 0.2, 0.1, 0.3) q << U3Gate(1, 0.2, 0.1, 0.3) q << RYYGate(0, 1, 0.4) - q << RYYGate(0, 1, 0.4) + q << RYYGate(0, 1, 0.4) q << RXGate(0, 0.2) q << RXGate(0, 0.2) q << CRYGate(0, 1, 0.23) @@ -44,7 +47,7 @@ def test_power(self): q1 << HGate(0) q1 << HGate(1) q1 << U3Gate(1, 0.2, 0.1, 0.3).power(2) - q1 << RYYGate(0, 1, 0.4).power(2) + q1 << RYYGate(0, 1, 0.4).power(2) q1 << RXGate(0, 0.2).power(2) q1 << CRYGate(0, 1, 0.23).power(2) @@ -68,10 +71,10 @@ def test_dagger(self): q3 << CRZGate(2, 1, -0.2) q3 << CXGate(0, 1) q3 << RYGate(2, -0.1) - q3 << RXGate(2, -0.3) + q3 << RXGate(2, -0.3) q3 << HGate(0) q3 << HGate(1) - q3 << HGate(2) + q3 << HGate(2) sv1 = simulate(q.dagger()).get_statevector() sv3 = simulate(q3).get_statevector() @@ -84,7 +87,7 @@ def test_wrapper_power(self): q1 = QuantumCircuit(2) q1 << U3Gate(1, 0.2, 0.1, 0.3) - q1 << RYYGate(0, 1, Parameter("p1", 0.4)) + q1 << RYYGate(0, 1, Parameter("p1", 0.4)) q1 << RXGate(0, 0.2) q1 << CRYGate(0, 1, 0.23) nq1 = q.join(q1.power(2), inplace=False) @@ -93,6 +96,7 @@ def test_wrapper_power(self): nq1.draw_circuit() nq2.unwrap().draw_circuit() from quafu.simulators.simulator import SVSimulator + backend = SVSimulator() sv1 = backend.run(nq1)["statevector"] sv2 = backend.run(nq2)["statevector"] @@ -107,15 +111,16 @@ def test_wrapper(self, inplace=True): q1 = QuantumCircuit(3) q1 << CXGate(0, 1) q1 << RXGate(2, Parameter("p1", 0.2)) - + q = q.join(q1.wrap(), qbits=[2, 3, 4], inplace=inplace) q.draw_circuit() from quafu.simulators.simulator import SVSimulator + backend = SVSimulator() sv1 = backend.run(q)["statevector"] q.unwrap().draw_circuit() - #multi-wrapper + # multi-wrapper q2 = QuantumCircuit(3) q2 << XGate(0) q2 << CXGate(0, 1) @@ -128,13 +133,12 @@ def test_wrapper(self, inplace=True): sv3 = backend.run(q2)["statevector"] assert math.isclose(np.abs(np.dot(sv2, sv3.conj())), 1.0) - def test_control_wrapper(self): q = QuantumCircuit(3) q << HGate(0) q << HGate(1) q << HGate(2) - + q1 = QuantumCircuit(3) q1 << CRYGate(0, 1, Parameter("p1", 0.3)) q1 << RXGate(2, 0.2) @@ -142,6 +146,7 @@ def test_control_wrapper(self): q.join(q1.wrap().add_controls([3, 4]), [2, 3, 4, 5, 6]) q.draw_circuit() from quafu.simulators.simulator import SVSimulator + backend = SVSimulator() sv1 = backend.run(q)["statevector"] q.unwrap() @@ -149,5 +154,6 @@ def test_control_wrapper(self): sv2 = backend.run(q)["statevector"] assert math.isclose(np.abs(np.dot(sv1, sv2.conj())), 1.0) -if __name__ == "__main__": - TestBuildingCircuit().test_wrapper() \ No newline at end of file + +if __name__ == "__main__": + TestBuildingCircuit().test_wrapper() diff --git a/tests/quafu/circuits/quantum_circuit_test.py b/tests/quafu/circuits/quantum_circuit_test.py index 919bd05..b1c5fe6 100644 --- a/tests/quafu/circuits/quantum_circuit_test.py +++ b/tests/quafu/circuits/quantum_circuit_test.py @@ -1,7 +1,8 @@ +import math + from quafu.circuits import QuantumCircuit from quafu.elements.element_gates import RXGate from quafu.elements.parameters import Parameter -import math class TestQuantumCircuit: @@ -34,10 +35,10 @@ def test_update_parameters(self): def test_instantiated_params(self): """Create Parameter objects""" pq = QuantumCircuit(4) - theta = [Parameter("theta_%d" %(i), i+1) for i in range(4)] + theta = [Parameter("theta_%d" % (i), i + 1) for i in range(4)] for i in range(4): pq.rx(i, theta[i]) - pq.ry(2, theta[0]*theta[1]-3.*theta[0]) + pq.ry(2, theta[0] * theta[1] - 3.0 * theta[0]) pq.draw_circuit() diff --git a/tests/quafu/instruction/gates_test.py b/tests/quafu/instruction/gates_test.py index f65c366..6cc2bee 100644 --- a/tests/quafu/instruction/gates_test.py +++ b/tests/quafu/instruction/gates_test.py @@ -1,10 +1,10 @@ import unittest -from numpy import pi import matplotlib.pyplot as plt - import quafu.elements as qe import quafu.elements.element_gates as qeg +from numpy import pi + from quafu import QuantumCircuit @@ -58,10 +58,44 @@ def test_instances(self): mcz = qeg.MCZGate(ctrls=[0, 1, 2], targ=3) toffoli = qeg.ToffoliGate(ctrl1=0, ctrl2=1, targ=2) - all_gates = [x, y, z, i, w, sw, swdg, sx, sxdg, sy, sydg, - h, s, sdg, t, tdg, - ph, rx, ry, rz, rxx, ryy, rzz, swap, iswap, fredkin, cx, cy, cz, cs, ct, cp, mcx, mcy, mcz, - toffoli] + all_gates = [ + x, + y, + z, + i, + w, + sw, + swdg, + sx, + sxdg, + sy, + sydg, + h, + s, + sdg, + t, + tdg, + ph, + rx, + ry, + rz, + rxx, + ryy, + rzz, + swap, + iswap, + fredkin, + cx, + cy, + cz, + cs, + ct, + cp, + mcx, + mcy, + mcz, + toffoli, + ] self.assertTrue(len(all_gates) <= len(gate_classes)) for gate in all_gates: self.assertIn(gate.name.lower(), gate_classes) diff --git a/tests/quafu/qasm/parameter_test.py b/tests/quafu/qasm/parameter_test.py index db6d28f..23a75f6 100644 --- a/tests/quafu/qasm/parameter_test.py +++ b/tests/quafu/qasm/parameter_test.py @@ -1,8 +1,8 @@ -import math +from quafu.elements import Parameter, ParameterExpression +from quafu.qfasm.qfasm_convertor import qasm_to_quafu from quafu import QuantumCircuit -from quafu.qfasm.qfasm_convertor import qasm_to_quafu -from quafu.elements import ParameterExpression, Parameter + class TestParser: """ @@ -32,7 +32,9 @@ def compare_cir(self, qc1: QuantumCircuit, qc2: QuantumCircuit): self.compare_parameter(gate1.paras[j], gate2.paras[j]) def compare_parameter(self, param1, param2): - if isinstance(param1, ParameterExpression) or isinstance(param2, ParameterExpression): + if isinstance(param1, ParameterExpression) or isinstance( + param2, ParameterExpression + ): assert isinstance(param2, ParameterExpression) assert isinstance(param1, ParameterExpression) assert param1.latex == param2.latex @@ -47,6 +49,7 @@ def compare_parameter(self, param1, param2): self.compare_parameter(param1.operands[i], param2.operands[i]) else: assert param1 == param2 + # ---------------------------------------- # test for parameter # ---------------------------------------- @@ -64,7 +67,7 @@ def test_parameter_plain(self): def test_parameter_func(self): qasm = """ - theta1 = 1.0; theta2 = 2.0; + theta1 = 1.0; theta2 = 2.0; gate test(rz, ry) a { rz(rz) a; ry(ry) a; @@ -120,7 +123,7 @@ def test_parameter_func_mix2(self): def test_parameter_expression(self): qasm = """ - theta1 = 1.0; theta2 = 2.0; + theta1 = 1.0; theta2 = 2.0; qreg q[2]; rx(theta1+theta2) q[0]; rx(theta1+theta1*theta2) q[1]; """ cir = qasm_to_quafu(openqasm=qasm) @@ -130,13 +133,13 @@ def test_parameter_expression(self): theta2 = Parameter("theta2", 2.0) self.compare_parameter(cir.variables[0], theta1) self.compare_parameter(cir.variables[1], theta2) - self.compare_parameter(cir.gates[0].paras[0], theta1+theta2) - self.compare_parameter(cir.gates[1].paras[0], theta1+theta1*theta2) + self.compare_parameter(cir.gates[0].paras[0], theta1 + theta2) + self.compare_parameter(cir.gates[1].paras[0], theta1 + theta1 * theta2) qasm = """ theta1 = 1.0; theta2 = 2.0; - theta3 = 3.0; theta4 = 4.0; - qreg q[2]; - rx(theta1+theta2-theta3*theta4^2) q[0]; + theta3 = 3.0; theta4 = 4.0; + qreg q[2]; + rx(theta1+theta2-theta3*theta4^2) q[0]; rx(sin(theta1*2+theta2)) q[1]; """ cir = qasm_to_quafu(openqasm=qasm) @@ -147,16 +150,15 @@ def test_parameter_expression(self): theta3 = Parameter("theta3", 3.0) theta4 = Parameter("theta4", 4.0) assert len(cir.variables) == 4 - theta = theta1 + theta2 - theta3 * theta4 ** 2 + theta = theta1 + theta2 - theta3 * theta4**2 self.compare_parameter(cir.variables[0], theta1) self.compare_parameter(cir.variables[1], theta2) self.compare_parameter(cir.variables[2], theta3) self.compare_parameter(cir.variables[3], theta4) self.compare_parameter(cir.gates[0].paras[0], theta) - theta = (theta1*2+theta2).sin() + theta = (theta1 * 2 + theta2).sin() self.compare_parameter(cir.gates[1].paras[0], theta) - def test_parameter_to_from(self): qc = QuantumCircuit(4) theta1 = Parameter("theta1", 1.0) @@ -165,4 +167,4 @@ def test_parameter_to_from(self): qc.rx(0, theta2) qc2 = QuantumCircuit(4) qc2.from_openqasm(qc.to_openqasm(with_para=True)) - self.compare_cir(qc,qc2) \ No newline at end of file + self.compare_cir(qc, qc2) diff --git a/tests/quafu/qasm/parser_test.py b/tests/quafu/qasm/parser_test.py index 07f886d..56caa98 100644 --- a/tests/quafu/qasm/parser_test.py +++ b/tests/quafu/qasm/parser_test.py @@ -76,7 +76,7 @@ def compare_cir(self, qc1: QuantumCircuit, qc2: QuantumCircuit): if hasattr(gate1, "pos"): assert gate1.pos == gate2.pos if hasattr(gate1, "paras") and len(gate1.paras) == 0: - assert len(gate2.paras) == 0 + assert len(gate2.paras) == 0 if hasattr(gate1, "paras") and len(gate1.paras) > 0: assert len(gate2.paras) > 0 for i, para in enumerate(gate1.paras): diff --git a/tests/quafu/simulator/basis_test.py b/tests/quafu/simulator/basis_test.py index a4eabac..19cd920 100644 --- a/tests/quafu/simulator/basis_test.py +++ b/tests/quafu/simulator/basis_test.py @@ -264,7 +264,6 @@ def test_after_measure(self): ) self.assertTrue(success) - class TestCliffordSimulatorBasis(BaseTest): """Test C++ Clifford simulator""" @@ -282,64 +281,48 @@ class TestCliffordSimulatorBasis(BaseTest): def test_simulate(self): print("test_simulate") self.circuit = BellCircuits.bell_no_measure() - result = simulate( - qc=self.circuit, simulator="clifford" - ) + result = simulate(qc=self.circuit, simulator="clifford") count = result.counts self.assertDictAlmostEqual(count, {}) def test_singleQgate_measure_atlast(self): self.circuit = BasicCircuits.singleQgate_measure_atlast() - result = simulate( - qc=self.circuit, shots=1, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=1, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {"11": 1}) def test_singleQgate_no_measure(self): self.circuit = BasicCircuits.singleQgate_no_measure() - result = simulate( - qc=self.circuit, shots=1, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=1, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {}) def test_singleQgate_measure_normal(self): self.circuit = BasicCircuits.singleQgate_measure_normal() - result = simulate( - qc=self.circuit, shots=10, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=10, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {"11": 10}) def test_multiQgate_measure_atlast(self): self.circuit = BasicCircuits.multiQgate_measure_atlast() - result = simulate( - qc=self.circuit, shots=10, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=10, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {"11": 10}) def test_multiQgate_no_measure(self): self.circuit = BasicCircuits.multiQgate_no_measure() - result = simulate( - qc=self.circuit, shots=1, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=1, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {}) def test_multiQgate_measure_normal(self): self.circuit = BasicCircuits.multiQgate_measure_normal() - result = simulate( - qc=self.circuit, shots=10, simulator="clifford" - ) + result = simulate(qc=self.circuit, shots=10, simulator="clifford") counts = result.counts self.assertDictAlmostEqual(counts, {"11": 10}) def test_anycbit_measure(self): self.circuit = BasicCircuits.any_cbit_measure() - result = simulate( - qc=self.circuit, shots=10, simulator="clifford" - ) - counts = result.counts + result = simulate(qc=self.circuit, shots=10, simulator="clifford") + counts = result.counts self.assertDictAlmostEqual(counts, {"0101": 10}) diff --git a/tests/quafu/simulator/classic_test.py b/tests/quafu/simulator/classic_test.py index 8afb8c2..256dcb1 100644 --- a/tests/quafu/simulator/classic_test.py +++ b/tests/quafu/simulator/classic_test.py @@ -14,10 +14,11 @@ import unittest +import numpy as np from base import BaseTest from quafu import QuantumCircuit, simulate -import numpy as np + class ClassicalCircuits: """Container for reference circuits used by the tests.""" @@ -175,10 +176,10 @@ def test_multi_reset(self): def test_cls_input_psi(self): self.circuit = ClassicalCircuits.single_reset() psi = np.zeros(4, dtype=np.complex128) - psi[0] = 1. + psi[0] = 1.0 result = simulate(qc=self.circuit, shots=10, psi=psi) probs = result.probabilities count = result.counts self.assertAlmostEqual(probs[0], 1) self.assertAlmostEqual(probs[1], 0) - self.assertDictAlmostEqual(count, {"10": 10}) \ No newline at end of file + self.assertDictAlmostEqual(count, {"10": 10}) diff --git a/tests/quafu/simulator/noise_test.py b/tests/quafu/simulator/noise_test.py index d52207c..2f496bd 100644 --- a/tests/quafu/simulator/noise_test.py +++ b/tests/quafu/simulator/noise_test.py @@ -1,8 +1,9 @@ -from quafu.elements.noise import Depolarizing, AmplitudeDamping -from quafu.elements.element_gates import HGate, XGate, CXGate +from quafu.algorithms.hamiltonian import Hamiltonian, PauliOp +from quafu.elements.element_gates import CXGate, HGate, XGate +from quafu.elements.noise import AmplitudeDamping, Depolarizing from quafu.simulators.simulator import NoiseSVSimulator + from quafu import QuantumCircuit -from quafu.algorithms.hamiltonian import Hamiltonian, PauliOp class NoisySimuTest: @@ -10,18 +11,20 @@ def test_noise_simu(self): q = QuantumCircuit(2) q << XGate(0) << Depolarizing(0, 0.1) q << XGate(1) << AmplitudeDamping(1, 0.2) - q.measure([0]) + q.measure([0]) simulator = NoiseSVSimulator() hamil = Hamiltonian([PauliOp("Z0"), PauliOp("Z1")]) res = simulator.run(q, shots=3000, hamiltonian=hamil) print(res["counts"]) print(res["pauli_expects"]) - + def test_noise_circuit(self): q = QuantumCircuit(5) q << HGate(0) q << CXGate(0, 1) << CXGate(1, 2) << CXGate(2, 3) << CXGate(3, 4) - q.add_noise("depolarizing", channel_args=(0.02,), gates=["cx"], qubits=[0, 2, 3]) + q.add_noise( + "depolarizing", channel_args=(0.02,), gates=["cx"], qubits=[0, 2, 3] + ) q.measure([0, 4]) q.draw_circuit() simulator = NoiseSVSimulator() @@ -29,5 +32,6 @@ def test_noise_circuit(self): print(res["counts"]) res.plot_probabilities(from_counts=True) + if __name__ == "__main__": - NoisySimuTest().test_noise_simu() \ No newline at end of file + NoisySimuTest().test_noise_simu()