# Source code for qubiter.quantum_CSD_compiler.CS_Decomp

```import math
import cuncsd_sq as csd
from qubiter.UnitaryMat import *

[docs]class CS_Decomp:

[docs]    @staticmethod
def get_csd(unitary_mats):
"""
This function does a CS (cosine-sine) decomposition (by calling the
LAPACK function cuncsd.f. The old C++ Qubiter called zggsvd.f
instead) of each unitary matrix in the list of arrays unitary_mats.
This function is called by the constructor of the class Node and is
fundamental for decomposing a unitary matrix into multiplexors and
diagonal unitaries.

Parameters
----------
unitary_mats : list(np.ndarray)

Returns
-------
list(np.ndarray), list(np.ndarray), list(np.ndarray)

"""
block_size = unitary_mats[0].shape[0]
num_mats = len(unitary_mats)
for mat in unitary_mats:
assert mat.shape == (block_size, block_size)
if block_size == 1:
left_mats = None
right_mats = None
vec = np.array([unitary_mats[k][0, 0]
for k in range(0, num_mats)])
vec1 = vec[0] * np.ones((num_mats,))
if np.linalg.norm(vec - vec1) < 1e-6:
central_mats = None
else:
c_vec = np.real(vec)
s_vec = np.imag(vec)
central_mats = np.arctan2(s_vec, c_vec)
else:
# Henning Dekant constructed a python wrapper for LAPACK's cuncsd.f
# via the python application f2py. Thanks Henning!

# In[2]: import cuncsd
# In[3]: print(cuncsd.cuncsd.__doc__)
# x11,x12,x21,x22,theta,u1,u2,v1t,v2t,work,rwork,iwork,info =
# cuncsd(p,x11,x12,x21,x22,lwork,lrwork,
# [jobu1,jobu2,jobv1t,jobv2t,trans,signs,m,q,
# ldx11,ldx12,ldx21,ldx22,ldu1,ldu2,ldv1t,ldv2t,credit])
#
# Wrapper for ``cuncsd``.
#
# Parameters
# ----------
# p : input int
# x11 : input rank-2 array('F') with bounds (p,p)
# x12 : input rank-2 array('F') with bounds (p,p)
# x21 : input rank-2 array('F') with bounds (p,p)
# x22 : input rank-2 array('F') with bounds (p,p)
# lwork : input int
# lrwork : input int
#
# Other Parameters
# ----------------
# jobu1 : input string(len=1), optional
#     Default: 'Y'
# jobu2 : input string(len=1), optional
#     Default: 'Y'
# jobv1t : input string(len=1), optional
#     Default: 'Y'
# jobv2t : input string(len=1), optional
#     Default: 'Y'
# trans : input string(len=1), optional
#     Default: 'T'
# signs : input string(len=1), optional
#     Default: 'O'
# m : input int, optional
#     Default: 2*p
# q : input int, optional
#     Default: p
# ldx11 : input int, optional
#     Default: p
# ldx12 : input int, optional
#     Default: p
# ldx21 : input int, optional
#     Default: p
# ldx22 : input int, optional
#     Default: p
# ldu1 : input int, optional
#     Default: p
# ldu2 : input int, optional
#     Default: p
# ldv1t : input int, optional
#     Default: p
# ldv2t : input int, optional
#     Default: p
# credit : input int, optional
#     Default: 0
#
# Returns
# -------
# x11 : rank-2 array('F') with bounds (p,p)
# x12 : rank-2 array('F') with bounds (p,p)
# x21 : rank-2 array('F') with bounds (p,p)
# x22 : rank-2 array('F') with bounds (p,p)
# theta : rank-1 array('f') with bounds (p)
# u1 : rank-2 array('F') with bounds (p,p)
# u2 : rank-2 array('F') with bounds (p,p)
# v1t : rank-2 array('F') with bounds (p,p)
# v2t : rank-2 array('F') with bounds (p,p)
# work : rank-1 array('F') with bounds (abs(lwork))
# rwork : rank-1 array('f') with bounds (abs(lrwork))
# iwork : rank-1 array('i') with bounds (p)
# info : int

left_mats = []
central_mats = []
right_mats = []

for mat in unitary_mats:
dim = mat.shape[0]
assert dim % 2 == 0
hdim = dim >> 1  # half dimension

p = hdim
# x11 = np.copy(mat[0:hdim, 0:hdim], 'C')
# x12 = np.copy(mat[0:hdim, hdim:dim], 'C')
# x21 = np.copy(mat[hdim:dim, 0:hdim], 'C')
# x22 = np.copy(mat[hdim:dim, hdim:dim], 'C')

x11 = mat[0:hdim, 0:hdim]
x12 = mat[0:hdim, hdim:dim]
x21 = mat[hdim:dim, 0:hdim]
x22 = mat[hdim:dim, hdim:dim]

# print('mat\n', mat)
# print('x11\n', x11)
# print('x12\n', x12)
# print('x21\n', x21)
# print('x22\n', x22)
x11, x12, x21, x22, theta, u1, u2, v1t, v2t, \
work, rwork, iwork, info = \
csd.cuncsd(p, x11, x12, x21, x22, lwork=-1, lrwork=-1,
trans='F')
# print('x11\n', x11)
# print('x12\n', x12)
# print('x21\n', x21)
# print('x22\n', x22)

lw = math.ceil(work[0].real)
lrw = math.ceil(rwork[0].real)
# print("work query:", lw)
# print("rwork query:", lrw)
x11, x12, x21, x22, theta, u1, u2, v1t, v2t, \
work, rwork, iwork, info = \
csd.cuncsd(p, x11, x12, x21, x22, lwork=lw, lrwork=lrw,
trans='F')
# print('info', info)
# print('u1 continguous', u1.flags.contiguous)
# u1 = np.ascontiguousarray(u1)
# u2 = np.ascontiguousarray(u2)
# v1t = np.ascontiguousarray(v1t)
# v2t = np.ascontiguousarray(v2t)
# print('u1 continguous', u1.flags.contiguous)

left_mats.append(u1)
left_mats.append(u2)
central_mats.append(theta)
right_mats.append(v1t)
right_mats.append(v2t)
return left_mats, central_mats, right_mats

if __name__ == "__main__":
from qubiter.FouSEO_writer import *
from qubiter.quantum_CSD_compiler.MultiplexorSEO_writer import *

def main():
print("\ncs decomp example-------------")
num_qbits = 2
num_rows = 1 << num_qbits
mat = FouSEO_writer.fourier_trans_mat(num_rows)
assert UnitaryMat.is_unitary(mat)
left_mats, central_mats, right_mats = CS_Decomp.get_csd([mat])
# print('left mats\n', left_mats)
# print('central_mats\n', central_mats)
# print('right_mats\n', right_mats)

left = np.zeros((num_rows, num_rows), dtype=complex)
half_nrows = num_rows // 2
left[0:half_nrows, 0:half_nrows] = left_mats[0]
left[half_nrows:num_rows, half_nrows:num_rows] = left_mats[1]
# print('left', left)

right = np.zeros((num_rows, num_rows), dtype=complex)
right[0:half_nrows, 0:half_nrows] = right_mats[0]
right[half_nrows:num_rows, half_nrows:num_rows] = right_mats[1]

center = MultiplexorSEO_writer.mp_mat(central_mats[0])
# print('center', center)
mat_prod = np.dot(np.dot(left, center), right)
# print('mat\n', mat)
# print('mat_prod\n', mat_prod)
err = np.linalg.norm(mat - mat_prod)
print('err=', err)

main()

```