def add_noise_generic_heis2(self, matrix, spectral_power_density, A_noise_power, H_pulse=None): ''' add generic noise model same as add_noise_generic, but for a exponentially varying hamiltonian with saturation, see docs. ''' spectrum = lambda u, x=spectral_power_density: x(u)*A_noise_power my_noise = noise_desciption(SPECTRUM_NOISE, spectrum, 0) H_data = hamiltonian_data(matrix, pulse(), signal_type.SWO2, noise = my_noise) self.hamiltonian_data += H_data
def add_noise_static_heis2(self, matrix, gamma): ''' add static noise model same as add_noise_static, but for a exponentially varying hamiltonian with saturation, see docs. ''' # my_noise = noise_desciption(STATIC_NOISE, None, 2./(2.*np.pi*T2)**2) my_noise = noise_desciption(STATIC_NOISE, None, gamma) H_data = hamiltonian_data(matrix, pulse(), signal_type.SWO2, noise = my_noise) self.hamiltonian_data += H_data
def add_H0(self, matrix, amplitude): ''' add a constant hamiltonian to the system Args: matrix (np.ndarray[dtype=np.complex, ndim=2]) : matrix element of the Hamiltonian (e.g. Pauli Z matrix) amplitude (double) : amplitude of the matrix element (e.g. 1e7 (Hz)) ''' H_pulse = pulse() H_pulse.add_block(0,-1,amplitude) H_data = hamiltonian_data(matrix, H_pulse,signal_type.NORMAL) self.hamiltonian_data += H_data
def add_noise_static(self, matrix, T2, H_pulse=None): ''' add static noise model Args: matrix (np.ndarray[dtype=np.complex, ndim=2]) : input matrix on what the noise needs to act. T2 (double) : the T2 you which you want to provide. TODO (later) H_pulse (pulse) : pulse describing a modulation of the noise. Optional variable ''' my_noise = noise_desciption(STATIC_NOISE, None, 2./(2.*np.pi*T2)**2) H_data = hamiltonian_data(matrix, pulse(), signal_type.NORMAL, noise = my_noise) self.hamiltonian_data += H_data
def add_noise_generic(self, matrix, spectral_power_density, A_noise_power, H_pulse=None): ''' add generic noise model Args: matrix (np.ndarray[dtype=np.complex, ndim=2]) : input matrix on what the noise needs to act. spectral_power_density (lamda) : function describing S(omega) (frequency expected in 2pi*f) A_noise_power (double) : the noise power to provide. TODO (later) H_pulse (pulse) : pulse describing a modulation of the noise. Optional variable ''' spectrum = lambda u, x=spectral_power_density: x(u)*A_noise_power my_noise = noise_desciption(SPECTRUM_NOISE, spectrum, 0) H_data = hamiltonian_data(matrix, pulse(), signal_type.NORMAL, noise = my_noise) self.hamiltonian_data += H_data
def __init__(self, list_frequency, list_exchange, pos_q1=0, pos_q2=1): # Set up Hamiltonian self.position_Q1 = pos_q1 self.position_Q2 = pos_q2 self.f_qubits = list_frequency self.ex_qubits = list_exchange self.number_qubits = np.count_nonzero(self.f_qubits) self.dimension = 2**self.number_qubits #Time of the simulation in nanoseconds self.ramp_time = 10 self.total_time = (1. / j_max / 2 + self.ramp_time) self.delta_z = (f1 - f2) * 1e9 self.exchange_dc = j_max * 1e9 self.H_zeeman = np.zeros([self.dimension, self.dimension], dtype=np.complex) self.H_zeeman[1, 1] = 1 / 2 self.H_zeeman[2, 2] = -1 / 2 self.H_heisenberg = np.zeros([4, 4], dtype=np.complex) self.H_heisenberg[1, 1] = -1 / 2 self.H_heisenberg[2, 2] = -1 / 2 self.H_heisenberg[1, 2] = 1 / 2 self.H_heisenberg[2, 1] = 1 / 2 oneoverfnoise = lambda omega: 1 / 2 / np.pi / omega #whitenoise=lambda omega: 0.*omega + 1 self.init = np.zeros([4, 4], dtype=np.complex) self.init[2, 2] = 1 #self.init[2,2] = 1/2 #self.init[1,2] = 1/2 #self.init[2,1] = 1/2 self.pulseDummy = pgen.pulse() def add_dc_exchange_pulse(self, t_ramp, t_start, t_stop): delay = 1 def enevlope_fun(time): return self.exchange_dc * (np.arctan(3) + np.arctan( 6 * (time - delay / 2) / delay)) / (2 * np.arctan(3)) def enevlope_fun_ss(time): return self.exchange_dc * (np.arctan(3) + np.arctan( 6 * (1 - time - delay / 2) / delay)) / (2 * np.arctan(3)) #self.pulseDummy.add_ramp(t_start,(t_start+t_ramp),self.exchange_dc) self.pulseDummy.add_function(t_start, (t_start + t_ramp), enevlope_fun) self.pulseDummy.add_block((t_start + t_ramp), (t_stop - t_ramp), self.exchange_dc) #self.pulseDummy.add_ramp_ss((t_stop-t_ramp),t_stop,self.exchange_dc,0) self.pulseDummy.add_function((t_stop - t_ramp), t_stop, enevlope_fun_ss) self.solver_obj.add_H1(2 * np.pi * self.H_heisenberg, self.pulseDummy) # Create the solver #self.solver_obj = DM.calculate_evolution(4) # Add the init hamiltonian self.solver_obj = DM.DM_solver() self.solver_obj.add_H0(2 * np.pi * self.H_zeeman, self.delta_z) #self.solver_obj.add_noise_static(self.H_zeeman,18*1e6) #self.solver_obj.add_noise_generic(self.H_heisenberg,oneoverfnoise,self.exchange_dc/100) add_dc_exchange_pulse(self, self.ramp_time, 0., self.total_time) #self.pulseDummy.add_ramp(0,9.4,self.exchange_dc) #self.pulseDummy.add_block(9.4,50-9.4,self.exchange_dc) #self.pulseDummy.add_ramp_ss(50-9.4,50,self.exchange_dc,0) #self.solver_obj.add_H1(self.H_heisenberg,self.pulseDummy) self.solver_obj.calculate_evolution(self.init, self.total_time, 50000, 10) self.solver_obj.plot_pop() #print(2*np.pi*self.delta_z*(self.total_time*1e-9)) #print(2*np.pi*np.sum(self.pulseDummy.get_pulse(self.total_time,1e11))*1e-11) #print(qt.Qobj(self.solver_obj.return_final_unitary())) def get_unitary_gate_fidelity(self, U=None): """ returns unitary gate fidelity Args runs (str/tuple) : number of runs to compute the average gate fidelity """ self.solver_obj.calculate_evolution(self.init, self.total_time, 50000, 1) U = qt.Qobj(self.solver_obj.get_unitary()[0]) target = qt.Qobj( sp.linalg.expm(-np.pi / 4. * 1j * sp.sparse.diags([0., -1., -1., 0.]).todense())) temp_phase = self.delta_z * (self.total_time * 1e-9) SQphase = qt.Qobj( sp.linalg.expm(-2 * np.pi * 1j * temp_phase * self.H_zeeman)) fidelity = qt.average_gate_fidelity(U, target * SQphase) return fidelity def get_average_gate_fidelity(self, runs=500, target=None): """ returns average gate fidelity Args runs (int) : number of runs to compute the average gate fidelity target (4x4 numpy array) : target unitary to compute fidelity """ self.solver_obj.calculate_evolution(self.init, self.total_time, 10000, 1) U_ideal = self.solver_obj.get_unitary()[0] self.solver_obj.add_noise_static(2 * np.pi * self.H_zeeman, 18 * 1e-6) self.solver_obj.add_noise_generic(2 * np.pi * self.H_heisenberg, oneoverfnoise, self.exchange_dc / 100) self.solver_obj.calculate_evolution(self.init, self.total_time, 10000, int(runs)) U_list = self.solver_obj.get_unitary() # Calculate the averaged super operator in the Lioville superoperator form using column convention basis = [qt.basis(4, it) for it in range(4)] superoperator_basis = [ basis_it1 * basis_it2.dag() for basis_it2 in basis for basis_it1 in basis ] averaged_map = np.zeros([16, 16], dtype=np.complex) for u in U_list: temp_U = qt.Qobj(u) output_density = list() for it in range(len(superoperator_basis)): temp_vec = np.array( qt.operator_to_vector( temp_U * superoperator_basis[it] * temp_U.dag() / float(runs)).full()).flatten() output_density.append(np.array(temp_vec)) averaged_map = np.add(averaged_map, np.array(output_density)) # Define the target unitary operation if target is None: target = sp.linalg.expm( -np.pi / 2. * 1j * sp.sparse.diags([0., -1., -1., 0.]).todense()) # get phase from optimizing noiseless unitary evolution def to_minimize_fidelity(theta): temp_z_gate = np.matmul( sp.linalg.expm(-2 * np.pi * 1j * theta * self.H_zeeman), U_ideal) temp_m = np.matmul(sp.conjugate(sp.transpose(target)), temp_z_gate) return np.real(1. - (sp.trace( np.matmul(temp_m, sp.conjugate(sp.transpose(temp_m)))) + np.abs(sp.trace(temp_m))**2.) / 20.) ideal_phase = sp.optimize.minimize( to_minimize_fidelity, [self.delta_z * (self.total_time * 1e-9)], method='Nelder-Mead', tol=1e-6).x[0] target = np.matmul( sp.linalg.expm(2 * np.pi * 1j * ideal_phase * self.H_zeeman), target) # Change the shape of the averaged super operator to match the definitions used in QuTip (row convention) target = qt.Qobj(target) averaged_map = qt.Qobj(averaged_map).trans() averaged_map._type = 'super' averaged_map.dims = [[[4], [4]], [[4], [4]]] averaged_map.superrep = qt.to_super(target).superrep # Calculate the average gate fidelity of the superoperator with the target unitary gate fidelity = qt.average_gate_fidelity(averaged_map, target) return fidelity def show_pulse_sequence(self): t, v = self.pulseDummy.get_pulse(self.total_time, 1e11) plt.plot(t, v) plt.xlabel('time (ns)') plt.ylabel('amplitude (a.u.)') plt.show() #print(self.solver_obj.get_unitary()) print((1 - get_average_gate_fidelity(self)) * 100)
def __init__(self, leverarm=0.021, offset=1e4, j_sat=0.300, b_diff=0.200, j_dc=0.025, noise_strength=1. * 1e-5): #Time of the simulation in nanoseconds # add offset to plotting # think of a way to scale down #Preset fundamental system parameters self.delta_z = b_diff * 1e9 self.exchange_sat = j_sat * 1e9 self.vB_leverarm = leverarm self.exchange_residual = offset self.noise_amplitude_voltage = noise_strength * self.vB_leverarm #Preset Hamiltonians self.H_zeeman = 2. * np.pi * ( qt.tensor(qt.sigmaz(), qt.qeye(2)) / 4. - qt.tensor(qt.qeye(2), qt.sigmaz()) / 4.)[:, :] self.H_zeeman_Q1 = 2. * np.pi * (qt.tensor(qt.sigmaz(), qt.qeye(2)) / 2.)[:, :] self.H_zeeman_Q2 = 2. * np.pi * (qt.tensor(qt.qeye(2), qt.sigmaz()) / 2.)[:, :] self.H_zeeman_ac = 2. * np.pi * ( qt.tensor(qt.sigmax(), qt.qeye(2)) / 2. + qt.tensor(qt.qeye(2), qt.sigmax()) / 2.)[:, :] #Preset exchange Hamiltonian. Multiplication with saturated exchange interaction is necessary since formula is neither linear nor exponentional, thus, factors cannot be moved infront of whole formula. self.H_heisenberg_raw = 2. * np.pi * ( (qt.tensor(qt.sigmax(), qt.sigmax()) + qt.tensor( qt.sigmay(), qt.sigmay()) + qt.tensor(qt.sigmaz(), qt.sigmaz()) - qt.tensor(qt.qeye(2), qt.qeye(2))) / 4.)[:, :] self.H_heisenberg = self.H_heisenberg_raw * self.exchange_sat #whitenoise=lambda omega: 0.*omega + 1 self.init = np.zeros([4, 4], dtype=np.complex) self.init[1, 1] = 1. # formula to set value of residual exchange coupling in Hz def residual_exchange(res_ex): if self.exchange_sat <= res_ex: print( "Saturation level is greater or equal than residual exchange" ) return None else: #return np.log(np.sqrt(self.exchange_sat*res_ex)/(self.exchange_sat-res_ex)) return exchange_sat_inverse(res_ex / self.exchange_sat) self.offset = residual_exchange(self.exchange_residual) self.exchange_dc = j_dc * 1e9 self.runs = int(5000) # self.total_time=15000 self.delta_z = b_diff * 1e9 self.exchange_dc = [self.exchange_dc / 5., self.exchange_dc] self.vB_operation_point = [(exchange_sat_inverse( (exchange_it + 0. * self.exchange_residual) / self.exchange_sat) - self.offset) / self.vB_leverarm for exchange_it in self.exchange_dc] #Set initial states for exchange settings self.init_states_ST = list() self.init_states_Bell = list() for exch in self.exchange_dc: eig_val, eig_vec = sp.linalg.eigh( np.diag(1e10 * np.array([1., 0., 0., -1.])) + self.H_zeeman * self.delta_z + self.H_heisenberg_raw * exch) temp_vec = (qt.Qobj(eig_vec[1]) + qt.Qobj(eig_vec[2])) / np.sqrt(2) self.init_states_ST.append( np.array((temp_vec * temp_vec.dag()).full())) temp_vec = (qt.Qobj(eig_vec[0]) + qt.Qobj(eig_vec[1])) / np.sqrt(2) self.init_states_Bell.append( np.array((temp_vec * temp_vec.dag()).full())) # Set up noise parameters oneoverfnoise = lambda omega: 1. / 2. / np.pi / omega self.T2sQ1 = 15. * 1e-5 #1.7*1e-6 self.T2sQ2 = 14. * 1e-5 #1.2*1e-6 # Is Ramsey possible to map full decay of charge noise in exchange # Set up noise Hamiltonians self.noise_amplitude_voltage_list = np.linspace(0.01, 0.1, 2) * self.vB_leverarm # self.noise_amplitude_voltage_list = np.linspace(0.1,1.,10)*1e-6 self.total_time_exch = [1000., 1000.] # self.list_exp_Q1 = list() # self.list_exp_Q2 = list() self.list_exp_J_ST = list() self.list_exp_J_Bell = list() self.time = [0., 0.] for iterator in range(len(self.total_time_exch)): # Add noise to Hamiltonian self.solver_obj = DM.DM_solver() self.solver_obj.add_H0(self.H_zeeman, self.delta_z) exchange_pulse = pgen.pulse() exchange_pulse.add_offset(self.vB_operation_point[1] * self.vB_leverarm + self.offset) self.solver_obj.add_H1_expsat(self.H_heisenberg, exchange_pulse) # self.solver_obj.add_noise_static(self.H_zeeman_Q1,self.T2sQ1) # self.solver_obj.add_noise_static(self.H_zeeman_Q2,self.T2sQ2) self.solver_obj.add_noise_generic_expsat( self.H_heisenberg, oneoverfnoise, self.noise_amplitude_voltage_list[1]) self.init = self.init_states_ST[iterator] self.solver_obj.calculate_evolution( self.init, self.total_time_exch[iterator], self.total_time_exch[iterator] * 10, self.runs) temp_list, temp_time = self.solver_obj.return_expectation_values_general( [self.init]) self.list_exp_J_ST.append(temp_list[0]) self.init = self.init_states_Bell[iterator] self.solver_obj.calculate_evolution( self.init, self.total_time_exch[iterator], self.total_time_exch[iterator] * 10, self.runs) temp_list, temp_time = self.solver_obj.return_expectation_values_general( [self.init]) self.list_exp_J_Bell.append(temp_list[0]) self.time[iterator] = temp_time # np.savetxt("./data/calibration_exchange_ST_5000.csv", np.array(self.list_exp_J_ST), delimiter=',') np.savetxt("./data/calibration_exchange_ST_j_5_bz_02_dv_01.csv", np.array(self.list_exp_J_ST[0]), delimiter=',') np.savetxt("./data/calibration_exchange_ST_j_25_bz_02_dv_01.csv", np.array(self.list_exp_J_ST[1]), delimiter=',') np.savetxt("./data/calibration_exchange_Bell_j_5_bz_02_dv_01.csv", np.array(self.list_exp_J_Bell[0]), delimiter=',') np.savetxt("./data/calibration_exchange_Bell_j_25_bz_02_dv_01.csv", np.array(self.list_exp_J_Bell[1]), delimiter=',') fig, axs = plt.subplots(2, 1) expect = self.list_exp_J_ST[0] axs[0].plot(self.time[0], expect, 'b', label="J = 5MHz") axs[0].set_xlabel("time (ns)") axs[0].set_ylabel("expectation |01>+|10>") axs[0].set_xlim(0, 1000) axs[0].grid(True) expect = self.list_exp_J_ST[1] axs[1].plot(self.time[1], expect, 'b', label="J = 25MHz") axs[1].set_xlabel("time (ns)") axs[1].set_ylabel("expectation |01>+|10>") axs[1].set_xlim(0, 1000) axs[1].grid(True) fig.savefig('./dephasing_exchange_bell.png', dpi=1200) plt.show()