diff --git a/src/qibojit/backends/clifford_operations_cpu.py b/src/qibojit/backends/clifford_operations_cpu.py index 2118a8d7..15636be9 100644 --- a/src/qibojit/backends/clifford_operations_cpu.py +++ b/src/qibojit/backends/clifford_operations_cpu.py @@ -6,7 +6,7 @@ PARALLEL = True -@njit("Tuple((u1[:], u1[:,:], u1[:,:]))(u1[:,:], u8)", parallel=PARALLEL, cache=True) +@njit(cache=True) def _get_rxz(symplectic_matrix, nqubits): return ( symplectic_matrix[:, -1], @@ -15,7 +15,7 @@ def _get_rxz(symplectic_matrix, nqubits): ) -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def H(symplectic_matrix, q, nqubits): r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -27,7 +27,7 @@ def H(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def CNOT(symplectic_matrix, control_q, target_q, nqubits): r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -40,7 +40,7 @@ def CNOT(symplectic_matrix, control_q, target_q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def CZ(symplectic_matrix, control_q, target_q, nqubits): """Decomposition --> H-CNOT-H""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -59,7 +59,7 @@ def CZ(symplectic_matrix, control_q, target_q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def S(symplectic_matrix, q, nqubits): r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -69,7 +69,7 @@ def S(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def Z(symplectic_matrix, q, nqubits): """Decomposition --> S-S""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -81,7 +81,7 @@ def Z(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def X(symplectic_matrix, q, nqubits): """Decomposition --> H-S-S-H""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -93,7 +93,7 @@ def X(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def Y(symplectic_matrix, q, nqubits): """Decomposition --> S-S-H-S-S-H""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -105,7 +105,7 @@ def Y(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def SX(symplectic_matrix, q, nqubits): """Decomposition --> H-S-H""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -116,7 +116,7 @@ def SX(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def SDG(symplectic_matrix, q, nqubits): """Decomposition --> S-S-S""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -127,7 +127,7 @@ def SDG(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def SXDG(symplectic_matrix, q, nqubits): """Decomposition --> H-S-S-S-H""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -138,7 +138,7 @@ def SXDG(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def RY_pi(symplectic_matrix, q, nqubits): """Decomposition --> H-S-S""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -151,7 +151,7 @@ def RY_pi(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def RY_3pi_2(symplectic_matrix, q, nqubits): """Decomposition --> H-S-S""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -164,7 +164,7 @@ def RY_3pi_2(symplectic_matrix, q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def SWAP(symplectic_matrix, control_q, target_q, nqubits): """Decomposition --> CNOT-CNOT-CNOT""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -195,7 +195,7 @@ def SWAP(symplectic_matrix, control_q, target_q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def iSWAP(symplectic_matrix, control_q, target_q, nqubits): """Decomposition --> H-CNOT-CNOT-H-S-S""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -228,7 +228,7 @@ def iSWAP(symplectic_matrix, control_q, target_q, nqubits): return symplectic_matrix -@njit("u1[:,:](u1[:,:], u8, u8, u8)", parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def CY(symplectic_matrix, control_q, target_q, nqubits): """Decomposition --> S-CNOT-SDG""" r, x, z = _get_rxz(symplectic_matrix, nqubits) @@ -254,13 +254,13 @@ def CY(symplectic_matrix, control_q, target_q, nqubits): # this cannot be cached anymore with numba unfortunately -@njit("i8(i8)", parallel=False, cache=True) +@njit(cache=True) def _packed_size(n): """Returns the size of an array of `n` booleans after packing.""" return int(np.ceil(n / 8)) -@njit(["u1[:,:](u1[:,:], i8)", "u1[:,:](b1[:,:], i8)"], parallel=PARALLEL, cache=True) +@njit(parallel=PARALLEL, cache=True) def _packbits(array, axis): array = array.astype(np.uint8) array = np.ascontiguousarray(np.swapaxes(array, axis, -1)) @@ -281,7 +281,7 @@ def _packbits(array, axis): return np.swapaxes(out, axis, -1) -@njit("u1[:,:](u1[:,:], i8)", parallel=PARALLEL, cache=True) +@njit(cache=True) def _pack_for_measurements(state, nqubits): """Prepares the state for measurements by packing the rows of the X and Z sections of the symplectic matrix.""" r, x, z = _get_rxz(state, nqubits) @@ -290,7 +290,7 @@ def _pack_for_measurements(state, nqubits): return np.hstack((x, z, r[:, None])) -@njit("u1[:](u1)", parallel=PARALLEL, cache=True) +@njit(cache=True) def _unpack_byte(byte): bits = np.empty(8, dtype=np.uint8) for i in range(8): @@ -298,7 +298,7 @@ def _unpack_byte(byte): return bits -@njit("u1[:,:](u1[:,:], i8, i8)", parallel=PARALLEL, cache=True) +@njit(cache=True) def _unpackbits(array, axis, count): # this is gonnna be used on 2d arrays only # i.e. portions of the symplectic matrix @@ -327,7 +327,7 @@ def _unpackbits(array, axis, count): return out -@njit("u1[:,:](u1[:,:], i8)", parallel=PARALLEL, cache=True) +@njit(cache=True) def _unpack_for_measurements(state, nqubits): """Unpacks the symplectc matrix that was packed for measurements.""" x = _unpackbits(state[:, : _packed_size(nqubits)], axis=1, count=nqubits) @@ -336,10 +336,6 @@ def _unpack_for_measurements(state, nqubits): @njit( - [ - "u1[:,:](u1[:,:], u8[:], u8[:], u8, b1)", - "u1[:,:](u1[:,:], u4[:], u4[:], u4, b1)", - ], parallel=PARALLEL, cache=True, fastmath=True, diff --git a/src/qibojit/custom_operators/gates.py b/src/qibojit/custom_operators/gates.py index 868d7411..23f3732f 100644 --- a/src/qibojit/custom_operators/gates.py +++ b/src/qibojit/custom_operators/gates.py @@ -2,7 +2,7 @@ from numba import njit, prange -@njit("int64(int64, int32[:])", cache=True) +@njit(cache=True) def multicontrol_index(g, qubits): i = 0 i += g @@ -12,16 +12,7 @@ def multicontrol_index(g, qubits): return i -@njit( - [ - "float32[:](float32[:], float32[:,:], int32, int32)", - "float64[:](float64[:], float64[:,:], int64, int64)", - "complex64[:](complex64[:], complex64[:,:], int64, int64)", - "complex128[:](complex128[:], complex128[:,:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_gate_kernel(state, gate, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -34,16 +25,7 @@ def apply_gate_kernel(state, gate, nstates, m): return state -@njit( - [ - "float32[:](float32[:], float32[:,:], int32[:], int32, int32)", - "float64[:](float64[:], float64[:,:], int32[:], int64, int64)", - "complex64[:](complex64[:], complex64[:,:], int32[:], int64, int64)", - "complex128[:](complex128[:], complex128[:,:], int32[:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_gate_kernel(state, gate, qubits, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -56,16 +38,7 @@ def multicontrol_apply_gate_kernel(state, gate, qubits, nstates, m): return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32, int32)", - "float64[:](float64[:], optional(float64[:,:]), int64, int64)", - "complex64[:](complex64[:], optional(complex64[:,:]), int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_x_kernel(state, gate, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -75,16 +48,7 @@ def apply_x_kernel(state, gate, nstates, m): return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32[:], int32, int32)", - "float64[:](float64[:], optional(float64[:,:]), int32[:], int64, int64)", - "complex64[:](complex64[:], optional(complex64[:,:]), int32[:], int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int32[:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_x_kernel(state, gate, qubits, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -94,14 +58,7 @@ def multicontrol_apply_x_kernel(state, gate, qubits, nstates, m): return state -@njit( - [ - "complex64[:](complex64[:], optional(complex64[:,:]), int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_y_kernel(state, gate, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -111,14 +68,7 @@ def apply_y_kernel(state, gate, nstates, m): return state -@njit( - [ - "complex64[:](complex64[:], optional(complex64[:,:]), int32[:], int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int32[:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_y_kernel(state, gate, qubits, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -128,16 +78,7 @@ def multicontrol_apply_y_kernel(state, gate, qubits, nstates, m): return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32, int32)", - "float64[:](float64[:], optional(float64[:,:]), int64, int64)", - "complex64[:](complex64[:], optional(complex64[:,:]), int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_z_kernel(state, gate, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -146,16 +87,7 @@ def apply_z_kernel(state, gate, nstates, m): return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32[:], int32, int32)", - "float64[:](float64[:], optional(float64[:,:]), int32[:], int64, int64)", - "complex64[:](complex64[:], optional(complex64[:,:]), int32[:], int64, int64)", - "complex128[:](complex128[:], optional(complex128[:,:]), int32[:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_z_kernel(state, gate, qubits, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -164,16 +96,7 @@ def multicontrol_apply_z_kernel(state, gate, qubits, nstates, m): return state -@njit( - [ - "float32[:](float32[:], float32, int32, int32)", - "float64[:](float64[:], float64, int64, int64)", - "complex64[:](complex64[:], complex64, int64, int64)", - "complex128[:](complex128[:], complex128, int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_z_pow_kernel(state, gate, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -182,16 +105,7 @@ def apply_z_pow_kernel(state, gate, nstates, m): return state -@njit( - [ - "float32[:](float32[:], float32, int32[:], int32, int32)", - "float64[:](float64[:], float64, int32[:], int64, int64)", - "complex64[:](complex64[:], complex64, int32[:], int64, int64)", - "complex128[:](complex128[:], complex128, int32[:], int64, int64)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_z_pow_kernel(state, gate, qubits, nstates, m): tk = 1 << m for g in prange(nstates): # pylint: disable=not-an-iterable @@ -200,16 +114,7 @@ def multicontrol_apply_z_pow_kernel(state, gate, qubits, nstates, m): return state -@njit( - [ - "float32[:](float32[:], float32[:,:], int32, int32, int32, boolean)", - "float64[:](float64[:], float64[:,:], int64, int64, int64, boolean)", - "complex64[:](complex64[:], complex64[:,:], int64, int64, int64, boolean)", - "complex128[:](complex128[:], complex128[:,:], int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_two_qubit_gate_kernel(state, gate, nstates, m1, m2, swap_targets=False): tk1, tk2 = 1 << m1, 1 << m2 uk1, uk2 = tk1, tk2 @@ -248,16 +153,7 @@ def apply_two_qubit_gate_kernel(state, gate, nstates, m1, m2, swap_targets=False return state -@njit( - [ - "float32[:](float32[:], float32[:,:], int32[:], int32, int32, int32, boolean)", - "float64[:](float64[:], float64[:,:], int32[:], int64, int64, int64, boolean)", - "complex64[:](complex64[:], complex64[:,:], int32[:], int64, int64, int64, boolean)", - "complex128[:](complex128[:], complex128[:,:], int32[:], int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_two_qubit_gate_kernel( state, gate, qubits, nstates, m1, m2, swap_targets=False ): @@ -297,16 +193,7 @@ def multicontrol_apply_two_qubit_gate_kernel( return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32, int32, int32, boolean)", - "float64[:](float64[:], optional(float64[:,:]), int64, int64, int64, boolean)", - "complex64[:](complex64[:], optional(complex64[:,:]), int64, int64, int64, boolean)", - "complex128[:](complex128[:], optional(complex128[:,:]), int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_swap_kernel(state, gate, nstates, m1, m2, swap_targets=False): tk1, tk2 = 1 << m1, 1 << m2 for g in prange(nstates): # pylint: disable=not-an-iterable @@ -317,16 +204,7 @@ def apply_swap_kernel(state, gate, nstates, m1, m2, swap_targets=False): return state -@njit( - [ - "float32[:](float32[:], optional(float32[:,:]), int32[:], int32, int32, int32, boolean)", - "float64[:](float64[:], optional(float64[:,:]), int32[:], int64, int64, int64, boolean)", - "complex64[:](complex64[:], optional(complex64[:,:]), int32[:], int64, int64, int64, boolean)", - "complex128[:](complex128[:], optional(complex128[:,:]), int32[:], int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_swap_kernel( state, gate, qubits, nstates, m1, m2, swap_targets=False ): @@ -338,14 +216,7 @@ def multicontrol_apply_swap_kernel( return state -@njit( - [ - "complex64[:](complex64[:], complex64[:], int64, int64, int64, boolean)", - "complex128[:](complex128[:], complex128[:], int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_fsim_kernel(state, gate, nstates, m1, m2, swap_targets=False): tk1, tk2 = 1 << m1, 1 << m2 uk1, uk2 = tk1, tk2 @@ -364,14 +235,7 @@ def apply_fsim_kernel(state, gate, nstates, m1, m2, swap_targets=False): return state -@njit( - [ - "complex64[:](complex64[:], complex64[:], int32[:], int64, int64, int64, boolean)", - "complex128[:](complex128[:], complex128[:], int32[:], int64, int64, int64, boolean)", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def multicontrol_apply_fsim_kernel( state, gate, qubits, nstates, m1, m2, swap_targets=False ): @@ -390,7 +254,7 @@ def multicontrol_apply_fsim_kernel( return state -@njit(["int32(int32, int32[:])", "int64(int64, int64[:])"], cache=True) +@njit(cache=True) def multitarget_index(i, targets): t = 0 for u, v in enumerate(targets): @@ -398,16 +262,7 @@ def multitarget_index(i, targets): return t -@njit( - [ - "float32[:](float32[:], float32[:,:], int32[:], int32, int32[:])", - "float64[:](float64[:], float64[:,:], int32[:], int64, int64[:])", - "complex64[:](complex64[:], complex64[:,:], int32[:], int64, int64[:])", - "complex128[:](complex128[:], complex128[:,:], int32[:], int64, int64[:])", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_three_qubit_gate_kernel(state, gate, qubits, nstates, targets): for g in prange(nstates): # pylint: disable=not-an-iterable ig = multicontrol_index(g, qubits) @@ -434,16 +289,7 @@ def apply_three_qubit_gate_kernel(state, gate, qubits, nstates, targets): return state -@njit( - [ - "float32[:](float32[:], float32[:,:], int32[:], int32, int32[:])", - "float64[:](float64[:], float64[:,:], int32[:], int64, int64[:])", - "complex64[:](complex64[:], complex64[:,:], int32[:], int64, int64[:])", - "complex128[:](complex128[:], complex128[:,:], int32[:], int64, int64[:])", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_four_qubit_gate_kernel(state, gate, qubits, nstates, targets): for g in prange(nstates): # pylint: disable=not-an-iterable ig = multicontrol_index(g, qubits) @@ -486,16 +332,7 @@ def apply_four_qubit_gate_kernel(state, gate, qubits, nstates, targets): return state -@njit( - [ - "float32[:](float32[:], float32[:,:], int32[:], int32, int32[:])", - "float64[:](float64[:], float64[:,:], int32[:], int64, int64[:])", - "complex64[:](complex64[:], complex64[:,:], int32[:], int64, int64[:])", - "complex128[:](complex128[:], complex128[:,:], int32[:], int64, int64[:])", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_five_qubit_gate_kernel(state, gate, qubits, nstates, targets): for g in prange(nstates): # pylint: disable=not-an-iterable ig = multicontrol_index(g, qubits) @@ -572,16 +409,7 @@ def apply_five_qubit_gate_kernel(state, gate, qubits, nstates, targets): return state -@njit( - [ - "float32[:](float32[:], float32[:,::1], int32[:], int32, int32[:])", - "float64[:](float64[:], float64[:,::1], int32[:], int64, int64[:])", - "complex64[:](complex64[:], complex64[:,::1], int32[:], int64, int64[:])", - "complex128[:](complex128[:], complex128[:,::1], int32[:], int64, int64[:])", - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def apply_multi_qubit_gate_kernel(state, gate, qubits, nstates, targets): nsubstates = 1 << len(targets) for g in prange(nstates): # pylint: disable=not-an-iterable diff --git a/src/qibojit/custom_operators/ops.py b/src/qibojit/custom_operators/ops.py index 10dff844..c7312e01 100644 --- a/src/qibojit/custom_operators/ops.py +++ b/src/qibojit/custom_operators/ops.py @@ -2,18 +2,12 @@ from numba import njit, prange -@njit("(i8,)", cache=True) +@njit(cache=True) def set_seed(seed): np.random.seed(seed) @njit( - [ - "float32[:](float32[:])", - "float64[:](float64[:])", - "complex64[:](complex64[:])", - "complex128[:](complex128[:])", - ], parallel=True, cache=True, ) @@ -25,12 +19,6 @@ def initial_state_vector(state): @njit( - [ - "float32[:,:](float32[:,:])", - "float64[:,:](float64[:,:])", - "complex64[:,:](complex64[:,:])", - "complex128[:,:](complex128[:,:])", - ], parallel=True, cache=True, ) @@ -42,7 +30,7 @@ def initial_density_matrix(state): return state -@njit("int64(int64, int64, int32[:])", cache=True) +@njit(cache=True) def collapse_index(g, h, qubits): i = 0 i += g @@ -53,12 +41,6 @@ def collapse_index(g, h, qubits): @njit( - [ - "float32[:](float32[:], int32[:], int32, int32)", - "float64[:](float64[:], int32[:], int64, int64)", - "complex64[:](complex64[:], int32[:], int64, int64)", - "complex128[:](complex128[:], int32[:], int64, int64)", - ], parallel=True, cache=True, ) @@ -75,12 +57,6 @@ def collapse_state(state, qubits, result, nqubits): @njit( - [ - "float32[:](float32[:], int32[:], int32, int32)", - "float64[:](float64[:], int32[:], int64, int64)", - "complex64[:](complex64[:], int32[:], int64, int64)", - "complex128[:](complex128[:], int32[:], int64, int64)", - ], parallel=True, cache=True, ) @@ -104,12 +80,6 @@ def collapse_state_normalized(state, qubits, result, nqubits): @njit( - [ - "int64[:](int64[:], float64[:], int64, optional(int64), int64, int64)", - "int32[:](int32[:], float32[:], int64, optional(int64), int64, int64)", - "int32[:](int32[:], float64[:], int64, optional(int64), int64, int64)", - "int64[:](int64[:], float32[:], int64, optional(int64), int64, int64)", - ], cache=True, parallel=True, ) @@ -155,12 +125,6 @@ def transpose_state(pieces, state, nqubits, order): @njit( - [ - "void(float32[:], float32[:], int32, int32)", - "void(float64[:], float64[:], int64, int64)", - "void(complex64[:], complex64[:], int64, int64)", - "void(complex128[:], complex128[:], int64, int64)", - ], cache=True, parallel=True, ) diff --git a/src/qibojit/custom_operators/quantum_info.py b/src/qibojit/custom_operators/quantum_info.py index 530f94b4..80b0c85a 100644 --- a/src/qibojit/custom_operators/quantum_info.py +++ b/src/qibojit/custom_operators/quantum_info.py @@ -8,12 +8,12 @@ ENGINE = qinfo.ENGINE # this should be numpy -@njit("(i8,)", cache=True) +@njit(cache=True) def set_seed(seed): ENGINE.random.seed(seed) -@njit("c16[:,:,::1](i8, c16[:,:,:,::1])", parallel=True, cache=True) +@njit(parallel=True, cache=True) def _pauli_basis_inner( nqubits, prod, @@ -28,7 +28,7 @@ def _pauli_basis_inner( return basis -@njit("c16[:,:,:,::1](c16[:,:,::1], i8)", parallel=False, cache=True) +@njit(cache=True) def _cartesian_product(arrays, n): num_arrays = len(arrays) num_elements = num_arrays**n # Total number of combinations @@ -52,11 +52,7 @@ def _cartesian_product(arrays, n): return result -@njit( - "c16[:,:,::1](i8, c16[:,::1], c16[:,::1], c16[:,::1], c16[:,::1], f8)", - parallel=True, - cache=True, -) +@njit(cache=True) def _pauli_basis( nqubits: int, pauli_0, @@ -75,23 +71,19 @@ def _pauli_basis( return _pauli_basis_inner(nqubits, prod) / normalization -@njit(["c16[:,:](c16[:,:], i8[:])", "c16[:,:,:](c16[:,:,:], i8[:])"], cache=True) +@njit(cache=True) def numba_transpose(array, axes): axes = to_fixed_tuple(axes, array.ndim) array = ENGINE.transpose(array, axes) return array -@njit( - ["c16[:,::1](c16[:,:], i8)", "c16[:,::1](c16[:,:,:], i8)"], - parallel=False, - cache=True, -) +@njit(cache=True) def _vectorization_row(state, dim: int): return ENGINE.reshape(ENGINE.ascontiguousarray(state), (-1, dim**2)) -@njit(["c16[:,::1](c16[:,:], i8)", "c16[:,::1](c16[:,:,:], i8)"], cache=True) +@njit(cache=True) def _vectorization_column(state, dim): indices = ENGINE.arange(state.ndim) indices[-2:] = indices[-2:][::-1] @@ -103,7 +95,7 @@ def _vectorization_column(state, dim): # dynamic tuple creation is not possible in numba # this might be jittable if we passed the shape # dim = (2,) * 2 * nqubits as inputs -@njit +@njit(cache=True) def _vectorization_system(state, dim=0): nqubits = int(ENGINE.log2(state.shape[-1])) new_axis = [ @@ -116,16 +108,12 @@ def _vectorization_system(state, dim=0): return ENGINE.reshape(state, (-1, 2 ** (2 * nqubits))) -@njit( - ["c16[:,:,::1](c16[:,:], i8)", "c16[:,:,::1](c16[:,:,:], i8)"], - parallel=False, - cache=True, -) +@njit(cache=True) def _unvectorization_row(state, dim: int): return ENGINE.reshape(ENGINE.ascontiguousarray(state), (state.shape[0], dim, dim)) -@njit(["c16[:,:,:](c16[:,:], i8)", "c16[:,:,:](c16[:,:,:], i8)"], cache=True) +@njit(cache=True) def _unvectorization_column(state, dim): last_dim = state.shape[0] state = state.T @@ -133,7 +121,7 @@ def _unvectorization_column(state, dim): return state.T -@njit("c16[:,:](c16[:,:], i8, i8)", parallel=True, cache=True) +@njit(cache=True) def _reshuffling(super_op, ax1: int, ax2: int): dim = int(ENGINE.sqrt(super_op.shape[0])) super_op = ENGINE.reshape(ENGINE.ascontiguousarray(super_op), (dim, dim, dim, dim)) @@ -145,17 +133,7 @@ def _reshuffling(super_op, ax1: int, ax2: int): return ENGINE.reshape(ENGINE.ascontiguousarray(super_op), (dim**2, dim**2)) -@njit( - [ - nbt.complex128[:]( - nbt.complex128[:, :], nbt.Tuple((nbt.int64[:], nbt.int64[:])) - ), - nbt.float64[:](nbt.float64[:, :], nbt.Tuple((nbt.int64[:], nbt.int64[:]))), - nbt.int64[:](nbt.int64[:, :], nbt.Tuple((nbt.int64[:], nbt.int64[:]))), - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def _array_at_2d_indices(array, indices): empty = ENGINE.empty(indices[0].shape, dtype=array.dtype) for i in prange(len(indices[0])): @@ -163,12 +141,7 @@ def _array_at_2d_indices(array, indices): return empty -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.int64[:, ::1]))( - nbt.complex128[:, :], nbt.int64 - ), - cache=True, -) +@njit(cache=True) def _post_sparse_pauli_basis_vectorization(basis, dim): indices = ENGINE.nonzero(basis) basis = _array_at_2d_indices(basis, indices) @@ -177,11 +150,7 @@ def _post_sparse_pauli_basis_vectorization(basis, dim): return basis, indices -@njit( - "c16[:,::1](i8, c16[:,::1], c16[:,::1], c16[:,::1], c16[:,::1], f8)", - parallel=False, - cache=True, -) +@njit(cache=True) def _vectorize_pauli_basis_row( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -190,11 +159,7 @@ def _vectorize_pauli_basis_row( return _vectorization_row(basis, dim) -@njit( - "c16[:,::1](i8, c16[:,::1], c16[:,::1], c16[:,::1], c16[:,::1], f8)", - parallel=False, - cache=True, -) +@njit(cache=True) def _vectorize_pauli_basis_column( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -203,18 +168,7 @@ def _vectorize_pauli_basis_column( return _vectorization_column(basis, dim) -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.int64[:, ::1]))( - nbt.int64, - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.float64, - ), - parallel=False, - cache=True, -) +@njit(cache=True) def _vectorize_sparse_pauli_basis_row( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -225,18 +179,7 @@ def _vectorize_sparse_pauli_basis_row( return _post_sparse_pauli_basis_vectorization(basis, dim) -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.int64[:, ::1]))( - nbt.int64, - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.float64, - ), - parallel=False, - cache=True, -) +@njit(cache=True) def _vectorize_sparse_pauli_basis_column( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -247,18 +190,7 @@ def _vectorize_sparse_pauli_basis_column( return _post_sparse_pauli_basis_vectorization(basis, dim) -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.int64[:]))( - nbt.int64, - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.float64, - ), - parallel=False, - cache=True, -) +@njit(cache=True) def _pauli_to_comp_basis_sparse_row( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -271,18 +203,7 @@ def _pauli_to_comp_basis_sparse_row( return ENGINE.ascontiguousarray(unitary).reshape(unitary.shape[0], -1), nonzero[1] -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.int64[:]))( - nbt.int64, - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.complex128[:, ::1], - nbt.float64, - ), - parallel=False, - cache=True, -) +@njit(cache=True) def _pauli_to_comp_basis_sparse_column( nqubits: int, pauli_0, pauli_1, pauli_2, pauli_3, normalization: float = 1.0 ): @@ -295,13 +216,7 @@ def _pauli_to_comp_basis_sparse_column( return ENGINE.ascontiguousarray(unitary).reshape(unitary.shape[0], -1), nonzero[1] -@njit( - nbt.Tuple((nbt.complex128[:, :], nbt.complex128[:, :], nbt.float64[:, :, ::1]))( - nbt.complex128[:, :] - ), - parallel=True, - cache=True, -) +@njit(cache=True) def _choi_to_kraus_preamble(choi_super_op): U, coefficients, V = ENGINE.linalg.svd(choi_super_op) U = U.T @@ -311,7 +226,7 @@ def _choi_to_kraus_preamble(choi_super_op): return U, V, coefficients -@njit("c16[:,:,:,:](c16[:,:,:], c16[:,:,:])", parallel=False, cache=True) +@njit(cache=True) def _kraus_operators(kraus_left, kraus_right): kraus_ops = ENGINE.empty((2,) + kraus_left.shape, dtype=kraus_left.dtype) kraus_ops[0] = kraus_left @@ -319,12 +234,7 @@ def _kraus_operators(kraus_left, kraus_right): return kraus_ops -@njit( - nbt.Tuple((nbt.complex128[:, :, :, :], nbt.float64[:, :, ::1]))( - nbt.complex128[:, ::1] - ), - cache=True, -) +@njit(cache=True) def _choi_to_kraus_row(choi_super_op): U, V, coefficients = _choi_to_kraus_preamble(choi_super_op) dim = int(ENGINE.sqrt(U.shape[-1])) @@ -334,12 +244,7 @@ def _choi_to_kraus_row(choi_super_op): return kraus_ops, coefficients -@njit( - nbt.Tuple((nbt.complex128[:, :, :, :], nbt.float64[:, :, ::1]))( - nbt.complex128[:, ::1] - ), - cache=True, -) +@njit(cache=True) def _choi_to_kraus_column(choi_super_op): U, V, coefficients = _choi_to_kraus_preamble(choi_super_op) dim = int(ENGINE.sqrt(U.shape[-1])) @@ -349,20 +254,20 @@ def _choi_to_kraus_column(choi_super_op): return kraus_ops, coefficients -@njit("f8[:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_statevector_real(dims: int): state = ENGINE.random.standard_normal(dims) return state / ENGINE.linalg.norm(state) -@njit("c16[:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_statevector(dims: int): state = ENGINE.random.standard_normal(dims) state = state + 1.0j * ENGINE.random.standard_normal(dims) return state / ENGINE.linalg.norm(state) -@njit("c16[:,::1](i8, i8, f8, f8)", parallel=True, cache=True) +@njit(parallel=True, cache=True) def _random_gaussian_matrix(dims: int, rank: int, mean: float, stddev: float): matrix = ENGINE.empty((dims, rank), dtype=ENGINE.complex128) for i in prange(dims): @@ -373,13 +278,13 @@ def _random_gaussian_matrix(dims: int, rank: int, mean: float, stddev: float): return matrix -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_density_matrix_pure(dims: int): state = _random_statevector(dims) return ENGINE.outer(state, ENGINE.conj(state).T) -@njit("c16[:,:](i8, i8, f8, f8)", parallel=True, cache=True) +@njit(cache=True) def _random_density_matrix_hs_ginibre(dims: int, rank: int, mean: float, stddev: float): state = _random_gaussian_matrix(dims, rank, mean, stddev) state = state @ ENGINE.ascontiguousarray( @@ -388,19 +293,19 @@ def _random_density_matrix_hs_ginibre(dims: int, rank: int, mean: float, stddev: return state / ENGINE.trace(state) -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_hermitian(dims: int): matrix = _random_gaussian_matrix(dims, dims, 0.0, 1.0) return (matrix + ENGINE.conj(matrix).T) / 2 -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_hermitian_semidefinite(dims: int): matrix = _random_gaussian_matrix(dims, dims, 0.0, 1.0) return ENGINE.conj(matrix).T @ matrix -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_unitary_haar(dims: int): matrix = _random_gaussian_matrix(dims, dims, 0.0, 1.0) Q, R = ENGINE.linalg.qr(matrix) @@ -410,7 +315,7 @@ def _random_unitary_haar(dims: int): return ENGINE.ascontiguousarray(Q) @ R -@njit(["c16[:,:](c16[:,:])", "f8[:,:](f8[:,:])"], parallel=False, cache=True) +@njit(cache=True) def expm_qinfo(A): """ Matrix exponential using scaling & squaring @@ -551,13 +456,13 @@ def expm_qinfo(A): return X -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _random_unitary(dims: int): H = _random_hermitian(dims) return expm_qinfo(-1.0j * H / 2) -@njit("c16[:,:](c16[:,:], i8, i8, f8, f8)", parallel=True, cache=True) +@njit(cache=True) def _random_density_matrix_bures_inner( unitary, dims: int, rank: int, mean: float, stddev: float ): @@ -570,13 +475,13 @@ def _random_density_matrix_bures_inner( return state / ENGINE.trace(state) -@njit("c16[:,:](i8, i8, f8, f8)", parallel=False, cache=True) +@njit(cache=True) def _random_density_matrix_bures(dims: int, rank: int, mean: float, stddev: float): unitary = _random_unitary(dims) return _random_density_matrix_bures_inner(unitary, dims, rank, mean, stddev) -@njit(nbt.Tuple((nbt.int64[:], nbt.int64[:]))(nbt.int64), parallel=False, cache=True) +@njit(cache=True) def _sample_from_quantum_mallows_distribution(nqubits: int): exponents = ENGINE.arange(nqubits, 0, -1, dtype=ENGINE.int64) powers = 4**exponents @@ -596,31 +501,13 @@ def _sample_from_quantum_mallows_distribution(nqubits: int): return hadamards, permutations -@njit( - [ - void( - nbt.complex128[:, :], - nbt.Tuple((nbt.int64[:], nbt.int64[:])), - nbt.complex128[:], - ), - void( - nbt.float64[:, :], nbt.Tuple((nbt.int64[:], nbt.int64[:])), nbt.float64[:] - ), - void(nbt.int64[:, :], nbt.Tuple((nbt.int64[:], nbt.int64[:])), nbt.int64[:]), - ], - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def _set_array_at_2d_indices(array, indices, values): for i in prange(len(indices[0])): array[indices[0][i], indices[1][i]] = values[i] -@njit( - nbt.Tuple((nbt.complex128[:, ::1], nbt.complex128[:, ::1]))(nbt.int64, nbt.int64), - parallel=True, - cache=True, -) +@njit(parallel=True, cache=True) def _super_op_from_bcsz_measure_preamble(dims: int, rank: int): super_op = _random_gaussian_matrix( dims**2, @@ -647,49 +534,49 @@ def _super_op_from_bcsz_measure_preamble(dims: int, rank: int): return operator, super_op -@njit("c16[:,:](i8, i8)", parallel=False, cache=True) +@njit(cache=True) def _super_op_from_bcsz_measure_row(dims: int, rank: int): operator, super_op = _super_op_from_bcsz_measure_preamble(dims, rank) operator = ENGINE.kron(ENGINE.eye(dims, dtype=operator.dtype), operator) return operator @ super_op @ operator -@njit("c16[:,:](i8, i8)", parallel=False, cache=True) +@njit(cache=True) def _super_op_from_bcsz_measure_column(dims: int, rank: int): operator, super_op = _super_op_from_bcsz_measure_preamble(dims, rank) operator = ENGINE.kron(operator, ENGINE.eye(dims, dtype=operator.dtype)) return operator @ super_op @ operator -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _super_op_from_haar_measure_row(dims: int): super_op = _random_unitary_haar(dims) super_op = _vectorization_row(super_op, dims) return ENGINE.outer(super_op, ENGINE.conj(super_op)) -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _super_op_from_haar_measure_column(dims: int): super_op = _random_unitary_haar(dims) super_op = _vectorization_column(super_op, dims) return ENGINE.outer(super_op, ENGINE.conj(super_op)) -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _super_op_from_hermitian_measure_row(dims: int): super_op = _random_unitary(dims) super_op = _vectorization_row(super_op, dims) return ENGINE.outer(super_op, ENGINE.conj(super_op)) -@njit("c16[:,:](i8)", parallel=True, cache=True) +@njit(cache=True) def _super_op_from_hermitian_measure_column(dims: int): super_op = _random_unitary(dims) super_op = _vectorization_column(super_op, dims) return ENGINE.outer(super_op, ENGINE.conj(super_op)) -@njit("c16[:,:](c16[:,:,::1], c16[:], i8)", parallel=True, cache=True) +@njit(parallel=True, cache=True) def _kraus_to_stinespring(kraus_ops, initial_state_env, dim_env: int): alphas = ENGINE.zeros((dim_env, dim_env, dim_env), dtype=initial_state_env.dtype) idx = ENGINE.arange(dim_env) @@ -703,7 +590,7 @@ def _kraus_to_stinespring(kraus_ops, initial_state_env, dim_env: int): return prod -@njit("c16[:,:,:](c16[:,::1], c16[::1], i8, i8)", parallel=True, cache=True) +@njit(parallel=True, cache=True) def _stinespring_to_kraus(stinespring, initial_state_env, dim: int, dim_env: int): stinespring = stinespring.reshape(dim, dim_env, dim, dim_env) stinespring = ENGINE.ascontiguousarray(ENGINE.swapaxes(stinespring, 1, 2)) @@ -722,37 +609,31 @@ def _stinespring_to_kraus(stinespring, initial_state_env, dim: int, dim_env: int return kraus.reshape(dim, dim_env, dim_env) -@njit("c16[:,:](c16[:,:])", parallel=True, cache=True) +@njit(cache=True) def _to_choi_row(channel): channel = _vectorization_row(channel, channel.shape[-1]) return ENGINE.outer(channel, ENGINE.conj(channel)) -@njit("c16[:,:](c16[:,:])", parallel=True, cache=True) +@njit(cache=True) def _to_choi_column(channel): channel = _vectorization_column(channel, channel.shape[-1]) return ENGINE.outer(channel, ENGINE.conj(channel)) -@njit("c16[:,:](c16[:,:])", parallel=False, cache=True) +@njit(cache=True) def _to_liouville_row(channel): channel = _to_choi_row(channel) return _reshuffling(channel, 1, 2) -@njit("c16[:,:](c16[:,:])", parallel=False, cache=True) +@njit(cache=True) def _to_liouville_column(channel): channel = _to_choi_column(channel) return _reshuffling(channel, 0, 3) -@njit( - nbt.Tuple((nbt.complex128[:, :, :], nbt.float64[:]))( - nbt.float64[:], nbt.complex128[:, :], nbt.float64 - ), - parallel=True, - cache=True, -) +@njit(cache=True) def _choi_to_kraus_cp_row(eigenvalues, eigenvectors, precision: float): eigv_gt_tol = ENGINE.abs(eigenvalues) > precision coefficients = ENGINE.sqrt(eigenvalues[eigv_gt_tol]) @@ -762,13 +643,7 @@ def _choi_to_kraus_cp_row(eigenvalues, eigenvectors, precision: float): return kraus_ops, coefficients -@njit( - nbt.Tuple((nbt.complex128[:, :, :], nbt.float64[:]))( - nbt.float64[:], nbt.complex128[:, :], nbt.float64 - ), - parallel=True, - cache=True, -) +@njit(cache=True) def _choi_to_kraus_cp_column(eigenvalues, eigenvectors, precision: float): eigv_gt_tol = ENGINE.abs(eigenvalues) > precision coefficients = ENGINE.sqrt(eigenvalues[eigv_gt_tol]) @@ -780,13 +655,13 @@ def _choi_to_kraus_cp_column(eigenvalues, eigenvectors, precision: float): return kraus_ops, coefficients -@njit("c16[:,:](c16[:,:,:])", parallel=True, cache=True) +@njit(cache=True) def _kraus_to_choi_row(kraus_ops): kraus_ops = _vectorization_row(kraus_ops, kraus_ops.shape[-1]) return kraus_ops.T @ ENGINE.conj(kraus_ops) -@njit("c16[:,:](c16[:,:,:])", parallel=True, cache=True) +@njit(cache=True) def _kraus_to_choi_column(kraus_ops): kraus_ops = _vectorization_column(kraus_ops, kraus_ops.shape[-1]) return kraus_ops.T @ ENGINE.conj(kraus_ops)