Skip to content

Commit

Permalink
feat: rewrote the operations as numba loops
Browse files Browse the repository at this point in the history
  • Loading branch information
BrunoLiegiBastonLiegi committed Dec 13, 2023
1 parent ce82a33 commit 53aecc6
Showing 1 changed file with 212 additions and 94 deletions.
306 changes: 212 additions & 94 deletions src/qibojit/backends/clifford.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from numba import njit
import numpy as np
from numba import njit, prange
from qibo.backends.clifford import CliffordOperations as CO


Expand All @@ -7,133 +8,250 @@ def __init__(self, engine):
super().__init__(engine)

@staticmethod
def I(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.I)(
symplectic_matrix, q, nqubits
)

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def H(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.H)(
symplectic_matrix, q, nqubits
)
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (x[i, q] & z[i, q])
tmp = symplectic_matrix[i, q]
symplectic_matrix[i, q] = symplectic_matrix[i, nqubits + q]
symplectic_matrix[:, nqubits + q] = tmp
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)
def CNOT(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(CO.CNOT)(
symplectic_matrix, control_q, target_q, nqubits
)
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (x[i, control_q] & z[i, target_q]) & (
x[i, target_q] ^ ~z[i, control_q]
)
symplectic_matrix[i, target_q] = x[i, target_q] ^ x[i, control_q]
symplectic_matrix[i, nqubits + control_q] = z[i, control_q] ^ z[i, target_q]
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)
def CZ(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(CO.CZ)(
symplectic_matrix, control_q, target_q, nqubits
)
"""Decomposition --> H-CNOT-H"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = (
r[i]
^ (x[i, target_q] & z[i, target_q])
^ (
x[i, control_q]
& x[i, target_q]
& (z[i, target_q] ^ ~z[i, control_q])
)
^ (x[i, target_q] & (z[i, target_q] ^ x[i, control_q]))
)
z_control_q = x[i, target_q] ^ z[i, control_q]
z_target_q = z[i, target_q] ^ x[i, control_q]
symplectic_matrix[i, nqubits + control_q] = z_control_q
symplectic_matrix[i, nqubits + target_q] = z_target_q
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def S(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.S)(
symplectic_matrix, q, nqubits
)
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (x[i, q] & z[i, q])
symplectic_matrix[i, nqubits + q] = z[i, q] ^ x[i, q]
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def Z(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.Z)(
symplectic_matrix, q, nqubits
)
"""Decomposition --> S-S"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (
(x[i, q] & z[i, q]) ^ x[i, q] & (z[i, q] ^ x[i, q])
)
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def X(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.X)(
symplectic_matrix, q, nqubits
)
"""Decomposition --> H-S-S-H"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = (
r[i] ^ (z[i, q] & (z[i, q] ^ x[i, q])) ^ (z[i, q] & x[i, q])
)
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def Y(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.Y)(
symplectic_matrix, q, nqubits
)
"""Decomposition --> S-S-H-S-S-H"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = (
r[i] ^ (z[i, q] & (z[i, q] ^ x[i, q])) ^ (x[i, q] & (z[i, q] ^ x[i, q]))
)
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def SX(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.SX)(
symplectic_matrix, q, nqubits
)
"""Decomposition --> H-S-H"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (z[i, q] & (z[i, q] ^ x[i, q]))
symplectic_matrix[i, q] = z[i, q] ^ x[i, q]
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def SDG(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.SDG)(
symplectic_matrix, q, nqubits
)
"""Decomposition --> S-S-S"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (x[i, q] & (z[i, q] ^ x[i, q]))
symplectic_matrix[i, nqubits + q] = z[i, q] ^ x[i, q]
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)
def SXDG(symplectic_matrix, q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8)", parallel=True, cache=True)(CO.SXDG)(
symplectic_matrix, q, nqubits
)

@staticmethod
def RX(symplectic_matrix, q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, f4)", parallel=True, cache=True)(CO.RX)(
symplectic_matrix, q, nqubits, theta
)

@staticmethod
def RZ(symplectic_matrix, q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, f4)", parallel=True, cache=True)(CO.RZ)(
symplectic_matrix, q, nqubits, theta
)

