def two_qubit_cliffords(): '''returns the two qubit clifford group as a list of qutip superoperators''' rot_cliffords = [qt.identity(2), qt.rotation((2/(np.sqrt(3))),(qt.sigmax()+qt.sigmay()+qt.sigmaz())/3),qt.rotation((4/(np.sqrt(3))),(qt.sigmax()+qt.sigmay()+qt.sigmaz())/3)] rot_class_cliffords = [qt.to_super(qt.tensor(x,y)) for x in rot_cliffords for y in rot_cliffords] class_one_cliffords = [qt.to_super(qt.tensor(x,y)) for x in qt.qubit_clifford_group() for y in qt.qubit_clifford_group()] class_two_cliffords = [x*qt.to_super(qt.cnot())*y for x in class_one_cliffords for y in rot_class_cliffords] class_three_cliffords = [x*qt.to_super(qt.iswap())*y for x in class_one_cliffords for y in rot_class_cliffords] class_four_cliffords = [x*qt.to_super(qt.swap()) for x in class_one_cliffords] return class_one_cliffords+class_two_cliffords+ class_three_cliffords+ class_four_cliffords
def max_fidelity(rho1: qtp.Qobj, rho2: qtp.Qobj, thetas1, thetas2): fid_vec = np.zeros((len(thetas1), len(thetas2))) rho1 = qtp.Qobj(rho1, dims=[[2, 2], [2, 2]], shape=(4, 4)) target_bell = qtp.Qobj(rho2.flatten(), dims=[[2, 2], [1, 1]], shape=(4, 1)) for i, theta1 in enumerate(thetas1): for j, theta2 in enumerate(thetas2): state_rotation = qtp.tensor(qtp.rotation(qtp.sigmaz(), theta1), qtp.rotation(qtp.sigmaz(), theta2)) target_state = state_rotation*target_bell fid_vec[i][j] = float( np.real((target_state.dag()*rho1*target_state).data[0, 0])) idxs_f_max = np.unravel_index(np.argmax(fid_vec), dims=(len(thetas1), len(thetas1))) max_F = 100*np.max(fid_vec) phase1 = thetas1[idxs_f_max[0]]*180./np.pi phase2 = thetas2[idxs_f_max[1]]*180./np.pi return max_F, phase1, phase2
def setUpClass(self): self.standard_pulses = { 'I': qtp.qeye(2), 'Z0': qtp.qeye(2), 'X180': qtp.sigmax(), 'mX180': qtp.sigmax(), 'Y180': qtp.sigmay(), 'mY180': qtp.sigmay(), 'X90': qtp.rotation(qtp.sigmax(), np.pi / 2), 'mX90': qtp.rotation(qtp.sigmax(), -np.pi / 2), 'Y90': qtp.rotation(qtp.sigmay(), np.pi / 2), 'mY90': qtp.rotation(qtp.sigmay(), -np.pi / 2), 'Z90': qtp.rotation(qtp.sigmaz(), np.pi / 2), 'mZ90': qtp.rotation(qtp.sigmaz(), -np.pi / 2), 'Z180': qtp.sigmaz(), 'mZ180': qtp.sigmaz(), 'CZ': qtp.gates.cphase(np.pi) }
import numpy as np import qutip as qtp from pycqed.analysis import analysis_toolbox as a_tools from pycqed.analysis import composite_analysis as ca from matplotlib import pyplot as plt from mpl_toolkits.axes_grid1 import make_axes_locatable from plotting_tools import * import time import os rotation_matrixes = [ qtp.qeye(2).full(), qtp.sigmax().full(), qtp.rotation(qtp.sigmay(), np.pi / 2).full(), qtp.rotation(qtp.sigmay(), -np.pi / 2).full(), qtp.rotation(qtp.sigmax(), np.pi / 2).full(), qtp.rotation(qtp.sigmax(), -np.pi / 2).full() ] pauli_matrixes = [ qtp.qeye(2).full(), qtp.sigmax().full(), qtp.sigmay().full(), qtp.sigmaz().full() ] pauli_ro = [qtp.qeye(2).full(), qtp.sigmaz().full()] def get_rotation(idx): # j inner loop # i outer loop j = idx % 6
class TomoAnalysis(): """Performs state tomography based on an overcomplete set of measurements and calibration measurements. Uses qutip to calculate resulting basis states from applied rotations Uses binary counting as general guideline in ordering states. Calculates rotations by using the qutip library BEFORE YOU USE THIS SET THE CORRECT ORDER BY CHANGING 'rotation_matrices' 'measurement_basis' + 'measurement_basis_labels' to values corresponding to your experiment and maybe 'readout_basis' """ # The set of single qubit rotation matrices used in the tomography # measurement (will be assumed to be used on all qubits) rotation_matrices = [qt.identity(2), qt.sigmax(), qt.rotation( qt.sigmay(), np.pi / 2), qt.rotation(qt.sigmay(), -1*np.pi / 2), qt.rotation(qt.sigmax(), np.pi / 2), qt.rotation(qt.sigmax(), -np.pi / 2)] measurement_operator_labels = ['I', 'X', 'y', '-y', 'x','-x'] #MAKE SURE THE LABELS CORRESPOND TO THE ROTATION MATRICES DEFINED ABOVE # The set of single qubit basis operators and labels (normalized) measurement_basis = [ qt.identity(2) , qt.sigmaz() , qt.sigmax() , qt.sigmay() ] measurement_basis_labels = ['I', 'Z', 'X', 'Y'] # The operators used in the readout basis on each qubit readout_basis = [qt.identity(2) , qt.sigmaz() ] def __init__(self, n_qubits=2, check_labels=False): """ keyword arguments: measurements_cal --- Should be an array of length 2 ** n_qubits measurements_tomo --- Should be an array of length length(rotation_matrices) ** n_qubits n_qubits --- default(2) the amount of qubits present in the expirement n_quadratures --- default(1(either I or Q)) The amount of complete measurement data sets. For example a combined IQ measurement has 2 measurement sets. tomo_vars : since this tomo does not have access to the original data, the vars should be given by tomo_var_i = 1 / N_i * np.var(M_i) where i stands for the data corresponding to rotation i. """ self.n_qubits = n_qubits self.n_states = 2 ** n_qubits # Generate the vectors of matrices that correspond to all measurements, # readout bases and rotations self.basis_vector = self._calculate_matrix_set( self.measurement_basis, n_qubits) self.readout_vector = self._calculate_matrix_set( self.readout_basis, n_qubits) self.rotation_vector = self._calculate_matrix_set( self.rotation_matrices, n_qubits) # generate the basis change matrix from pauli to comp and back A = np.zeros((self.n_states**2, self.n_states**2), dtype=complex) for i in range(self.n_states**2): # do an orthonormal transformation. A[:,i] = np.ravel(self.basis_vector[i].full()) self.basis_pauli_to_comp_trafo_matrix= A / self.n_states self.basis_comp_to_pauli_trafo_matrix= np.linalg.inv(A) #get dims of qutip objects self.qt_dims = [[2 for i in range(self.n_qubits)], [2 for i in range(self.n_qubits)]] if check_labels is True: # prints the order of basis set corresponding to the # tomographic rotations print(self.get_meas_operator_labels(n_qubits)) print(self.get_basis_labels(n_qubits)) def execute_pseudo_inverse_tomo(self, meas_operators, meas_tomo, use_pauli_basis=False, verbose=False): """ Performs a linear tomography by simple inversion of the system of equations due to calibration points TE_correction_matrix: a matrix multiplying the calibration points to correct for estimated mixture due to Thermal excitation. """ #some argument parsing to we allow more general input meas_operators = [meas_operators] if type(meas_operators) == qt.Qobj else meas_operators #for each independent set of measurements(with their own measurement operator, calculate the coeff matrix) coeff_matrices = [] for measurement_operator in meas_operators: coeff_matrices.append(self.calculate_LI_coefficient_matrix(measurement_operator, do_in_pauli=use_pauli_basis)) coefficient_matrix = np.vstack(coeff_matrices) basis_decomposition = np.zeros(4 ** self.n_qubits, dtype=complex) if use_pauli_basis: # first skip beta0 #basis_decomposition[1:] = np.dot(np.linalg.pinv(coefficient_matrix[:, 1:]), meas_tomo) basis_decomposition[:] = np.dot(np.linalg.pinv(coefficient_matrix[:, :]), meas_tomo) # re-add beta0 #basis_decomposition[0] = betas[0] rho = self.trans_pauli_to_comp(basis_decomposition) else: basis_decomposition = np.conj(np.linalg.pinv(coefficient_matrix)).dot(meas_tomo) rho = qt.Qobj(np.reshape(basis_decomposition, [self.n_states, self.n_states]), dims=self.qt_dims) return (basis_decomposition, rho) def execute_mle_T_matrix_tomo(self, measurement_operators, meas_tomo, weights_tomo =False, show_time=True, ftol=0.01, xtol=0.001, full_output=0, max_iter=100, TE_correction_matrix = None): """ Performs a least squares optimization using fmin_powell in order to get the closest physically realisable state. This is done by constructing a lower triangular matrix T consisting of 4 ** n qubits params Keyword arguments: measurement_operators: list of meas operators meas_tomo: list of n_rot measurements beloning to the measurmeent operator use_weights : default(False) Weighs the quadrature data by the std in the estimator of the mean : since this tomo does not have access to the original data, the vars should be given by tomo_var_i = 1 / N_i * np.var(M_i) where i stands for the data corresponding to rotation i. --- arguments for scipy fmin_powel method below, see the powell documentation """ # first we calculate the measurement matrices tstart = time.time() measurement_operators = [measurement_operators] if type(measurement_operators) == qt.Qobj else measurement_operators measurement_vectors = [] for measurement_operator in measurement_operators: measurement_vectors.append([m.full() for m in self.get_measurement_vector(measurement_operator)]) measurement_vector = np.vstack(measurement_vectors) # initiate with equal weights self.weights = weights_tomo if weights_tomo else np.ones(len(measurement_vector)) # save it in the object for use in optimization self.measurements_tomo = meas_tomo self.measurement_vector_numpy = measurement_vector tlinear = time.time() # find out the starting rho by the linear tomo discard, rho0 = self.execute_pseudo_inverse_tomo(measurement_operators, meas_tomo) # now fetch the starting t_params from the cholesky decomp of rho tcholesky = time.time() T0 = np.linalg.cholesky(scipy.linalg.sqrtm((rho0.dag() * rho0).full())) t0 = np.zeros(4 ** self.n_qubits, dtype='complex' ) di = np.diag_indices(2 ** self.n_qubits) tri = np.tril_indices(2 ** self.n_qubits, -1) t0[0:2 ** self.n_qubits] = T0[di] t0[2**self.n_qubits::2] = T0[tri].real t0[2**self.n_qubits+1::2] = T0[tri].imag topt = time.time() # minimize the likelihood function using scipy t_optimal = scipy.optimize.fmin_powell( self._max_likelihood_optimization_function, t0, maxiter=max_iter, full_output=full_output, ftol=ftol, xtol=xtol) if show_time is True: print(" Time to calc rotation matrices %.2f " % (tlinear-tstart)) print(" Time to do linear tomo %.2f " % (tcholesky-tlinear)) print(" Time to build T %.2f " % (topt-tcholesky)) print(" Time to optimize %.2f" % (time.time()-topt)) return qt.Qobj(self.build_rho_from_triangular_params(t_optimal), dims=self.qt_dims) def execute_SDPA_2qubit_tomo(self, measurement_operators, counts_tomo, N_total=1, used_bins=[0,3], correct_measurement_operators=True, calc_chi_squared =False, correct_zero_count_bins=True, TE_correction_matrix = None): """ Estimates a density matrix given single shot counts of 4 thresholded bins using a custom C semidefinite solver from Nathan Langford Each bin should correspond to a projection operator: 0: 00, 1: 01, 2: 10, 3: 11 The calibration counts are used in calculating corrections to the (ideal) measurement operators The tomo counts are used for the actual reconstruction. N_total is used for normalization. if counts_tomo is normalized use N_total=1 """ # If the tomography bins have zero counts, they not satisfy gaussian noise. If N>>1 then turning them into 1 fixes # convergence problems without screwing the total statistics/estimate. # if(np.sum(np.where(np.array(counts_tomo) == 0)) > 0): # print("WARNING: Some bins contain zero counts, this violates gaussian assumptions. \n \ # If correct_zero_count_bins=True these will be set to 1 to minimize errors") if correct_zero_count_bins: counts_tomo = np.array([[int(b) if b > 0 else 1 for b in bin_counts] for bin_counts in counts_tomo]) #Select the correct data based on the bins used #(and therefore based on the projection operators used) data = counts_tomo[:,used_bins].T.flatten() #get the total number of counts per tomo N = np.array([np.sum(counts_tomo, axis=1) for k in used_bins]).flatten() # add weights based on the total number of data points kept each run # N_total is a bit arbitrary but should be the average number of total counts of all runs, since in nathans code this # average is estimated as a parameter. weights = N/float(N_total) #get the observables from the rotation operators and the bins kept(and their corresponding projection operators) measurement_vectors = [] for k in used_bins: measurement_vectors.append([m.full() for m in self.get_measurement_vector(measurement_operators[k])]) measurement_vector = np.vstack(measurement_vectors) # print('length of the measurement vector'len(measurement_vector)) #calculate the density matrix using the csdp solver a = time.time() rho_nathan = csdp_tomo.tomo_state(data, measurement_vector, weights) b=time.time()-a # print("time solving csdp: ", b) # print(rho_nathan) n_estimate = rho_nathan.trace() # print(n_estimate) rho = qt.Qobj(rho_nathan / n_estimate,dims=self.qt_dims) # if((np.abs(N_total - n_estimate) / N_total > 0.03)): # print('WARNING estimated N(%d) is not close to provided N(%d) '% (n_estimate,N_total)) if calc_chi_squared: chi_squared = self._state_tomo_goodness_of_fit(rho, data, N, measurement_vector) return rho, chi_squared else: return rho def execute_SDPA_MC_2qubit_tomo(self, measurement_operators, counts_tomo, N_total, used_bins = [0,2], n_runs = 100, array_like = False, correct_measurement_operators = True, TE_correction_matrix=None): """ Executes the SDPDA tomo n_runs times with data distributed via a Multinomial distribution in order to get a list of rhos from which one can calculate errorbars on various derived quantities returns a list of Qobjects (the rhos). If array_like is set to true it will just return a 3D array of rhos """ rhos= [] for i in range(n_runs): #generate a data set based on multinomial distribution with means according to the measured data mc = [np.random.multinomial(sum(counts),(np.array(counts)+0.0) / sum(counts)) for counts in counts_tomo] rhos.append(self.execute_SDPA_2qubit_tomo(measurement_operators, mc, N_total, used_bins, correct_measurement_operators, TE_correction_matrix=TE_correction_matrix)) if array_like: return np.array([rho.full() for rho in rhos]) else: return rhos def calculate_LI_coefficient_matrix(self, measurement_operator, do_in_pauli=False): """ Calculates the coefficient matrix used when inversing the linear system of equations needed to find rho Requires a calibrated measurement operator if do_in_pauli is true this will presume measurement operator is given in the basis. """ coefficient_matrix = np.zeros((len(self.rotation_matrices) ** self.n_qubits, 4 ** self.n_qubits), dtype=complex) Ms = self.get_measurement_vector(measurement_operator, do_in_pauli=do_in_pauli) for i in range(len(Ms)): coefficient_matrix[i,:] = np.ravel(Ms[i].full()) return coefficient_matrix def get_measurement_vector(self, measurement_operator, do_in_pauli=False): """ Returns a list of rotated measurement operator based on an initial measurement operator which should be obtained from the calibration """ n_rotations = len(self.rotation_matrices) ** self.n_qubits measurement_vector = [] for i in range(n_rotations): R = self.rotation_vector[i] if do_in_pauli: M = self.trans_comp_to_pauli(R.dag() * measurement_operator * R) else: M = R.dag() * measurement_operator * R measurement_vector.append(M) return measurement_vector def calibrate_measurement_operator(self, cal_meas, calibration_points=None, TE_correction_matrix=None, transform_to_pauli=False): """ Calibrates the measurement operator in any basis. Assumes cal_meas are the eigenvalues of the calibration_points defaults the calibration_points to be the computational basis projectors. If TE_correction_matrix is set, this alters the computational basis states """ if calibration_points is None: calibration_points = comp_projectors M = sum([cal_meas[i] * calibration_points[i] for i in range(len(cal_meas))]) return M if not transform_to_pauli else self.trans_comp_to_pauli(M) def calibrate_bin_operators(self, calibration_counts, calibration_points=None, normalize=False): M_bins = [] #calculate bin probabilities normalized horizontally if normalize: cal_probs = (calibration_counts / np.sum(calibration_counts, axis = 1, dtype=float)[:,np.newaxis]) else: cal_probs = np.array(calibration_counts)#(calibration_counts / np.sum(calibration_counts, axis = 1, dtype=float)[:,np.newaxis]) for probs in cal_probs.T: #calibrate the measurement operator for each bin in the same way as done with average tomo. M_bins.append(self.calibrate_measurement_operator(probs, calibration_points)) return M_bins # HELPERS def trans_pauli_to_comp(self, rho_pauli): """ Converts a rho in the pauli basis, or pauli basis vector or Qobj of rho in pauli basis to comp basis. """ if(rho_pauli.shape[0] == self.n_states): basis_decomposition = np.ravel(rho_pauli.full()) if (type(rho_pauli) == qt.Qobj) else np.ravel(rho_pauli) else: basis_decomposition = rho_pauli return qt.Qobj(np.reshape(self.basis_pauli_to_comp_trafo_matrix.dot(basis_decomposition), [self.n_states, self.n_states]), dims=self.qt_dims) def trans_comp_to_pauli(self, rho_comp): """ Converts a rho in the computational basis, or comp basis vector or Qobj of rho in computational basis to Pauli basis. """ if(rho_comp.shape[0] == self.n_states): basis_decomposition = np.ravel(rho_comp.full()) if (type(rho_comp) == qt.Qobj) else np.ravel(rho_comp) else: basis_decomposition = rho_comp return qt.Qobj(np.reshape(self.basis_comp_to_pauli_trafo_matrix.dot(basis_decomposition), [self.n_states, self.n_states]), dims=self.qt_dims) def _calculate_matrix_set(self, starting_set, n_qubits): """recursive function that returns len(starting_set) ** n_qubits measurement_basis states tensored with eachother based on the amount of qubits So for 2 qubits assuming your basis set is {I, X, Y, Z} you get II IX IY IZ XI XX XY XZ ... """ if(n_qubits > 1): return [qt.tensor(x, y) for x in self._calculate_matrix_set(starting_set, n_qubits - 1) for y in starting_set] else: return starting_set def get_basis_labels(self, n_qubits): """ Returns the basis labels in the same order as the basis vector is parsed. Requires self.measurement_basis_labels to be set with the correct order corresponding to the matrices in self.measurement_basis """ if(n_qubits > 1): return [x + y for x in self.get_basis_labels(n_qubits - 1) for y in self.measurement_basis_labels] else: return self.measurement_basis_labels def get_meas_operator_labels(self, n_qubits): """ Returns a vector of the rotations in order based on self.measurement_operator_labels """ if(n_qubits > 1): return [x + y for x in self.get_meas_operator_labels(n_qubits - 1) for y in self.measurement_operator_labels] else: return self.measurement_operator_labels ###############################3 # MLE T Matrix functions # def build_rho_from_triangular_params(self, t_params): # build the lower triangular matrix T T_mat = np.zeros( (2 ** self.n_qubits, 2 ** self.n_qubits), dtype="complex") di = np.diag_indices(2 ** self.n_qubits) T_mat[di] = t_params[0:2**self.n_qubits] tri = np.tril_indices(2 ** self.n_qubits, -1) T_mat[tri] = t_params[2**self.n_qubits::2] T_mat[tri] += 1j * t_params[2**self.n_qubits+1::2] rho = np.dot(np.conj(T_mat.T), T_mat) / \ np.trace(np.dot(np.conj(T_mat.T), T_mat)) return rho def _max_likelihood_optimization_function(self, t_params): """ Optimization function that is evaluated many times in the maximum likelihood method. Calculates the difference between expected measurement values and the actual measurement values based on a guessed rho Keyword arguments: t_params : cholesky decomp parameters used to construct the initial rho Requires: self.weights : weights per measurement vector used in calculating the loss """ rho = self.build_rho_from_triangular_params(t_params) L = 0 + 0j for i in range(len(self.measurement_vector_numpy)): expectation = np.trace( np.dot(self.measurement_vector_numpy[i], rho)) L += ((expectation - self.measurements_tomo[i]) ** 2) * self.weights[i] return L ############################################################################################# # CDSP tomo functions for likelihood. def _state_tomo_likelihood_function(self, rho, data, normalisations, observables, fixedweight=False): data_predicted = [] for ii in range(len(data)): data_predicted.append((rho.full().dot(observables[ii])).trace()*normalisations[ii]) data_predicted = np.array(data_predicted) if fixedweight: likely_function = np.sum( (data-data_predicted)**2/data ) else: likely_function = np.sum( (data-data_predicted)**2/data_predicted ) return likely_function """ Calculates the goodness of fit. It has a normalisation which is just the sum of the counts in the superconducting case since there are no missing counts like in the photon case. """ def _state_tomo_goodness_of_fit(self, rho, data, normalisations, observables, fixedweight=False, eig_cutoff=1e-6): likely_function = self._state_tomo_likelihood_function(rho, data, normalisations, observables, fixedweight=fixedweight) num_data = len(data) num_eigs = np.sum(rho.eigenenergies()>eig_cutoff) rho_dim = rho.shape[0] num_dofs = num_eigs*(2*rho_dim-num_eigs) out = {} out['pure'] = likely_function / (num_data-(2*rho_dim-1)) out['mixed'] = likely_function / (num_data-rho_dim**2) out['dofs'] = likely_function / (num_data-num_dofs) return out
import numpy as np import qutip as qt # initialize handy shortcuts using qutip zero = qt.basis(2, 0) # |0> one = qt.basis(2, 1) # |1> # shorthand for Paulis Z = qt.sigmaz() X = qt.sigmax() Y = qt.sigmay() Id = qt.identity(2) # shorthand for rotations Y2p = qt.rotation(Y, np.pi / 2) Y2m = qt.rotation(Y, -np.pi / 2) Z2p = qt.rotation(Z, np.pi / 2) Z2m = qt.rotation(Z, -np.pi / 2) X2p = qt.rotation(X, np.pi / 2) X2m = qt.rotation(X, -np.pi / 2) # Initialize density matrices for fiducial singlequbit states rho_0 = qt.ket2dm(zero) rho_1 = qt.ket2dm(one) rho_Xp = qt.ket2dm(Y2p * zero) rho_Xm = qt.ket2dm(Y2m * zero) rho_Yp = qt.ket2dm(X2m * zero) rho_Ym = qt.ket2dm(X2p * zero) # Initialize two qubit states
def evolve_densitymatrix_in_CPMG(self, m, rho0): """ Implementation of Lindblad master equation, based on Markovian approach (weak coupling) is carried out by considering the collapse operator including the influence of flux noise in the Hamiltonian. This module performs the numerical evolution of the total Hamiltonian within CPMG pulse sequence. Evolution for each pulse duration or the precession time duration between two adjacent/consecutive pulses can be handled by using QuTiP.mesolve that calculates the evolution of the density matrix. The mesolve in QuTiP is capable of handling time-dependent Hamiltonians and the terms with collapse operators. Note: This module is capable of simulating an arbitrary number of Np pulses. The first CPMG and measurement (pi/2)x +(pi)x -(pi)x (pi/2)y +---+ +--------+ +--------+ +---+ | | | | | | | |++++++++++ | | | | | | | | M1 + +---+------+--------+------------+--------+------+---+---------+ t0 t1 t2 t3 t4 t5 t6 t7 The second CPMG and measurement (pi/2)x +(pi)x -(pi)x (pi/2)y +---+ +--------+ +--------+ +---+ | | | | | | | |++++++++++ | | | | | | | | M2 + +---+------+--------+------------+--------+------+---+---------+ t8 t9 t10 t11 t12 t13 t14 t15 """ #-------------------------------------------------- # Generate subsequent measurement time interval (synchronized) #-------------------------------------------------- t = self.time #The synchronized time is generated during parameter setup Np = pars['Np'] # Index of m mp = 2 * m * (Np + 2) # Number of subintervals NumPro = pars['Num'] #-------------------------------------------------------- # Evolution in (pi/2)x between 0 and t0 for m=0; t[mp]-tau/2 and t[m] for m!=0 #-------------------------------------------- pars['Num'] = int(NumPro / 2) #test#print("For (pi/2)x: Num=",pars['Num']) rho = self.solveME(rho0, t[mp], t[mp + 1], evolType='(pi/2)x', theSys='qubit-HO') #rho = self.evolve_PiX(rho0, t[mp], t[mp+1], evolType='(pi/2)x', theSys='qubit-HO'); #-------------------------------------------- # Evolution in free precession time (half) between t[mp] and t[mp+1] #-------------------------------------------- if self.collapse == []: pars['dirname'] = [] pars['Num'] = int(NumPro) #test#print("For (free/2): Num=",pars['Num']) mm = m rho = self.solveME(rho, t[mp + 1], t[mp + 2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=0, m=mm) #rho = self.evolve_free(rho, t[mp+1], t[mp+2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=0, m=mm); sgn = 1.0 n = 1 while n <= Np: #-------------------------------------------- # Evolution in (pi)x between t[mp+2n-1] and t[mp+2n] #-------------------------------------------- pars['Num'] = int(NumPro) #test#print("For (pi)[",n,"]x: Num=",pars['Num']) rho = self.solveME(rho, t[mp + 2 * n], t[mp + 2 * n + 1], evolType='(pi)x', theSys='qubit-HO', sign=sgn) #rho = self.evolve_PiX(rho, t[mp+2*n], t[mp+2*n+1], evolType='(pi)x', theSys='qubit-HO', sign=sgn) #------------------------------------------ # Evolution in free precessing time between t[mp+2n] and t[mp+2n+1] #------------------------------------------ if n == pars['Np']: pars['Num'] = int(NumPro) #test#print("For (free/2): Num=",pars['Num']) else: pars['Num'] = int(2 * NumPro) #test#print("For (free): Num=",pars['Num']) mm = m nn = n rho = self.solveME(rho, t[mp + 2 * n + 1], t[mp + 2 * n + 2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=nn, m=mm) #rho = self.evolve_free(rho, t[mp+2*n+1], t[mp+2*n+2], evolType='free', dirname=pars['dirname'], theSys='qubit-HO', n=nn, m=mm); # Sign update for the next adjacent (pi)x pulse sgn = -sgn # # Update n for next evolution n += 1 # Evolution in (pi/2)y pulse pars['Num'] = int(NumPro / 2) #test#print("For (pi/2)y: Num=",pars['Num']) rho = self.solveME(rho, t[mp + 2 * Np + 2], t[mp + 2 * Np + 3], evolType='(pi/2)y', theSys='qubit-HO') #rho = self.evolve_PiY(rho, t[mp+2*Np+2], t[mp+2*Np+3], evolType='(pi/2)y', theSys='qubit-HO'); #Invert Number of subintervals pars['Num'] = int(NumPro) #------------------------------------------------ # Fidelity check for rho in CPMG and rho_avg #------------------------------------------------ if (m == 0): Uavg = rotation(self.sx, np.pi / 2) #varphi= 2.0 / np.pi * param['lambdaz'] * param['Te']; varphi = 2.0 / np.pi * pars['g'] * pars['Te'] #Time averaged Hamiltonian Up = (-1j * varphi * self.sz * (self.a + self.a.dag())).expm() Uavg = Up * Uavg if pars['Np'] % 2 != 0: Uavg = rotation(self.sx, np.pi) * Uavg Uavg = rotation(self.sy, np.pi / 2) * Uavg print ("State Fidelity(rho,rho_avg)=%.3lf where rho_avg=Uavg*rho0*Uavg.dag()"\ %fidelity(rho, Uavg * rho0 * Uavg.dag())) return rho
def standard_qubit_pulses_to_rotations(pulse_list: List[Tuple]) \ -> List[qtp.Qobj]: """ Converts lists of n-tuples of standard PycQED single-qubit pulse names to the corresponding rotation matrices on the n-qubit Hilbert space. """ standard_pulses = { 'I': qtp.qeye(2), 'X0': qtp.qeye(2), 'Z0': qtp.qeye(2), 'X180': qtp.rotation(qtp.sigmax(), np.pi), 'mX180': qtp.rotation(qtp.sigmax(), -np.pi), 'Y180': qtp.rotation(qtp.sigmay(), np.pi), 'mY180': qtp.rotation(qtp.sigmay(), -np.pi), 'X90': qtp.rotation(qtp.sigmax(), np.pi/2), 'mX90': qtp.rotation(qtp.sigmax(), -np.pi/2), 'Y90': qtp.rotation(qtp.sigmay(), np.pi/2), 'mY90': qtp.rotation(qtp.sigmay(), -np.pi/2), 'Z90': qtp.rotation(qtp.sigmaz(), np.pi/2), 'mZ90': qtp.rotation(qtp.sigmaz(), -np.pi/2), 'Z180': qtp.rotation(qtp.sigmaz(), np.pi), 'mZ180': qtp.rotation(qtp.sigmaz(), -np.pi), } rotations = [qtp.tensor(*[standard_pulses[pulse] for pulse in qb_pulses]) for qb_pulses in pulse_list] for i in range(len(rotations)): rotations[i].dims = [[d] for d in rotations[i].shape] return rotations