Source code for WPSProtocol.Module_WalshPSeq

# -*- coding: utf-8 -*-
"""
Created on Thu Jun  6 11:48:00 2024

Module for Generating Walsh Pulse Sequence 
for Dynamical Decoupling in Long-range interactions

@author: Jessica
"""
import numpy as np
from numpy import linalg
from scipy import linalg as splinalg
import matplotlib.pyplot as plt
from scipy import sparse as sp
import scipy.sparse.linalg
from functools import reduce
import itertools
from scipy import linalg
from scipy.linalg import expm

Z = np.array([[1, 0], [0, -1]])
X = np.array([[0, 1], [1, 0]])
Y = np.array([[0, -1j], [1j, 0]])
I = np.array([[1, 0], [0, 1]])
H = np.array([[1, 1], [1, -1]])


[docs] def WF_Conditions(tupleprdt, **kwargs): """ *args: tupleprdt is a list of tuple of signs of wxi and wyi. Returns: tupleprdt - a list of Pauli Ops depending on eq 8 from the paper """ for i, tprdt in enumerate(tupleprdt): if tprdt[0] == tprdt[1] == 1: tupleprdt[i] = I elif tprdt[0] == -tprdt[1] == 1: tupleprdt[i] = X elif -tprdt[0] == tprdt[1] == 1: tupleprdt[i] = Y elif tprdt[0] == tprdt[1] == -1: tupleprdt[i] = Z return tupleprdt
[docs] def WF_Generate(params, **kwargs): """ **kwargs: W_x, W_y: Required - list of one element consisting the index of x, y part of one qubit. q: Optional - gives the number of times H has to tensor product with itself. Useful to form WF based on the highest index of decoupling lists of Wx, Wy. Returns: lstPaulOp - a list of Pauli Ops based on the W.I associated with that qubit. """ wx, wy, lst, q, signTuple, lstPaulOp = kwargs['wx'], kwargs['wy'], [], 0, [], [] H0, H1 = np.eye(1), H if 'q' in kwargs: q = kwargs['q'] else: q = int(np.ceil(np.log2(max(wx, wy)+1))) if q == 0: lst = [H0] else: lst = [H1] for i in range(q-1): lst += [H1] Hf = reduce(np.kron, lst) wfx, wfy = Hf[wx], Hf[wy] for i, wfx_k in enumerate(wfx): signTuple += [(wfx_k, wfy[i])] lstPaulOp = WF_Conditions(signTuple) return lstPaulOp
[docs] def WF_WIList(params, **kwargs): """ **kwargs: Wx, Wy - list consisting the index of x, y part of each qubit. Returns: Pseq - Pulse sequence. """ Wx, Wy, lstPaulOp, Pseq = kwargs['Wx'], kwargs['Wy'], [], [] q = int(np.ceil(np.log2(max(max(Wx, Wy))+1))) for i, wx in enumerate(Wx): lstPaulOp += [WF_Generate(params, wx = wx, wy = Wy[i], q = q)] padded_lstPaulOp = list(zip(*itertools.zip_longest(*lstPaulOp, fillvalue=I))) pseq_k = [[] for _ in range(len(padded_lstPaulOp[0]))] for i, ps_k in enumerate(pseq_k): for j, paulop in enumerate(padded_lstPaulOp): pseq_k[i] += [paulop[i]] for i, ps_k in enumerate(pseq_k): Pseq += [reduce(np.kron, ps_k)] return Pseq
[docs] def WPSresource_Hamiltonian_TimeEvolOp_IsingType(params, **kwargs): """ Returns: Resource Hamiltonian (Hr) and its time evolution for τ time. """ N, opH, unitary_timeOp = params['N'], params['opH'], 0 H_r, R, r, alpha = np.zeros((2**N, 2**N), dtype = complex), params['R'], params['r'], params['alpha'] lst = [I for _ in range(N)] for op in opH: for i in range(N): for j in range(i+1, N, 1): lst[i] = op lst[j] = op H_r += (np.abs(R[i]-R[j]))*reduce(np.kron, lst)/(np.power(np.abs(i-j), alpha)) lst = [I for _ in range(N)] tau = params['tau'] unitary_timeOp = expm(-1j*tau*H_r/(params['n'])) return H_r, unitary_timeOp
[docs] def WPSeq_TimeEvolOp(params, **kwargs): """ To input any Hamiltonian other than XY, use 'Hr' in kwargs. **kwargs: Hr Optional Returns: Unitary time evolution operator as per eq1 and time interval based on τ step. """ Pseq, unitary_timeOp, timeOpPHrP = params['pulses'], [], np.eye(2**(params['N'])) if 'Hr' in kwargs: Hr = kwargs['Hr'] expHr = expm(-1j*params['tau']*Hr/(params['n'])) else: Hr, expHr = WPSresource_Hamiltonian_TimeEvolOp_IsingType(params) for i, p in enumerate(Pseq): timeOpPHrP = np.linalg.inv(p) @ expHr @ p @ timeOpPHrP t_list = np.arange(0, params['T'], params['tau']) unitary_timeOp = [np.linalg.matrix_power(timeOpPHrP, i) for i, t in enumerate(t_list)] return unitary_timeOp, t_list