@staticmethod
def RY(symplectic_matrix, q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, f4)", parallel=True, cache=True)(CO.RY)(
symplectic_matrix, q, nqubits, theta
)
"""Decomposition --> H-S-S-S-H"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = r[i] ^ (z[i, q] & x[i, q])
symplectic_matrix[i, q] = z[i, q] ^ x[i, q]
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)
def SWAP(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(CO.SWAP)(
symplectic_matrix, control_q, target_q, nqubits
)
"""Decomposition --> CNOT-CNOT-CNOT"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[:-1, -1] = (
r[i]
^ (
x[i, control_q]
& z[i, target_q]
& (x[i, target_q] ^ ~z[i, control_q])
)
^ (
(x[i, target_q] ^ x[i, control_q])
& (z[i, target_q] ^ z[i, control_q])
& (z[i, target_q] ^ ~x[i, control_q])
)
^ (
x[i, target_q]
& z[i, control_q]
& (
x[i, control_q]
^ x[i, target_q]
^ z[i, control_q]
^ ~z[i, target_q]
)
)
)
x_cq = symplectic_matrix[i, control_q]
x_tq = symplectic_matrix[i, target_q]
z_cq = symplectic_matrix[i, nqubits + control_q]
z_tq = symplectic_matrix[i, nqubits + target_q]
symplectic_matrix[i, control_q] = x_tq
symplectic_matrix[i, target_q] = x_cq
symplectic_matrix[i, nqubits + control_q] = z_tq
symplectic_matrix[i, nqubits + target_q] = z_cq
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)
def iSWAP(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(
CO.iSWAP
)(symplectic_matrix, control_q, target_q, nqubits)

@staticmethod
def FSWAP(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(
CO.FSWAP
)(symplectic_matrix, control_q, target_q, nqubits)
"""Decomposition --> H-CNOT-CNOT-H-S-S"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = (
r[i]
^ (x[i, target_q] & z[i, target_q])
^ (x[i, control_q] & z[i, control_q])
^ (x[i, control_q] & (z[i, control_q] ^ x[i, control_q]))
^ (
(z[i, control_q] ^ x[i, control_q])
& (z[i, target_q] ^ x[i, target_q])
& (x[i, target_q] ^ ~x[i, control_q])
)
^ (
(x[i, target_q] ^ z[i, control_q] ^ x[i, control_q])
& (x[i, target_q] ^ z[i, target_q] ^ x[i, control_q])
& (
x[i, target_q]
^ z[i, target_q]
^ x[i, control_q]
^ ~z[i, control_q]
)
)
^ (
x[i, control_q]
& (x[i, target_q] ^ x[i, control_q] ^ z[i, control_q])
)
)
z_control_q = x[i, target_q] ^ z[i, target_q] ^ x[i, control_q]
z_target_q = x[i, target_q] ^ z[i, control_q] ^ x[i, control_q]
symplectic_matrix[i, nqubits + control_q] = z_control_q
symplectic_matrix[i, nqubits + target_q] = z_target_q
tmp = symplectic_matrix[i, control_q]
symplectic_matrix[i, control_q] = symplectic_matrix[i, target_q]
symplectic_matrix[i, target_q] = tmp
return symplectic_matrix

@staticmethod
@njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)
def CY(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(CO.CY)(
symplectic_matrix, control_q, target_q, nqubits
)

@staticmethod
def CRX(symplectic_matrix, control_q, target_q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, u8, f4)", parallel=True, cache=True)(
CO.CRX
)(symplectic_matrix, control_q, target_q, nqubits, theta)

@staticmethod
def CRZ(symplectic_matrix, control_q, target_q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, u8, f4)", parallel=True, cache=True)(
CO.CRZ
)(symplectic_matrix, control_q, target_q, nqubits, theta)

@staticmethod
def CRY(symplectic_matrix, control_q, target_q, nqubits, theta):
return njit("b1[:,:](b1[:,:], u8, u8, u8, f4)", parallel=True, cache=True)(
CO.CRY
)(symplectic_matrix, control_q, target_q, nqubits, theta)

@staticmethod
def ECR(symplectic_matrix, control_q, target_q, nqubits):
return njit("b1[:,:](b1[:,:], u8, u8, u8)", parallel=True, cache=True)(CO.ECR)(
symplectic_matrix, control_q, target_q, nqubits
)
"""Decomposition --> S-CNOT-SDG"""
r = symplectic_matrix[:-1, -1]
x = symplectic_matrix[:-1, :nqubits]
z = symplectic_matrix[:-1, nqubits:-1]
for i in prange(symplectic_matrix.shape[0] - 1):
symplectic_matrix[i, -1] = (
r[i]
^ (x[i, target_q] & (z[i, target_q] ^ x[i, target_q]))
^ (
x[i, control_q]
& (x[i, target_q] ^ z[i, target_q])
& (z[i, control_q] ^ ~x[i, target_q])
)
^ (
(x[i, target_q] ^ x[i, control_q])
& (z[i, target_q] ^ x[i, target_q])
)
)
x_target_q = x[i, control_q] ^ x[i, target_q]
z_control_q = z[i, control_q] ^ z[i, target_q] ^ x[i, target_q]
z_target_q = z[i, target_q] ^ x[i, control_q]
symplectic_matrix[i - 1, target_q] = x_target_q
symplectic_matrix[i - 1, nqubits + control_q] = z_control_q
symplectic_matrix[i - 1, nqubits + target_q] = z_target_q
return symplectic_matrix

0 comments on commit 53aecc6

Please sign in to comment.