def e_ops_gen(params): projectors = gen_projectors(params) sm = tensor(sigmam(), qeye(params.c_levels)) a = tensor(qeye(2), destroy(params.c_levels)) base_e_ops = {'a': a, 'sm': sm, 'n': a.dag() * a} e_ops = base_e_ops.copy() for key, item in base_e_ops.items(): for i in range(2): for j in range(2): e_ops[key + '_' + str(i) + str(j)] = projectors[i] * item * projectors[j] for n in range(2): e_ops['p_q' + str(n)] = tensor(fock_dm(2, n), qeye(params.c_levels)) for n in range(params.c_levels): e_ops['p_c' + str(n)] = tensor(qeye(2), fock_dm(params.c_levels, n)) e_ops['P_0'] = projectors[0] e_ops['P_1'] = projectors[1] e_ops['P_01'] = projectors[0] * projectors[1] return e_ops
def controlled_mat3(arg_value): """ A qubit control an operator acting on a 3 level system """ control_value = arg_value dim = mat3.dims[0][0] return (tensor(fock_dm(2, control_value), mat3) + tensor(fock_dm(2, 1 - control_value), identity(dim)))
def adc_choi(x): kraus = [ np.sqrt(1 - x) * qeye(2), np.sqrt(x) * destroy(2), np.sqrt(x) * fock_dm(2, 0), ] return kraus_to_choi(kraus)
def __init__(self, nbrOfQubits=4, FockDim=3): self.nbrOfQubits = nbrOfQubits self.FockDim = FockDim self.qubits = [] for i in range(nbrOfQubits): self.qubits.append(qt.fock_dm(FockDim, 0))
def __init__(self, resonance_freq, anharmonicity, T1=0, T2=0): self.resonance_freq = resonance_freq self.anharmonicity = anharmonicity self.T1 = T1 self.T2 = T2 self.state = qt.fock_dm(3, 0) # 3-level qubit initialized in ground state
def testFockDensityMatrix(self): """ states: Fock density matrix """ N = 10 for i in range(N): rho = fock_dm(N, i) # make sure rho has trace close to 1.0 assert_(abs(rho.tr() - 1.0) < 1e-12) assert_(rho.data[i, i] == 1.0)
def test_smesolve_homodyne_methods(): "Stochastic: smesolve: homodyne methods with single jump operator" def arccoth(x): return 0.5*np.log((1.+x)/(x-1.)) th = 0.1 # Interaction parameter alpha = np.cos(th) beta = np.sin(th) gamma = 1. N = 30 # number of Fock states Id = qeye(N) a = destroy(N) s = 0.5*((alpha+beta)*a + (alpha-beta)*a.dag()) x = (a + a.dag()) * 2**-0.5 H = Id c_op = [gamma**0.5 * a] sc_op = [s] e_op = [x, x*x] rho0 = fock_dm(N,0) # initial vacuum state T = 6. # final time N_store = 200 # number of time steps for which we save the expectation values Nsub = 5 tlist = np.linspace(0, T, N_store) ddt = (tlist[1]-tlist[0]) #### Analytic solution y0 = 0.5 A = (gamma**2 + alpha**2 * (beta**2 + 4*gamma) - 2*alpha*beta*gamma)**0.5 B = arccoth((-4*alpha**2*y0 + alpha*beta - gamma)/A) y_an = (alpha*beta - gamma + A / np.tanh(0.5*A*tlist - B))/(4*alpha**2) list_methods_tol = [['euler-maruyama', 2e-2], ['fast-euler-maruyama', 2e-2], ['pc-euler', 2e-3], ['milstein', 1e-3], ['fast-milstein', 1e-3], ['milstein-imp', 1e-3], ['taylor15', 1e-4], ['taylor15-imp', 1e-4] ] for n_method in list_methods_tol: sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver = n_method[0]) sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver = n_method[0], noise = sol.noise) err = 1/T * np.sum(np.abs(y_an - (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt print(n_method[0], ': deviation =', err, ', tol =', n_method[1]) assert_(err < n_method[1]) assert_(np.all(sol.noise == sol2.noise))# just to check that noise is not affected by smesolve assert_(np.all(sol.expect[0] == sol2.expect[0]))
def cphase(theta, N=2, control=0, target=1): """ Returns quantum object representing the controlled phase shift gate. Parameters ---------- theta : float Phase rotation angle. N : integer The number of qubits in the target space. control : integer The index of the control qubit. target : integer The index of the target qubit. Returns ------- U : qobj Quantum object representation of controlled phase gate. """ if N < 1 or target < 0 or control < 0: raise ValueError("Minimum value: N=1, control=0 and target=0") if control >= N or target >= N: raise ValueError("control and target need to be smaller than N") U_list1 = [identity(2)] * N U_list2 = [identity(2)] * N U_list1[control] = fock_dm(2, 1) U_list1[target] = phasegate(theta) U_list2[control] = fock_dm(2, 0) U = tensor(U_list1) + tensor(U_list2) return U
def test_result_states(): N = 5 a = qutip.destroy(N) coefficient = np.random.random() + 1j * np.random.random() H = a.dag() * a + coefficient * a + np.conj(coefficient) * a.dag() H_brme = [[H, '1']] psi0 = qutip.fock_dm(N, 2) times = np.linspace(0, 10, 10) me = qutip.mesolve(H, psi0, times).states brme = qutip.brmesolve(H_brme, psi0, times).states assert max( np.abs((me_state - brme_state).full()).max() for me_state, brme_state in zip(me, brme)) < 1e-5
def test_thermal(): N = 10 beta = 0.5 assert qutip.thermal_dm(N, 0) == qutip.fock_dm(N, 0) thermal_operator = qutip.thermal_dm(N, beta) thermal_analytic = qutip.thermal_dm(N, beta, method="analytic") np.testing.assert_allclose(thermal_operator.full(), thermal_analytic.full(), atol=2e-5) with pytest.raises(ValueError) as e: qutip.thermal_dm(N, beta, method="other") assert str(e.value) == ("The method option can only take " "values 'operator' or 'analytic'")
def single_crosstalk_simulation(num_gates): """ A single simulation, with num_gates representing the number of rotations. Args: num_gates (int): The number of random gates to add in the simulation. Returns: result (qutip.solver.Result): A qutip Result object obtained from any of the solver methods such as mesolve. """ num_qubits = 2 # Qubit-0 is the target qubit. Qubit-1 suffers from crosstalk. myprocessor = ModelProcessor(model=MyModel(num_qubits)) # Add qubit frequency detuning 1.852MHz for the second qubit. myprocessor.add_drift(2 * np.pi * (sigmaz() + 1) / 2 * 1.852, targets=1) myprocessor.native_gates = None # Remove the native gates mycompiler = MyCompiler(num_qubits, { "pulse_amplitude": 0.02, "duration": 25 }) myprocessor.add_noise(ClassicalCrossTalk(1.0)) # Define a randome circuit. gates_set = [ Gate("ROT", 0, arg_value=0), Gate("ROT", 0, arg_value=np.pi / 2), Gate("ROT", 0, arg_value=np.pi), Gate("ROT", 0, arg_value=np.pi / 2 * 3), ] circuit = QubitCircuit(num_qubits) for ind in np.random.randint(0, 4, num_gates): circuit.add_gate(gates_set[ind]) # Simulate the circuit. myprocessor.load_circuit(circuit, compiler=mycompiler) init_state = tensor( [Qobj([[init_fid, 0], [0, 0.025]]), Qobj([[init_fid, 0], [0, 0.025]])]) options = SolverOptions(nsteps=10000) # increase the maximal allowed steps e_ops = [tensor([qeye(2), fock_dm(2)])] # observable # compute results of the run using a solver of choice with custom options result = myprocessor.run_state(init_state, solver="mesolve", options=options, e_ops=e_ops) result = result.expect[0][-1] # measured expectation value at the end return result
def osc_relax(): w = 1.0 # oscillator frequency kappa = 0.1 # relaxation rate a = destroy(10) # oscillator annihilation operator rho0 = fock_dm(10, 5) # initial state, fock state with 5 photons H = w * a.dag() * a # Hamiltonian # A list of collapse operators c_ops = [sqrt(kappa) * a] tlist = linspace(0, 50, 100) # request that the solver return the expectation value of the photon number state operator a.dag() * a result = mesolve(H, rho0, tlist, c_ops, [a.dag() * a]) print result fig, axes = plt.subplots(1,1) axes.plot(tlist, result.expect[0]) axes.set_xlabel(r'$t$', fontsize=20) axes.set_ylabel(r"Photon number", fontsize=16);
def __init__(self, resonance_freq, anharmonicity, fock_dim=3): self.fock_dim = fock_dim self.resonance_freq = resonance_freq self.anharmonicity = anharmonicity self.state = qt.fock_dm(fock_dim, 0)
def run_sim_full_and_approx(p): """ TODO: -Should get separate the approximate (rotating wave) evolution into its own function. Then the plotting routines should take a list of results, using different -Double check that coupling of higher qudit levels scales as standard lowering/rasing ops to a good approximation. -Add co-rotating terms evolution to approximate RF results. IMPORTANT: the passed in options 'p' should not be changed inside this function if one wants to run things in parallel via multiprocessing.Pool.map() Otherwise, in particular if p gets entries with new objects, can lead to things break down in non-trivial way. """ # if p.show_approx_evolution: # print("The codebase for the approx evolution needs a couple of minor updates with the recent changes...\n\n\n") # return None if p.N_q > 2 and p.show_approx_evolution: #Need to generalize the approximate treatment to more levels print( "ERROR: The approximate evolution (in terms of J_m functions) for now only handles a 2 level qudit...\n\ ... set p.show_approx_evolution=False in order to look at the full evolution instead.\n" ) return None #make sure the config is sane assert (len(p.mode_freqs) == len(p.mode_q_coupling_freqs) == len( p.mode_decay_rates)) assert (p.N_q == len(p.q_level_freqs) == len(p.q_level_freqs_time_dep) == len(p.q_level_dephasing_rate) == (len(p.q_level_decay_rate) + 1)) assert (p.non_rwa_terms == 0 or p.non_rwa_terms == 1) results = DictExtended() #keep a reference to the specific options/parameters that were used in the simulation along with the results results.p = DictExtended(p) #qubit lowering operator c = q.tensor(q.destroy(p.N_q), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) #mode lowering operators; a[j], gives the lowering operator for the jth mode (first is the 0th indexed) a = [] for j in xrange(p.mode_count): #prettier way to do this? temp_op_list = [q.qeye(p.N_q)] for i in xrange(p.mode_count): if i == j: temp_op_list.append(q.destroy(p.N_m)) else: temp_op_list.append(q.qeye(p.N_m)) a.append(q.tensor(*temp_op_list)) #keep a reference to the key operators of our system results.op = DictExtended(a=a, c=c) #Build the total Hamiltonian of our system H_rest_list = [] for i in xrange(p.mode_count): H_rest_list.append(p.mode_freqs[i] * a[i].dag() * a[i]) H_rest_list.append(p.mode_q_coupling_freqs[i] * (a[i].dag() * c + a[i] * c.dag()) + p.non_rwa_terms * p.mode_q_coupling_freqs[i] * (a[i].dag() * c.dag() + a[i] * c)) H_rest_static = np.sum(H_rest_list) H_q_static = q.tensor(q.Qobj(np.diag(p.q_level_freqs)), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) H_q_t_list = [] #then time dependence... for lvl in xrange(p.N_q): h_matrix = q.tensor(q.fock_dm(p.N_q, lvl), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) H_q_t_list.append([ h_matrix, p.q_level_freqs_time_dep[lvl](p.tlist, p.epsilon, p.omega_drive) ]) H_static = H_q_static + H_rest_static results.H = [H_static] + H_q_t_list # print(H) #Get the eigenvalues/vectors of static portion of the Hamiltonian eigen_system = H_static.eigenstates() results.eigen_vals = eigen_system[0] results.eigen_vecs = eigen_system[1] if p.print_extra_info: print("Eigen frequencies:") print( map(lambda item: "%0.9g" % item, (results.eigen_vals - results.eigen_vals[0]) / (2.0 * np.pi))) print("Consecutive differences in eigen frequencies:") print( map(lambda item: "%0.9g" % item, (results.eigen_vals[1:] - results.eigen_vals[:-1]) / (2.0 * np.pi))) print("Differences between mode and 1st excited qubit freqs") print((np.array(p.mode_freqs) - p.q_level_freqs[1]) / (2.0 * np.pi)) if p.only_show_ev: #in case we only want to see the eigenvalues/extra_info return None #Here we generate a whole bunch of states that might be of interest... but later we'll only consider some subset of those that we want to plot #...this could be streamlined... in reality no need to create all these states if they are not to be used... but this portion of the code is fast anyway #WE ASSUME that the initial state is one of those states defined here #Generate the single excitation states in bare basis results.states_collection = generate_single_excitation_states(p) # print(states_collection) #add the ground state results.states_collection['state_0'] = fock_state(p, 0, []) #add the systems's eigenstates eigenvectors_count = p.N_q * p.N_m * p.mode_count for m in xrange(eigenvectors_count): results.states_collection[ "state_v%d" % m] = results.eigen_vecs[m] * results.eigen_vecs[m].dag() # results.states_collection["state_pv%d" % m]= results.eigen_vecs[m]*results.eigen_vecs[m].dag() #add some other fun states results.states_collection["g"] = q.tensor( q.fock_dm(p.N_q, 0), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) results.states_collection["e"] = q.tensor( q.fock_dm(p.N_q, 1), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) results.states_collection["sup_v1-v2"] = 0.5 * ( results.states_collection["state_v1"] + results.states_collection["state_v2"]) #get the right states whose expectation values we want to plot results.e_ops = [ results.states_collection[state_name] for state_name in p.states_to_evolve ] #initial state results.rho0 = results.states_collection[p.rho0_name] results.c_op_list = [] #qubit dephasing if np.count_nonzero(p.q_level_dephasing_rate) > 0: dis_op = q.tensor( q.Qobj(np.diag(np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate))), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) if p.dissipators_in_dressed_basis: dis_op = dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) #TESTING #reverse which state "feels" the phase shift # rev_dephasing=np.array(p.q_level_dephasing_rate)[::-1] # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])) # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) #qubit decay if np.count_nonzero(p.q_level_decay_rate) > 0: dis_op = q.tensor(q.Qobj(np.diag(np.sqrt(p.q_level_decay_rate), 1)), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) if p.dissipators_in_dressed_basis: dis_op = dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) #TESTING # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) #mode decay for i, entry in enumerate(p.mode_decay_rates): if entry != 0: dis_op = np.sqrt(entry) * a[i] if p.dissipators_in_dressed_basis: dis_op = dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) # print(results.c_op_list) results.output = q.mesolve(results.H, results.rho0, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={ False: None, True: True }[p.show_progress_bar]) if p.show_approx_evolution: if p.N_q > 2: #Need to generalize the approximate (i.e. rotating frame) treatment to more levels print( "ERROR: The approximate (in terms of J_m functions) for now only handles a 2 level qudit...\n\n\n" ) return None if p.omega_drive == 0: #the rotated frame we've chosen assumes p.omega_drive is not zero, because we end up with a 1/p.omega_drive in the arugment #of the Bessel functions. print( "ERROR: The approximate expression for the Hamiltonian (in terms of Bessel functions) assumes that omega_drive is not zero. \ If the drive is not needed, simply set its amplitude to zero. Full numerics if of course possible with omega_drive==0." ) return results #TODO: double check minus signs, etc and generalize to many qudit levels def Utrans(t): return ((+1.0j) * ( (p.q_level_freqs[1] - p.q_level_freqs[0]) * t - p.epsilon / (1.0 * p.omega_drive) * np.cos(p.omega_drive * t)) * c.dag() * c + np.sum([(+1.0j) * t * p.mode_freqs[i] * a[i].dag() * a[i] for i in xrange(p.mode_count)])).expm() results.Utrans = Utrans zero_ops = [q.Qobj(np.zeros((p.N_q, p.N_q)))] + [ q.Qobj(np.zeros((p.N_m, p.N_m))) for i in xrange(p.mode_count) ] zero_H = q.tensor(*zero_ops) results.H_rf = [] results.H_rf.append( zero_H ) #dirrtyyy... seems like qutip wants at least one time independent Hamiltonian portion... just give it all zeros. for i in xrange(p.mode_count): ham_coeff_time_array = np.zeros(np.array(p.tlist).shape) for m in p.bessel_indices: ham_coeff_time_array = ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv( m, p.epsilon / (1.0 * p.omega_drive)) * np.exp( (1.0j) * (p.mode_freqs[i] - (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive) * p.tlist)) matrix_components = [c * a[i].dag(), ham_coeff_time_array] results.H_rf.append(matrix_components) results.H_rf.append([ matrix_components[0].dag(), np.conjugate(matrix_components[1]) ]) if p.non_rwa_terms != 0: #do the same but for the non-rwa terms ham_coeff_time_array = np.zeros(np.array(p.tlist).shape) for m in p.bessel_indices: ham_coeff_time_array = ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv( m, p.epsilon / (1.0 * p.omega_drive)) * np.exp( (1.0j) * (p.mode_freqs[i] + (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive) * p.tlist)) matrix_components = [ c.dag() * a[i].dag(), ham_coeff_time_array ] results.H_rf.append(matrix_components) results.H_rf.append([ matrix_components[0].dag(), np.conjugate(matrix_components[1]) ]) results.rho0_rf = Utrans(0) * results.rho0 * Utrans(0).dag() #In general we have to be careful about how the dissipators transform in the rotated frame... #take them as the same as in lab frame, which should be true in our case # results.c_op_list = [] #If the "measurement operators" (i.e. ones we calculate the expectation values of) do not commute with the unitary transformation #that defines the rotating frame, we need to transform the resulting density matrix before calculating expectation values if we #want to compare the results to the full lab frame evolution if p.transform_before_measurement: def e_ops_func(t, rho, transformation=Utrans, e_ops=results.e_ops): """Transform the density matrix into the lab frame, and calculate the expectation values. TODO: this could probably be streamlined... need to look into qutip's code """ rho_lab_frame = Utrans(t).dag() * q.Qobj(rho) * Utrans(t) for i, e_operator in enumerate(e_ops): expectation_values[i][e_ops_func.idx] = q.expect( e_operator, rho_lab_frame).real e_ops_func.idx += 1 e_ops_func.idx = 0 expectation_values = [ np.zeros(len(p.tlist)) for i in xrange(len(results.e_ops)) ] results.output_rf = q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, e_ops_func, options=p.odeoptions, progress_bar={ False: None, True: True }[p.show_progress_bar]) results.output_rf.expect = expectation_values else: results.output_rf = q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={ False: None, True: True }[p.show_progress_bar]) print("p.epsilon / ( p.omega_drive )=%f, " % (p.epsilon / (1.0 * p.omega_drive), ) + ",".join([ "J_%d()=%f" % (m, sc.special.jv(m, (p.epsilon / (1.0 * p.omega_drive)))) for m in p.bessel_indices ])) if p.show_plots: plot_results(p, results) return results
def vacuum_ket(self): kets = [] for m in self.modes(): kets.append(qt.fock_dm(self.nfock(m))) return qt.tensor(kets)
def __init__(self, nHO=15, nbar_mode=0.0, delta_g=2 * pi * 40e3, Omega_R=2 * pi * 80e3, T=25e-6, two_loops=True, species='8888', ion_spacing=-1, mode=1, factor=1, tau_mot=0, tau_spin=0, gamma_el=0, gamma_ram=0, Delta_ca=-2 * pi * 10e12, Delta_sr=2 * pi * 30e12, Omega_R_2=2 * pi * 80e3, phase_insensitive=False, sq_factor=1.0, qudpl_and_raman=False, on_radial_modes=False, nbars=[0.1, 0.1, 5, 5, 5, 5], mode_freqs=None): ''' nHO: dimension of HO space which is simulated nbar_mode: mean thermal population of motional mode delta_g: gate detuning Omega_R: Rabi frequency T: elapsed time two loops: do two-loop (True) or single loop (false) gate species: ion species of crystal ion-spacing: integer (+1) or half-integer (-1) standing wavelengths mode: ip (1) or oop (-1), this code assumes gate is performed an axial mode factor: square-root of ratio of Rabi frequencies for efficiency tests tau_mot: motional coherence time, =0 for intinity tau_spin: spin coherence time, =0 for intinity gamma_el: Rayleigh elastic dephasing rate gamma_ram: Raman scattering rate Delta_ca: calcium Raman detuning, from 397 Delta_sr: strontium Raman detuning, from 422 sq_factor: relative area offset of single qubit pulses (i.e. to simulate if Ramsey pi/2 and pi pulses aren't calibrated properly) Only for MS gate: Omega_R_2: Rabi frequency for second ion, for mixed species gate driven with different lasers phase_insensitive: wraps gate in single qubit operations driven by gate laser, to make it insensitive relative to phase of other single qubit operations qudpl_and_raman: use two different lasers for MS gate, with different lamb-dicke factors on_radial_modes: does gate on radial modes, but still sets it up for axial modes, this is used for simulating off-resonant excitation of radial modes for a tilted ion crystal nbars: temperature of all modes, this is only used for calculating errors from off-resonantly exciting other modes ! Note that most gate parameters are class objects, and therefore parameters are changed by previously run scans ! ''' self.nq = 2 # number of qubit states self.nHO = nHO self.nbar_mode = nbar_mode self.delta_g = delta_g self.Omega_R = Omega_R self.Omega_R_2 = Omega_R_2 self.T = T self.two_loops = two_loops self.species = species self.ion_spacing = ion_spacing self.mode = mode self.factor = factor self.tau_mot = tau_mot self.tau_spin = tau_spin self.gamma_el = gamma_el self.gamma_ram = gamma_ram self.Delta_ca = Delta_ca # detuning from 397 transition self.Delta_sr = Delta_sr # detuning from 422 transition!! (not 408, would require different matrix elements) self.delta_LS = 0 # frequency offset due to light shift self.ampl_asym_1 = 0 # amplitude asymmetry in sidebands self.ampl_asym_2 = 0 # amplitude asymmetry in sidebands of second species laser self.species_Rabi_asym_MS = 0 # Rabi frequency asymmetry btw the two species, for MS gate self.phase_insensitive = phase_insensitive # surround gate pulse with pi/2 pulses so that can use analysis pulse that is not phase-stabilised to gate self.sq_factor = sq_factor self.qudpl_and_raman = qudpl_and_raman self.on_radial_modes = on_radial_modes self.nbars = nbars # single qubit operators self.up_up = fock_dm(2, 0) # |up><up| self.dn_dn = fock_dm(2, 1) # |dn><dn| self.sz = self.up_up - self.dn_dn self.up = basis(2, 0) # |up> self.dn = basis(2, 1) # |dn> self.pl = (self.up + self.dn) / 2 # |+> self.mn = (self.up - self.dn) / 2 # |-> self.pp = self.pl * self.pl.dag() # |+><+| self.mm = self.mn * self.mn.dag() # |+><+| self.pm = self.pl * self.mn.dag() # |+><+| self.mp = self.mn * self.pl.dag() # |+><+| # two qubit operators self.uu_uu = tensor(self.up_up, self.up_up) # |up,up><up,up| self.dd_dd = tensor(self.dn_dn, self.dn_dn) # |dn,dn><dn,dn| self.ud_ud = tensor(sigmap(), sigmap()) # |up,dn><up,dn| self.du_du = tensor(sigmam(), sigmam()) # |dn,up><dn,up| self.pp_pp = tensor(self.pp, self.pp) # |+,+><+,+| self.mm_mm = tensor(self.mm, self.mm) # |-,-><-,-| self.pm_pm = tensor(self.pm, self.pm) # |+,-><+,-| self.mp_mp = tensor(self.mp, self.mp) # |-,+><-,+| # two qubit operators with HO, o* means tensor product self.uu_id = tensor(self.up_up, qeye(self.nq), qeye(self.nHO)) # |up><up| o* id self.id_uu = tensor(qeye(self.nq), self.up_up, qeye(self.nHO)) # id o* |up><up| self.dd_id = tensor(self.dn_dn, qeye(self.nq), qeye(self.nHO)) # |dn><dn| o* id self.id_dd = tensor(qeye(self.nq), self.dn_dn, qeye(self.nHO)) # id o* |dn><dn| self.sp_id = tensor(sigmap(), qeye(self.nq), qeye(self.nHO)) # |up><up| o* id self.id_sp = tensor(qeye(self.nq), sigmap(), qeye(self.nHO)) # id o* |up><up| self.sm_id = tensor(sigmam(), qeye(self.nq), qeye(self.nHO)) # |dn><dn| o* id self.id_sm = tensor(qeye(self.nq), sigmam(), qeye(self.nHO)) # id o* |dn><dn| # readout projection operators self.spin_dndn = tensor(self.dn_dn, self.dn_dn, qeye(self.nHO)) self.spin_upup = tensor(self.up_up, self.up_up, qeye(self.nHO)) self.spin_updn_dnup = tensor(self.up_up, self.dn_dn, qeye( self.nHO)) + tensor(self.dn_dn, self.up_up, qeye(self.nHO)) self.spin_updn = tensor(self.up_up, self.dn_dn, qeye(self.nHO)) self.spin_dnup = tensor(self.dn_dn, self.up_up, qeye(self.nHO)) self.spin_pp = tensor(self.pp, self.pp, qeye(self.nHO)) self.spin_mm = tensor(self.mm, self.mm, qeye(self.nHO)) self.spin_pm = tensor(self.pp, self.mm, qeye(self.nHO)) self.spin_mp = tensor(self.mm, self.pp, qeye(self.nHO)) # annihilation operator self.a = tensor(qeye(self.nq), qeye(self.nq), destroy(self.nHO)) self.ad = self.a.dag() self.mw_offset_phase_1 = random.random( ) * 2 * pi # use this phase offset for mw pulses because they don't have a fixed phase relationship to gate lasers self.phi_sum_1 = random.random( ) * 2 * pi # sum frequency of phases of two sidebands, ie the phase that determines phi in sigma_phi self.phi_diff_1 = random.random( ) * 2 * pi # difference frequency of phases of two sidebands, constant and ~0 for our frequency geometry self.setup_mode_structure(mode_freqs) self.set_ion_parameters()
tau = 0.6 steps = 2e4 sigma = 0.01 gamma_1 = (1000. * tau)**-1. #ridic low, just trying for stability gamma_phi = (1000. * tau)**-1. #ridic low, just trying for stability delta = 2. * np.pi * 1600. g = 2. * np.pi * 50. kappa = 2. * np.pi * 5. omega_cav = 2. * np.pi * 7400. n_c = 60 #number of cavity states n_q = 1 zero_state = qt.fock_dm(2, 0) one_state = qt.fock_dm(2, 1) vacuum = qt.fock_dm(n_c, 0) zero_ket = qt.fock(2, 0) one_ket = qt.fock(2, 1) vacuum_ket = qt.fock(n_c, 0) sz = jc.s_z(n_c, n_q, 0) rest_ham = delta * (-sz / 2.) a = jc.ann(n_c, n_q) a_dag = a.dag() sp = jc.s_p(n_c, n_q, 0) sm = sp.dag() jc_ham = g * (a * sp + a_dag * sm) rabi_ham = [
def test_fockdm_type(): "State CSR Type: fock_dm" st = fock_dm(5,3) assert_equal(isspmatrix_csr(st.data), True)
# Similarly, print("Basis with {N} states, where state {n} is occupied:\n".format(N=4, n=2), fock(4, 2)) print() # Generating a Coherent State print( "Coherent State with {N} fock states in hilbert space and eigenvalue = {alpha} \n" .format(N=10, alpha=1.0), coherent(N=10, alpha=1.0)) print() # Fock state (as a density matrix) print( "Fock State as a Density Matrix with hilbert space size = {dim} and occupied state = {n} \n" .format(dim=5, n=2), fock_dm(5, 2)) print() print( "Coherent State as a Density Matrix with number of fock states = {N} and eigenvalue = {alpha}" .format(N=8, alpha=1.0), coherent_dm(N=8, alpha=1.0)) print() n = 1 # average number of thermal photons print( "Thermal State as Density Matrix with with number of basis states in hilbert space = {N} and expectation value for number of particles in thermal state = {n}\n" .format(N=8, n=n), thermal_dm(8, n)) wait() """ --------------- </Operators> --------------- """
wa = 1.0 * 2 * pi # atom frequency g = 0.05 * 2 * pi # coupling strength kappa = 0.005 # cavity dissipation rate gamma = 0.05 # atom dissipation rate N = 15 # number of cavity fock states n_th_a = 0.0 # avg number of thermal bath excitation use_rwa = True tlist = linspace(0, 100, 10000) qt.Options(nsteps=1e10) # intial state psi0 = qt.tensor(qt.basis(N, 0), qt.basis(2, 0)) # start with an excited atom e_cav = qt.tensor(qt.fock_dm(N, 1), qt.fock_dm(2, 0)) e_qb = qt.tensor(qt.fock_dm(N, 0), qt.fock_dm(2, 1)) # operators a = qt.tensor(qt.destroy(N), qt.qeye(2)) sm = qt.tensor(qt.qeye(N), qt.destroy(2)) # Hamiltonian if use_rwa: H = wc * a.dag() * a + wa * sm.dag() * sm + g * (a.dag() * sm + a * sm.dag()) else: H = wc * a.dag() * a + wa * sm.dag() * sm + g * (a.dag() + a) * (sm + sm.dag()) c_ops = []
def evolve(self, rho, omegas, detuning, detuning_percent, detuning_fixed, dephasing, dephasing_sigma, timestep): """ Constant state evolution of the system, for a single timestep, using the STIRAP hamiltonian as in arXiv:1901.06603v1. :params: -------- rho: (np.array) Input state of the systems (from either the previous timestep or the initial state) omegas: (np.array) An array of size 2 containing the constant pulse values for the pump and stokes pulse respectively. omega_max: (float) Maximum value for the pulses detuning: (bool) True for inclusion of random detuning in the interval [-detuning_percent,detuning_percent]*omega_max detuning_percent: (float) Value between 0 and 1 defining the interval from which detuning is randomly sampled. :returns: --------- rho_out: (np.array) State of the system after the evolution. reduced_rho_out: (np.array) Array containing only the diagonal elements of rho_out. """ tlist = [0.0, timestep] # Turn the listform of rho into a proper qobj for the solver rho_proper = qt.Qobj(rho.reshape(self.num_levels, self.num_levels)) # Initialize an empty array to fill with the pulse values to make the hamiltonian. ham_array = np.zeros((self.num_levels, self.num_levels)) for i in range(self.num_levels): for j in range(self.num_levels): if i == j + 1: ham_array[i, j] = 0.5 * omegas[j] elif j == i + 1: ham_array[i, j] = 0.5 * omegas[i] hamiltonian = qt.Qobj(ham_array) # If the detuning kwarg = True we introduce uniformly random detuning into # each timestep. if detuning_fixed == False: delta1 = np.random.uniform(low=-detuning_percent * self.omega_max, high=detuning_percent * self.omega_max) delta2 = 0 elif detuning_fixed == True: delta2 = detuning_percent * self.omega_max delta1 = detuning_percent * self.omega_max elif detuning_fixed == "pierpaolo": delta2 = detuning_percent * self.omega_max delta1 = -23.5 * delta2 detuning_hamiltonian = qt.Qobj([[0, 0, 0], [0, delta1, 0], [0, 0, delta2]]) # Currently only compatablibe with num_levels = 3 if detuning == True: H = hamiltonian + detuning_hamiltonian #print(H) else: H = hamiltonian #print(H) # Dephasing section: Sigma = dephasing_sigma * self.omega_max c0 = np.sqrt(Sigma) * qt.fock_dm(3, 0) c1 = np.sqrt(Sigma) * qt.fock_dm(3, 1) c2 = np.sqrt(Sigma) * qt.fock_dm(3, 2) collapse_operators = [c0, c1, c2] # Pass the (stepwise constant) hamiltonian into the mesolve function to evolve the # state and access the resultant state using the .states attribute. if dephasing == True: rho_out = qt.mesolve(H, rho_proper, tlist, c_ops=collapse_operators) elif dephasing == False: rho_out = qt.mesolve(H, rho_proper, tlist) x = rho_out.states[-1] # flatten the resultant state back into an array again. Also return a reduced_rho # containing only the diagonal elements of the density matrix. rho_final = x.full().reshape((self.num_levels**2, )) reduced_rho_final = x.diag() return rho_final, reduced_rho_final
axes.plot(kappa_arr, np.absolute(max_pts), '--b*') axes.set_xlabel("$\kappa$") axes.set_ylabel("Radius of Wigner Function") axes.set_title("$nbar(\omega)=%f$" % nbar_omega) if __name__ == "__main__": gamma1 = 1 # TODO gamma2 = 0.5 # 0.1 # TODO nbar_omega = 0.1 # Temperature dependence tlist = np.linspace(0.0, 20.0, tN) nbar_omega = (0.1, 0.4, 0.7) kappa = np.linspace(0, 1, 3) kappa = np.append(kappa, np.linspace(1, 1.25, 10)) kappa = np.append(kappa, np.linspace(1.5, 3, 4)) rho0 = qutip.fock_dm(N, 0) print(kappa) results = [[0 for j in kappa] for i in nbar_omega] fig1, axes = plt.subplots(1, len(nbar_omega), figsize=(16, 3)) for i, nb in enumerate(nbar_omega): for j, kap in enumerate(kappa): results[i][j] = solve_lindblad(gamma1, gamma2, nb, nb, kap, rho0, tlist, 0) print("solved for nbar_omega=%f and kappa=%f" % (nb, kap)) plot_max_W_func(kappa, nb, results[i], axes[i]) # plt.hold(True) # plt.plot(kappa) plt.show()
print("Eseries 1 + Eseries 2\n", es1 + es2, end="\n\n") print("Eseries 1 - Eseries 2\n", es1 - es2, end="\n\n") print("Eseries 1 * Eseries 2\n", es1 * es2, end="\n\n") print("Eseries: (es1 + es2) * (es1 - es2) \n", (es1 + es2) * (es1 - es2), end="\n\n") wait() """ --------------- </Expecation Values of eseries> --------------- """ es3 = eseries([0.5 * sigmaz(), 0.5 * sigmaz()], [1j, -1j]) + eseries( [-0.5j * sigmax(), 0.5 * sigmax()], [1j, -1j]) print("*Eseries:\n", es3) print("Eseries' Value at t = 0.0:\n", es3.value(0.0)) print("Eseries' Value at t = pi/2:\n", es3.value(pi / 2)) rho = fock_dm(2, 1) es3_expect = expect(rho, es3) print( "For fock density matrix(2,1) Expectation of fock density matrix(2,1) on the state given above (*Eseries)\n", es3_expect) print("Expectation values at times 0 and pi/2 are:\n", es3_expect.value([0.0, pi / 2])) wait() """ --------------- </Software Versions> --------------- """ """ Software Version QuTiP 4.6.2 Numpy 1.21.4 SciPy 1.7.2
def test_smesolve_homodyne_methods(): "Stochastic: smesolve: homodyne methods with single jump operator" def arccoth(x): return 0.5 * np.log((1. + x) / (x - 1.)) th = 0.1 # Interaction parameter alpha = np.cos(th) beta = np.sin(th) gamma = 1. N = 30 # number of Fock states Id = qeye(N) a = destroy(N) s = 0.5 * ((alpha + beta) * a + (alpha - beta) * a.dag()) x = (a + a.dag()) * 2**-0.5 H = Id c_op = [gamma**0.5 * a] sc_op = [s] e_op = [x, x * x] rho0 = fock_dm(N, 0) # initial vacuum state T = 6. # final time N_store = 200 # number of time steps for which we save the expectation values Nsub = 5 tlist = np.linspace(0, T, N_store) ddt = (tlist[1] - tlist[0]) #### Analytic solution y0 = 0.5 A = (gamma**2 + alpha**2 * (beta**2 + 4 * gamma) - 2 * alpha * beta * gamma)**0.5 B = arccoth((-4 * alpha**2 * y0 + alpha * beta - gamma) / A) y_an = (alpha * beta - gamma + A / np.tanh(0.5 * A * tlist - B)) / (4 * alpha**2) list_methods_tol = [['euler-maruyama', 2e-2], ['fast-euler-maruyama', 2e-2], ['pc-euler', 2e-3], ['milstein', 1e-3], ['fast-milstein', 1e-3], ['milstein-imp', 1e-3], ['taylor15', 1e-4], ['taylor15-imp', 1e-4]] for n_method in list_methods_tol: sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver=n_method[0]) sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver=n_method[0], noise=sol.noise) err = 1 / T * np.sum( np.abs(y_an - (sol.expect[1] - sol.expect[0] * sol.expect[0].conj()))) * ddt print(n_method[0], ': deviation =', err, ', tol =', n_method[1]) assert_(err < n_method[1]) assert_(np.all(sol.noise == sol2.noise) ) # just to check that noise is not affected by smesolve assert_(np.all(sol.expect[0] == sol2.expect[0]))
def H_approx_Par(phi): H = -np.exp(-phi**2 / 2) * sum( eval_laguerre(n, phi**2) * qt.fock_dm(NFock, n) for n in range(NFock)) return H
def test_smesolve_homodyne_methods(): "Stochastic: smesolve: homodyne methods with single jump operator" def arccoth(x): return 0.5 * np.log((1. + x) / (x - 1.)) th = 0.1 # Interaction parameter alpha = np.cos(th) beta = np.sin(th) gamma = 1. N = 30 # number of Fock states Id = qeye(N) a = destroy(N) s = 0.5 * ((alpha + beta) * a + (alpha - beta) * a.dag()) x = (a + a.dag()) * 2**-0.5 H = Id c_op = [gamma**0.5 * a] sc_op = [s] e_op = [x, x * x] rho0 = fock_dm(N, 0) # initial vacuum state T = 3. # final time # number of time steps for which we save the expectation values N_store = 121 Nsub = 10 tlist = np.linspace(0, T, N_store) ddt = (tlist[1] - tlist[0]) #### Analytic solution y0 = 0.5 A = (gamma**2 + alpha**2 * (beta**2 + 4 * gamma) - 2 * alpha * beta * gamma)**0.5 B = arccoth((-4 * alpha**2 * y0 + alpha * beta - gamma) / A) y_an = (alpha * beta - gamma + A / np.tanh(0.5 * A * tlist - B)) / (4 * alpha**2) list_methods_tol = [['euler-maruyama', 2e-2], ['pc-euler', 2e-3], ['pc-euler-2', 2e-3], ['platen', 1e-3], ['milstein', 1e-3], ['milstein-imp', 1e-3], ['rouchon', 1e-3], ['taylor1.5', 1e-4], ['taylor1.5-imp', 1e-4], ['explicit1.5', 1e-4], ['taylor2.0', 1e-4]] for n_method in list_methods_tol: sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver=n_method[0]) sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, store_measurement=0, nsubsteps=Nsub, method='homodyne', solver=n_method[0], noise=sol.noise) sol3 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub * 5, method='homodyne', solver=n_method[0], tol=1e-8) err = 1/T * np.sum(np.abs(y_an - \ (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt err3 = 1/T * np.sum(np.abs(y_an - \ (sol3.expect[1]-sol3.expect[0]*sol3.expect[0].conj())))*ddt print(n_method[0], ': deviation =', err, ', tol =', n_method[1]) assert_(err < n_method[1]) # 5* more substep should decrease the error assert_(err3 < err) # just to check that noise is not affected by smesolve assert_(np.all(sol.noise == sol2.noise)) assert_(np.all(sol.expect[0] == sol2.expect[0])) sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler', store_measurement=1) sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler', store_measurement=0) sol3 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=11, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') # sol and sol2 have the same seed, sol3 differ. assert_(np.all(sol.noise == sol2.noise)) assert_(np.all(sol.noise != sol3.noise)) assert_(not np.all(sol.measurement[0] == 0. + 0j)) assert_(np.all(sol2.measurement[0] == 0. + 0j)) sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([1, 2]), ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([2, 1]), ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') # sol and sol2 have the seed of traj 1 and 2 reversed. assert_(np.all(sol.noise[0, :, :, :] == sol2.noise[1, :, :, :])) assert_(np.all(sol.noise[1, :, :, :] == sol2.noise[0, :, :, :]))
def test_smesolve_homodyne_methods(): "Stochastic: smesolve: homodyne methods with single jump operator" def arccoth(x): return 0.5*np.log((1.+x)/(x-1.)) th = 0.1 # Interaction parameter alpha = np.cos(th) beta = np.sin(th) gamma = 1. N = 30 # number of Fock states Id = qeye(N) a = destroy(N) s = 0.5*((alpha+beta)*a + (alpha-beta)*a.dag()) x = (a + a.dag()) * 2**-0.5 H = Id c_op = [gamma**0.5 * a] sc_op = [s] e_op = [x, x*x] rho0 = fock_dm(N,0) # initial vacuum state T = 3. # final time # number of time steps for which we save the expectation values N_store = 121 Nsub = 10 tlist = np.linspace(0, T, N_store) ddt = (tlist[1]-tlist[0]) #### Analytic solution y0 = 0.5 A = (gamma**2 + alpha**2 * (beta**2 + 4*gamma) - 2*alpha*beta*gamma)**0.5 B = arccoth((-4*alpha**2*y0 + alpha*beta - gamma)/A) y_an = (alpha*beta - gamma + A / np.tanh(0.5*A*tlist - B))/(4*alpha**2) list_methods_tol = [['euler-maruyama', 2e-2], ['pc-euler', 2e-3], ['pc-euler-2', 2e-3], ['platen', 1e-3], ['milstein', 1e-3], ['milstein-imp', 1e-3], ['rouchon', 1e-3], ['taylor1.5', 1e-4], ['taylor1.5-imp', 1e-4], ['explicit1.5', 1e-4], ['taylor2.0', 1e-4]] for n_method in list_methods_tol: sol = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub, method='homodyne', solver = n_method[0]) sol2 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, store_measurement=0, nsubsteps=Nsub, method='homodyne', solver = n_method[0], noise = sol.noise) sol3 = smesolve(H, rho0, tlist, c_op, sc_op, e_op, nsubsteps=Nsub*5, method='homodyne', solver = n_method[0], tol=1e-8) err = 1/T * np.sum(np.abs(y_an - \ (sol.expect[1]-sol.expect[0]*sol.expect[0].conj())))*ddt err3 = 1/T * np.sum(np.abs(y_an - \ (sol3.expect[1]-sol3.expect[0]*sol3.expect[0].conj())))*ddt print(n_method[0], ': deviation =', err, ', tol =', n_method[1]) assert_(err < n_method[1]) # 5* more substep should decrease the error assert_(err3 < err) # just to check that noise is not affected by smesolve assert_(np.all(sol.noise == sol2.noise)) assert_(np.all(sol.expect[0] == sol2.expect[0])) sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler', store_measurement=1) sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=10, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler', store_measurement=0) sol3 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=11, ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') # sol and sol2 have the same seed, sol3 differ. assert_(np.all(sol.noise == sol2.noise)) assert_(np.all(sol.noise != sol3.noise)) assert_(not np.all(sol.measurement[0] == 0.+0j)) assert_(np.all(sol2.measurement[0] == 0.+0j)) sol = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([1,2]), ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') sol2 = smesolve(H, rho0, tlist[:2], c_op, sc_op, e_op, noise=np.array([2,1]), ntraj=2, nsubsteps=Nsub, method='homodyne', solver='euler') # sol and sol2 have the seed of traj 1 and 2 reversed. assert_(np.all(sol.noise[0,:,:,:] == sol2.noise[1,:,:,:])) assert_(np.all(sol.noise[1,:,:,:] == sol2.noise[0,:,:,:]))
H = QubitHam4_MatterSitesQubits_nocounter_rot(6, 6, 0.1, 0.1, 17.0, 16.0, 74e-15, 67e-15, 30.0, 30.0e-15, -0.212381034483) H.tidyup(atol=1e-1) psi_0 = qt.tensor( [qt.basis(2, 1), qt.basis(n, 0), qt.basis(n, 1), qt.basis(2, 0)]) tlist = np.linspace(0, 25, 500) e_ops = [] # Create excitation states for qubits and oscillators g_qb = qt.fock_dm(2, 0) e_qb = qt.fock_dm(2, 1) g_osc = qt.fock_dm(n, 0) e_osc = qt.fock_dm(n, 1) f_osc = qt.fock_dm(n, 2) PG = qt.tensor([g_qb, g_osc, g_osc, g_qb]) P1 = qt.tensor([e_qb, g_osc, e_osc, g_qb]) P2 = qt.tensor([g_qb, e_osc, e_osc, g_qb]) P3 = qt.tensor([g_qb, e_osc, g_osc, e_qb]) P4 = qt.tensor([g_qb, g_osc, e_osc, e_qb]) P5 = qt.tensor([g_qb, f_osc, g_osc, g_qb]) P6 = qt.tensor([g_qb, g_osc, f_osc, g_qb]) #P_e1 = qt.tensor([e_qb,qt.qeye(n),qt.qeye(n),qt.qeye(2)])
def run_sim_full_and_approx(p): """ TODO: -Should get separate the approximate (rotating wave) evolution into its own function. Then the plotting routines should take a list of results, using different -Double check that coupling of higher qudit levels scales as standard lowering/rasing ops to a good approximation. -Add co-rotating terms evolution to approximate RF results. IMPORTANT: the passed in options 'p' should not be changed inside this function if one wants to run things in parallel via multiprocessing.Pool.map() Otherwise, in particular if p gets entries with new objects, can lead to things break down in non-trivial way. """ # if p.show_approx_evolution: # print("The codebase for the approx evolution needs a couple of minor updates with the recent changes...\n\n\n") # return None if p.N_q>2 and p.show_approx_evolution: #Need to generalize the approximate treatment to more levels print("ERROR: The approximate evolution (in terms of J_m functions) for now only handles a 2 level qudit...\n\ ... set p.show_approx_evolution=False in order to look at the full evolution instead.\n") return None #make sure the config is sane assert(len(p.mode_freqs)==len(p.mode_q_coupling_freqs)==len(p.mode_decay_rates)) assert(p.N_q==len(p.q_level_freqs)==len(p.q_level_freqs_time_dep)==len(p.q_level_dephasing_rate)==(len(p.q_level_decay_rate)+1)) assert(p.non_rwa_terms==0 or p.non_rwa_terms==1) results=DictExtended() #keep a reference to the specific options/parameters that were used in the simulation along with the results results.p=DictExtended(p) #qubit lowering operator c=q.tensor(q.destroy(p.N_q), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) #mode lowering operators; a[j], gives the lowering operator for the jth mode (first is the 0th indexed) a=[] for j in xrange(p.mode_count): #prettier way to do this? temp_op_list=[q.qeye(p.N_q)] for i in xrange(p.mode_count): if i==j: temp_op_list.append(q.destroy(p.N_m)) else: temp_op_list.append(q.qeye(p.N_m)) a.append(q.tensor(*temp_op_list)) #keep a reference to the key operators of our system results.op=DictExtended(a=a, c=c) #Build the total Hamiltonian of our system H_rest_list=[] for i in xrange(p.mode_count): H_rest_list.append(p.mode_freqs[i] * a[i].dag() * a[i]) H_rest_list.append(p.mode_q_coupling_freqs[i] * (a[i].dag()*c + a[i]*c.dag()) + p.non_rwa_terms * p.mode_q_coupling_freqs[i] * (a[i].dag()*c.dag() + a[i]*c) ) H_rest_static=np.sum(H_rest_list) H_q_static=q.tensor(q.Qobj(np.diag(p.q_level_freqs)), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) H_q_t_list=[] #then time dependence... for lvl in xrange(p.N_q): h_matrix=q.tensor(q.fock_dm(p.N_q,lvl), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) H_q_t_list.append([h_matrix, p.q_level_freqs_time_dep[lvl](p.tlist, p.epsilon, p.omega_drive)]) H_static=H_q_static + H_rest_static results.H=[H_static] + H_q_t_list # print(H) #Get the eigenvalues/vectors of static portion of the Hamiltonian eigen_system=H_static.eigenstates() results.eigen_vals=eigen_system[0] results.eigen_vecs=eigen_system[1] if p.print_extra_info: print("Eigen frequencies:") print(map(lambda item: "%0.9g" % item, (results.eigen_vals-results.eigen_vals[0])/(2.0*np.pi))) print("Consecutive differences in eigen frequencies:") print(map(lambda item: "%0.9g" % item, (results.eigen_vals[1:] - results.eigen_vals[:-1])/(2.0*np.pi))) print("Differences between mode and 1st excited qubit freqs") print((np.array(p.mode_freqs) - p.q_level_freqs[1])/(2.0*np.pi)) if p.only_show_ev: #in case we only want to see the eigenvalues/extra_info return None #Here we generate a whole bunch of states that might be of interest... but later we'll only consider some subset of those that we want to plot #...this could be streamlined... in reality no need to create all these states if they are not to be used... but this portion of the code is fast anyway #WE ASSUME that the initial state is one of those states defined here #Generate the single excitation states in bare basis results.states_collection=generate_single_excitation_states(p) # print(states_collection) #add the ground state results.states_collection['state_0']=fock_state(p, 0,[]) #add the systems's eigenstates eigenvectors_count=p.N_q*p.N_m*p.mode_count for m in xrange(eigenvectors_count): results.states_collection["state_v%d" % m]=results.eigen_vecs[m]*results.eigen_vecs[m].dag() # results.states_collection["state_pv%d" % m]= results.eigen_vecs[m]*results.eigen_vecs[m].dag() #add some other fun states results.states_collection["g"]=q.tensor(q.fock_dm(p.N_q, 0), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) results.states_collection["e"]=q.tensor(q.fock_dm(p.N_q, 1), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) results.states_collection["sup_v1-v2"]=0.5 * (results.states_collection["state_v1"] + results.states_collection["state_v2"]) #get the right states whose expectation values we want to plot results.e_ops=[results.states_collection[state_name] for state_name in p.states_to_evolve] #initial state results.rho0 = results.states_collection[p.rho0_name] results.c_op_list = [] #qubit dephasing if np.count_nonzero(p.q_level_dephasing_rate)>0: dis_op=q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) if p.dissipators_in_dressed_basis: dis_op=dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) #TESTING #reverse which state "feels" the phase shift # rev_dephasing=np.array(p.q_level_dephasing_rate)[::-1] # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(rev_dephasing) ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)])) # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(2.0) * np.sqrt(p.q_level_dephasing_rate) ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) #qubit decay if np.count_nonzero(p.q_level_decay_rate)>0: dis_op=q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.qeye(p.N_m) for i in xrange(p.mode_count)]) if p.dissipators_in_dressed_basis: dis_op=dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) #TESTING # results.c_op_list.append(q.tensor(q.Qobj(np.diag( np.sqrt(p.q_level_decay_rate), 1 ) ), *[q.ket2dm(q.basis(p.N_m,0)) for i in xrange(p.mode_count)])) #mode decay for i, entry in enumerate(p.mode_decay_rates): if entry!=0: dis_op=np.sqrt(entry) * a[i] if p.dissipators_in_dressed_basis: dis_op=dis_op.transform(results.eigen_vecs, inverse=False) # dis_op=dis_op.transform(results.eigen_vecs, inverse=True) results.c_op_list.append(dis_op) # print(results.c_op_list) results.output = q.mesolve(results.H, results.rho0, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar]) if p.show_approx_evolution: if p.N_q>2: #Need to generalize the approximate (i.e. rotating frame) treatment to more levels print("ERROR: The approximate (in terms of J_m functions) for now only handles a 2 level qudit...\n\n\n") return None if p.omega_drive==0: #the rotated frame we've chosen assumes p.omega_drive is not zero, because we end up with a 1/p.omega_drive in the arugment #of the Bessel functions. print("ERROR: The approximate expression for the Hamiltonian (in terms of Bessel functions) assumes that omega_drive is not zero. \ If the drive is not needed, simply set its amplitude to zero. Full numerics if of course possible with omega_drive==0.") return results #TODO: double check minus signs, etc and generalize to many qudit levels def Utrans(t): return ( (+1.0j) * ( (p.q_level_freqs[1] - p.q_level_freqs[0]) * t - p.epsilon/(1.0 * p.omega_drive ) * np.cos(p.omega_drive * t) ) * c.dag()*c + np.sum([ (+1.0j) * t * p.mode_freqs[i] * a[i].dag()*a[i] for i in xrange(p.mode_count)]) ).expm() results.Utrans=Utrans zero_ops=[q.Qobj(np.zeros((p.N_q, p.N_q)))] + [q.Qobj(np.zeros((p.N_m, p.N_m))) for i in xrange(p.mode_count)] zero_H=q.tensor(*zero_ops) results.H_rf=[] results.H_rf.append(zero_H) #dirrtyyy... seems like qutip wants at least one time independent Hamiltonian portion... just give it all zeros. for i in xrange(p.mode_count): ham_coeff_time_array=np.zeros(np.array(p.tlist).shape) for m in p.bessel_indices: ham_coeff_time_array=ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(m, p.epsilon / ( 1.0 * p.omega_drive )) * np.exp( (1.0j) * (p.mode_freqs[i] - (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive ) * p.tlist)) matrix_components=[c*a[i].dag(), ham_coeff_time_array] results.H_rf.append(matrix_components) results.H_rf.append([matrix_components[0].dag(), np.conjugate(matrix_components[1])]) if p.non_rwa_terms!=0: #do the same but for the non-rwa terms ham_coeff_time_array=np.zeros(np.array(p.tlist).shape) for m in p.bessel_indices: ham_coeff_time_array=ham_coeff_time_array + ( (1.0j)**m * p.mode_q_coupling_freqs[i] * sc.special.jv(m, p.epsilon / ( 1.0 * p.omega_drive )) * np.exp( (1.0j) * (p.mode_freqs[i] + (p.q_level_freqs[1] - p.q_level_freqs[0]) - m * p.omega_drive ) * p.tlist)) matrix_components=[c.dag()*a[i].dag(), ham_coeff_time_array] results.H_rf.append(matrix_components) results.H_rf.append([matrix_components[0].dag(), np.conjugate(matrix_components[1])]) results.rho0_rf=Utrans(0)*results.rho0*Utrans(0).dag() #In general we have to be careful about how the dissipators transform in the rotated frame... #take them as the same as in lab frame, which should be true in our case # results.c_op_list = [] #If the "measurement operators" (i.e. ones we calculate the expectation values of) do not commute with the unitary transformation #that defines the rotating frame, we need to transform the resulting density matrix before calculating expectation values if we #want to compare the results to the full lab frame evolution if p.transform_before_measurement: def e_ops_func(t, rho, transformation=Utrans, e_ops=results.e_ops): """Transform the density matrix into the lab frame, and calculate the expectation values. TODO: this could probably be streamlined... need to look into qutip's code """ rho_lab_frame=Utrans(t).dag()*q.Qobj(rho)*Utrans(t) for i, e_operator in enumerate(e_ops): expectation_values[i][e_ops_func.idx]=q.expect(e_operator, rho_lab_frame).real e_ops_func.idx+=1 e_ops_func.idx=0 expectation_values=[np.zeros(len(p.tlist)) for i in xrange(len(results.e_ops))] results.output_rf=q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, e_ops_func, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar]) results.output_rf.expect=expectation_values else: results.output_rf=q.mesolve(results.H_rf, results.rho0_rf, p.tlist, results.c_op_list, results.e_ops, options=p.odeoptions, progress_bar={False:None, True:True}[p.show_progress_bar]) print("p.epsilon / ( p.omega_drive )=%f, " % (p.epsilon / ( 1.0 * p.omega_drive ), ) + ",".join([ "J_%d()=%f" % (m, sc.special.jv(m,(p.epsilon / ( 1.0 * p.omega_drive )))) for m in p.bessel_indices]) ) if p.show_plots: plot_results(p, results) return results
import matplotlib.pyplot as plt import qutip N = 25 taus = np.linspace(0, 25.0, 200) a = qutip.destroy(N) H = 2 * np.pi * a.dag() * a kappa = 0.25 n_th = 2.0 # bath temperature in terms of excitation number c_ops = [np.sqrt(kappa * (1 + n_th)) * a, np.sqrt(kappa * n_th) * a.dag()] states = [ {'state': qutip.coherent_dm(N, np.sqrt(2)), 'label': "coherent state"}, {'state': qutip.thermal_dm(N, 2), 'label': "thermal state"}, {'state': qutip.fock_dm(N, 2), 'label': "Fock state"}, ] fig, ax = plt.subplots(1, 1) for state in states: rho0 = state['state'] # first calculate the occupation number as a function of time n = qutip.mesolve(H, rho0, taus, c_ops, [a.dag() * a]).expect[0] # calculate the correlation function G2 and normalize with n(0)n(t) to # obtain g2 G2 = qutip.correlation_3op_1t(H, rho0, taus, c_ops, a.dag(), a.dag()*a, a) g2 = G2 / (n[0] * n)