def k_alpha_rate(t, delta_r_bar=0): if verbose: print(t) schedule(t, 1, delta_r_bar=delta_r_bar) # Construct the first order transition matrix ground_energy, ground_state = SchrodingerEquation(hamiltonians=eq.hamiltonians).ground_state() overlap = 0 for op in eq.jump_operators[0].jump_operators: overlap = overlap - (np.abs(ground_state.conj().T @ op @ ground_state) ** 2)[0, 0] + \ (ground_state.conj().T @ op.conj().T @ op @ ground_state)[0, 0] if graph.degeneracy > 1: # Solve for the k lowest eigenvalues, where k=degeneracy energies, states = SchrodingerEquation(hamiltonians=eq.hamiltonians).eig(k=graph.degeneracy+1) states = states.T rates_into_degenerate = np.zeros(energies.shape[0] ** 2) for op in eq.jump_operators[0].jump_operators: rates_into_degenerate = rates_into_degenerate + (np.abs(states.conj().T @ op @ states) ** 2).flatten() rates_into_degenerate = np.reshape(rates_into_degenerate, (energies.shape[0], energies.shape[0])) rates_into_degenerate = rates_into_degenerate[:, 0].flatten().real rates_into_degenerate = rates_into_degenerate[1:graph.degeneracy] rates_into_degenerate = np.sum(rates_into_degenerate) overlap = overlap.real - rates_into_degenerate return overlap.real
def compute_first_beta_order(eq: LindbladMasterEquation, schedule): dt = .001 def energy_difference(t): schedule(t, 1) eigvals, eigvecs = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() return eigvals[-1] - eigvals[0] def integrated_phase_factor(t): return scipy.integrate.quad(energy_difference, 0, t, epsrel=1e-7, epsabs=1e-7)[0] phase = integrated_phase_factor(1) # Compute matrix element at the very beginning and end schedule(1 - dt, 1) eigvals, eigvecs = SchrodingerEquation(hamiltonians=eq.hamiltonians).eig() schedule(1 - 2 * dt, 1) eigvals_past, eigvecs_past = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() # ensure that the first eigenvalue is positive if eigvecs[0][0] <= 0: eigvecs[0] = eigvecs[0] * -1 if eigvecs_past[0][0] <= 0: eigvecs_past[0] = eigvecs_past[0] * -1 if eigvecs[-1][0] <= 0: eigvecs[-1] = eigvecs[-1] * -1 if eigvecs_past[-1][0] <= 0: eigvecs_past[-1] = eigvecs_past[-1] * -1 res_end = (np.vdot(eigvecs[-1], (eigvecs[0] - eigvecs_past[0]) / dt) / (eigvals[0] - eigvals[-1])) schedule(2 * dt, 1) eigvals, eigvecs = SchrodingerEquation(hamiltonians=eq.hamiltonians).eig() schedule(dt, 1) eigvals_past, eigvecs_past = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() # ensure that the first eigenvalue is positive if eigvecs[0][0] <= 0: eigvecs[0] = eigvecs[0] * -1 if eigvecs_past[0][0] <= 0: eigvecs_past[0] = eigvecs_past[0] * -1 if eigvecs[-1][0] <= 0: eigvecs[-1] = eigvecs[-1] * -1 if eigvecs_past[-1][0] <= 0: eigvecs_past[-1] = eigvecs_past[-1] * -1 res_beginning = (np.vdot(eigvecs[-1], (eigvecs[0] - eigvecs_past[0]) / dt) / (eigvals[0] - eigvals[-1])) print(res_end**2 + res_beginning**2)
def spectrum_vs_time(self, time, schedule, k=2, num=None, plot=False, which='S', hamiltonian=True): """Solves for the small (S) or large (L) energy sector.""" if num is None: num = self._num_from_time(time) times = np.linspace(0, time, num=num) if not hamiltonian and (self.noise_model is None or self.noise_model is 'monte_carlo'): print('No noise models found. Finding Hamiltonian spectrum') hamiltonian = True if self.noise_model is None or self.noise_model is 'monte_carlo' or hamiltonian: # Initialize Schrodinger equation schrodinger_equation = SchrodingerEquation(hamiltonians=self.hamiltonian) eigvals = np.zeros((len(times), k), dtype=np.float64) for i in range(len(times)): schedule(times[i], time) eigval, eigvec = schrodinger_equation.eig(which=which, k=k) if which == 'S': eigvals[i] = eigval[0:k] elif which == 'L': eigvals[i] = eigval[len(eigval)-k-1:-1] if plot: plotted_eigvals = np.swapaxes(eigvals, 0, 1) for i in range(k): plt.scatter(times, plotted_eigvals[i], color='teal') plt.ylabel('Energy') plt.xlabel('Time') plt.show() else: eigvals = np.zeros((len(times), self.cost_hamiltonian.shape[0], self.cost_hamiltonian.shape[0]), dtype=np.complex128) raise NotImplementedError return eigvals
def evolve(t): # Generate a mapping from a time to an index schedule(t, 1) if not full_output: if degeneracy == 1: ground_energy, ground_state = SchrodingerEquation( hamiltonians=eq.hamiltonians).ground_state() overlap = 0 for op in eq.jump_operators[0].jump_operators: overlap = overlap - (np.abs(ground_state.conj().T @ op @ ground_state) ** 2)[0, 0] + \ (ground_state.conj().T @ op.conj().T @ op @ ground_state)[0, 0] else: # Solve for the k lowest eigenvalues, where k=degeneracy energies, states = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig(k=degeneracy) states = states.T ground_state = states[:, 0] overlap = np.zeros(energies.shape[0]**2) diag = np.zeros(energies.shape[0]) for op in eq.jump_operators[0].jump_operators: overlap = overlap + (np.abs(states.conj().T @ op @ states) **2).flatten() diag = diag - ground_state.conj().T @ op.conj( ).T @ op @ ground_state overlap = overlap + np.diag(diag).flatten() else: energies, states = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig(k='all') states = states.T overlap = np.zeros(energies.shape[0]**2) for op in eq.jump_operators[0].jump_operators: overlap = overlap + (np.abs(states.conj().T @ op @ states)** 2).flatten() return overlap.real
def jump_rate_scaling(): graph = Graph(nx.star_graph(n=4)) # line_graph(n=3) energy = 1 phis = np.arange(.001, .01, .001) rates = np.zeros(len(phis)) rydberg_hamiltonian_cost = hamiltonian.HamiltonianMIS(graph, IS_subspace=True, code=qubit) print(rydberg_hamiltonian_cost.hamiltonian) for d in range(len(phis)): print(d) laser = EffectiveOperatorHamiltonian(omega_g=np.cos(phis[d]), omega_r=np.sin(phis[d]), energies=(energy, ), graph=graph) dissipation = EffectiveOperatorDissipation(omega_g=np.cos(phis[d]), omega_r=np.sin(phis[d]), rates=(energy, ), graph=graph) # Find the dressed states simulation = SchrodingerEquation(hamiltonians=[laser]) eigval, eigvec = simulation.eig() eigval = [ rydberg_hamiltonian_cost.approximation_ratio( State(eigvec[i].T, is_ket=True, graph=graph, IS_subspace=True)) for i in range(eigval.shape[0]) ] print(eigval) optimal_eigval_index = np.argmax(eigval) optimal_eigval = eigvec[optimal_eigval_index] rate = 0 for i in range(graph.n): # Compute the decay rate of the MIS into other states for j in range(eigvec.shape[0]): if j != optimal_eigval_index: rate += np.abs(eigvec[j] @ dissipation.jump_operators[i] @ optimal_eigval.T)**2 rates[d] = rate fit = np.polyfit(np.log(phis), np.log(rates), deg=1) plt.scatter(np.log(phis), np.log(rates), color='teal') plt.plot(np.log(phis), fit[0] * np.log(phis) + fit[1], color='k', label=r'$y=$' + str(np.round(fit[0], decimals=2)) + r'$x+$' + str(np.round(fit[1], decimals=2))) plt.legend() plt.xlabel(r'$\log(\phi)$') plt.ylabel(r'$\log(\rm{rate})$') plt.show()
def find_gap(graph, use_Z2_symmetry=True): # Compute the number of ground states and first excited states cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry) # Generate a dummy graph with one fewer nodes # Cut cost and driver hamiltonian in half to account for Z2 symmetry if use_Z2_symmetry: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1))) driver.hamiltonian row = list(range(2 ** (graph.n - 1))) column = list(range(2 ** (graph.n - 1))) column.reverse() driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix( (np.ones(2 ** (graph.n - 1)), (row, column))) else: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n))) n_ground = len(ground_states(cost.hamiltonian)) times = np.arange(0, 1, .01) def schedule(t): cost.energies = (t,) driver.energies = (t - 1,) min_gap = np.inf for i in range(len(times)): schedule(times[i]) eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False) eigvals = np.flip(eigvals) if eigvals[-1] - eigvals[0] < min_gap: min_gap = eigvals[-1] - eigvals[0] return min_gap, n_ground
def Q(eq: SchrodingerEquation, dissipation): # Diagonalize Hamiltonian # Compute Q matrix elements eigval, eigvec = eq.eig() q = np.zeros((len(eigval), len(eigval))) jump_operators = np.array(dissipation.jump_operators) # For each pair of eigenvectors for pair in itertools.product(range(eigvec.shape[0]), repeat=2): # Compute the rate if pair[0] != pair[1]: eigvec1 = State(tools.outer_product(eigvec[pair[0]].T, eigvec[pair[0]].T), is_ket=False, IS_subspace=True, graph=graph) eigvec2 = State(tools.outer_product(eigvec[pair[1]].T, eigvec[pair[1]].T), is_ket=False, IS_subspace=True, graph=graph) rates = np.sum([ np.trace(eigvec2 @ jump_operators[i] @ eigvec1 @ jump_operators[i].conj().T) for i in range(len(jump_operators)) ]) q[pair[1], pair[0]] = rates.real**2 q[pair[0], pair[0]] = q[pair[0], pair[0]] - rates.real**2 return q
def rate_vs_eigenenergy(times, graph=line_graph(n=2), which='S'): """For REIT, compute the total leakage from the ground state to a given state. Plot the total leakage versus the final eigenenergy""" bad = np.arange(0, 2**graph.n, 1) if which == 'S': index = 0 elif which == 'L': index = -1 else: index = which bad = np.delete(bad, index) full_rates = np.zeros(len(bad)) # Good is a list of good eigenvalues # Bad is a list of bad eigenvalues. If 'other', defaults to all remaining eigenvalues outside of 'good' def schedule(t, tf): phi = (tf - t) / tf * np.pi / 2 x.energies = (np.sin(phi)**2, ) x = hamiltonian.HamiltonianDriver(graph=graph, IS_subspace=False, energies=(1, )) zz = hamiltonian.HamiltonianMaxCut(graph, cost_function=False, energies=(1 / 2, )) #dissipation = lindblad_operators.SpontaneousEmission(graph=graph, rates=(1,)) eq = SchrodingerEquation(hamiltonians=[x, zz]) def compute_rate(): # Construct the first order transition matrix energies, states = eq.eig(k='all') rates = np.zeros(len(bad)) for j in range(graph.n): for i in range(len(bad)): rates[i] = rates[i] + (np.abs( states[i].conj() @ qubit.left_multiply( State(states[index].T), [j], qubit.Z))**2)[0, 0] # Select the relevant rates from 'good' to 'bad' print(rates) return rates for i in range(len(times)): print(times[i]) schedule(times[i], 1) full_rates = full_rates + compute_rate() eigval, eigvec = eq.eig(k='all') return full_rates, eigval
def k_beta(delta_r_bar): dt = 0.001 overlap = 0 for time in [0, 1-2*dt]: def normalize_phase(eig): where_nonzero = np.argwhere(np.absolute(eig) > 1e-9)[0] eig = np.e**(-1j*np.angle(eig[where_nonzero[0], where_nonzero[1]])) * eig # We can take the eigenvalues to be real for this Hamiltonian eig = eig.real return eig / np.linalg.norm(eig) schedule(time, 1, delta_r_bar=delta_r_bar) # Construct the first order transition matrix ground_energy, ground_state = SchrodingerEquation(hamiltonians=eq.hamiltonians).ground_state() ground_state = normalize_phase(ground_state) ham = scipy.sparse.csr_matrix((-ground_energy * np.ones(graph.num_independent_sets), (range(graph.num_independent_sets), range(graph.num_independent_sets))), shape=(graph.num_independent_sets, graph.num_independent_sets)) for h in eq.hamiltonians: ham = ham + h.hamiltonian schedule(time+dt, 1, delta_r_bar=delta_r_bar) # Construct the first order transition matrix ground_energy_dt, ground_state_dt = SchrodingerEquation(hamiltonians=eq.hamiltonians).ground_state() ground_state_dt = normalize_phase(ground_state_dt) d_ground_state_dt = (ground_state_dt - ground_state) / dt # Construct a projector out of the ground subspace proj = np.identity(graph.num_independent_sets) proj = proj - tools.outer_product(ground_state, ground_state) if graph.degeneracy>1 and time == 0: # Project out of the ground subspace energies, states = SchrodingerEquation(hamiltonians=eq.hamiltonians).eig(k=graph.degeneracy) for i in range(graph.degeneracy-1): proj = proj - tools.outer_product(states[i+1, np.newaxis].T, states[i+1, np.newaxis].T) d_ground_state_dt = proj @ d_ground_state_dt # Multiply by 1/(H-E_0) # If at the end, remove the rows corresponding to degeneracies if time != 0: d_ground_state_dt = d_ground_state_dt[graph.degeneracy:] ham = ham[graph.degeneracy:, graph.degeneracy:] else: d_ground_state_dt = d_ground_state_dt[:-1] ham = ham[:-1, :-1] res = scipy.sparse.linalg.spsolve(ham, d_ground_state_dt).real overlap += np.linalg.norm(res)**2 return overlap
def gap(t): t = t[0] if verbose: print('time', t) schedule(t) eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False) eigvals = np.flip(eigvals) if verbose: print('gap', eigvals[-1]-eigvals[0]) return eigvals[-1]-eigvals[0]
def evolve(t, part='real'): # Generate a mapping from a time to an index dt = .001 phase = integrated_phase_factor(t) schedule(t, 1) eigvals, eigvecs = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() schedule(t - dt, 1) eigvals_past, eigvecs_past = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() # ensure that the first eigenvalue is positive if eigvecs[0][0] <= 0: eigvecs[0] = eigvecs[0] * -1 if eigvecs_past[0][0] <= 0: eigvecs_past[0] = eigvecs_past[0] * -1 if eigvecs[-1][0] <= 0: eigvecs[-1] = eigvecs[-1] * -1 if eigvecs_past[-1][0] <= 0: eigvecs_past[-1] = eigvecs_past[-1] * -1 #print(eigvecs[0]-eigvecs_past[0]) #print(t, np.vdot(eigvecs[-1], (eigvecs[0]-eigvecs_past[0])/dt)) if part == 'real': res = (np.vdot(eigvecs[-1], (eigvecs[0] - eigvecs_past[0]) / dt) * np.exp(-1j * phase)).real if res < 0: print('warning: real part is negative', t, res) #return 0 return res else: res = (np.vdot(eigvecs[-1], (eigvecs[0] - eigvecs_past[0]) / dt) * np.exp(-1j * phase)).imag if res > 0: print('warning: complex part is positive', t, res) #return 0 return res
def evolve(t): # Generate a mapping from a time to an index print(t) schedule(t, 1) ground_energy, ground_state = SchrodingerEquation( hamiltonians=eq.hamiltonians).ground_state() overlap = 0 for op in eq.jump_operators[0].jump_operators: overlap = overlap + (np.abs( ground_state.conj().T @ op @ ground_state)**2)[0, 0] - ( ground_state.conj().T @ op.conj().T @ op @ ground_state)[0, 0] #print(t, overlap.real) return overlap.real
def low_energy_subspace_at_fixed_time(graph, s, use_Z2_symmetry=True, n_ground=None, k=None, p=2): # Compute the number of ground states and first excited states if p == 2: cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry) if p != 2: ham = graph if use_Z2_symmetry: graph = sk_integer(int(np.log2(graph.shape[0]))+1) else: graph = sk_integer(int(np.log2(graph.shape[0]))) cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry) # Replace the cost Hamiltonian cost._diagonal_hamiltonian = ham if use_Z2_symmetry: cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n-1)), np.arange(2 ** (graph.n-1)))),shape=(2 ** (graph.n-1), 2 ** (graph.n-1))) else: cost._hamiltonian = sparse.csr_matrix((cost._diagonal_hamiltonian.flatten(), (np.arange(2 ** (graph.n)), np.arange(2 ** (graph.n)))),shape=(2 ** (graph.n), 2 ** (graph.n))) # Generate a dummy graph with one fewer nodes # Cut cost and driver hamiltonian in half to account for Z2 symmetry if use_Z2_symmetry: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1))) driver.hamiltonian row = list(range(2 ** (graph.n - 1))) column = list(range(2 ** (graph.n - 1))) column.reverse() driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix( (np.ones(2 ** (graph.n - 1)), (row, column))) else: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n))) if k is None: if n_ground is None: n_ground = len(ground_states(cost.hamiltonian)) k=n_ground+1 def schedule(t): cost.energies = (np.sqrt(np.math.factorial(p)/(2 * graph.n**(p-1))) * t,) driver.energies = (1 - t,) schedule(s) eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=k, return_eigenvectors=False) eigvals = np.flip(eigvals) return s, eigvals
def gap_over_time(graph, verbose=False, use_Z2_symmetry=True): # Compute the number of ground states and first excited states cost = HamiltonianMaxCut(graph, cost_function=False, use_Z2_symmetry=use_Z2_symmetry) print(np.min(cost.hamiltonian) / graph.n ** (3 / 2)) # Generate a dummy graph with one fewer nodes # Cut cost and driver hamiltonian in half to account for Z2 symmetry if use_Z2_symmetry: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n - 1))) driver.hamiltonian row = list(range(2 ** (graph.n - 1))) column = list(range(2 ** (graph.n - 1))) column.reverse() driver._hamiltonian = driver._hamiltonian + (-1) ** graph.n * sparse.csr_matrix( (np.ones(2 ** (graph.n - 1)), (row, column))) else: driver = HamiltonianDriver(graph=Graph(nx.complete_graph(graph.n))) if verbose: print(ground_states(cost.hamiltonian)) n_ground = len(ground_states(cost.hamiltonian)) # print(driver.hamiltonian.todense()) # print(np.linalg.eigh(driver.hamiltonian.todense())) if verbose: print('degeneracy ', n_ground) times = np.arange(0, 1, .01) def schedule(t): cost.energies = (1 / np.sqrt(graph.n) * t,) driver.energies = (1 - t,) all_eigvals = np.zeros((len(times), n_ground + 1)) for i in range(len(times)): if verbose: print(times[i]) schedule(times[i]) eigvals = SchrodingerEquation(hamiltonians=[cost, driver]).eig(k=n_ground + 1, return_eigenvectors=False) eigvals = np.flip(eigvals) all_eigvals[i] = eigvals for i in range(n_ground + 1): plt.scatter(times, all_eigvals[:, i], color='blue', s=2) if verbose: print(all_eigvals) plt.xlabel(r'normalized time $s$') plt.ylabel(r'energy') plt.show()
def evolve(t): # Generate a mapping from a time to an index def find_nearest_index(array, value): array = np.asarray(array) idx = (np.abs(array - value)).argmin() return idx index = find_nearest_index(times, t) schedule(t, 1) eigenenergies, eigenbasis = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() eigenbasis = eigenbasis.T ops = [] for op in eq.jump_operators[0].jump_operators: ops.append(eigenbasis.conj().T @ op @ eigenbasis) temp = np.zeros((dim, dim)) for op in ops: temp = temp + op @ rhos[index] @ op.conj().T - 1 / 2 * ( op.conj().T @ op @ rhos[index] + rhos[index] @ op.conj().T @ op) return np.diag(np.multiply(temp, np.identity(dim)))
def run(self, time, schedule, num=None, initial_state=None, full_output=True, method='RK45', verbose=False, iterations=None): if method == 'odeint' or method == 'trotterize' and num is None: num = self._num_from_time(time, method=method) if initial_state is None: # Begin with all qudits in the ground s initial_state = State(np.zeros((self.cost_hamiltonian.hamiltonian.shape[0], 1)), code=self.code, IS_subspace=self.IS_subspace, graph=self.graph) initial_state[-1, -1] = 1 if self.noise_model is not None and self.noise_model != 'monte_carlo': initial_state = State(outer_product(initial_state, initial_state), IS_subspace=self.IS_subspace, code=self.code, graph=self.graph) if self.noise_model == 'continuous': # Initialize master equation if method == 'trotterize': master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise) results, info = master_equation.run_trotterized_solver(initial_state, 0, time, num=num, schedule=lambda t: schedule(t, time), full_output=full_output, verbose=verbose) else: master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise) results, info = master_equation.run_ode_solver(initial_state, 0, time, num=num, schedule=lambda t: schedule(t, time), method=method, full_output=full_output, verbose=verbose) elif self.noise_model is None: # Noise model is None # Initialize Schrodinger equation schrodinger_equation = SchrodingerEquation(hamiltonians=self.hamiltonian) if method == 'trotterize': results, info = schrodinger_equation.run_trotterized_solver(initial_state, 0, time, num=num, verbose=verbose, full_output=full_output, schedule=lambda t: schedule(t, time)) else: results, info = schrodinger_equation.run_ode_solver(initial_state, 0, time, num=num, verbose=verbose, schedule=lambda t: schedule(t, time), method=method, full_output=full_output) else: assert self.noise_model == 'monte_carlo' # Initialize master equation master_equation = LindbladMasterEquation(hamiltonians=self.hamiltonian, jump_operators=self.noise) results, info = master_equation.run_stochastic_wavefunction_solver(initial_state, 0, time, num=num, full_output=full_output, schedule=lambda t: schedule(t, time), method=method, verbose=verbose, iterations=iterations) if len(results.shape) == 2: # The algorithm has output a single state out = [State(results, IS_subspace=self.IS_subspace, code=self.code, graph=self.graph)] elif len(results.shape) == 3: # The algorithm has output an array of states out = [State(res, IS_subspace=self.IS_subspace, code=self.code, graph=self.graph) for res in results] else: assert len(results.shape) == 4 if self.noise_model != 'monte_carlo': raise Exception('Run output has more dimensions than expected') out = [] for i in range(results.shape[0]): # For all iterations res = [] for j in range(results.shape[1]): # For all times res.append( State(results[i, j, ...], IS_subspace=self.IS_subspace, code=self.code, graph=self.graph)) out.append(res) return out, info
def run_stochastic_wavefunction_solver(self, s, t0, tf, num=50, schedule=lambda t: None, times=None, full_output=True, method='trotterize', verbose=False, iterations=None): if iterations is None: iterations = 1 # Compute probability that we have a jump # For the stochastic solver, we have to return a times dictionary assert s.is_ket # Save state properties is_ket = s.is_ket code = s.code IS_subspace = s.IS_subspace state_shape = s.shape graph = s.graph num_jumps = [] jump_times = [] jump_indices = [] if times is None and (method == 'odeint' or method == 'trotterize'): times = np.linspace(0, 1, num=int(num)) * (tf - t0) + t0 if not (method == 'odeint' or method == 'trotterize'): raise NotImplementedError schrodinger_equation = SchrodingerEquation( hamiltonians=self.hamiltonians + self.jump_operators) def f(t, state): if method == 'odeint': t, state = state, t if method != 'odeint': state = np.reshape(np.expand_dims(state, axis=0), state_shape) state = State(state, is_ket=is_ket, code=code, IS_subspace=IS_subspace) return np.asarray( schrodinger_equation.evolution_generator(state)).flatten() assert len(times) > 1 if full_output: outputs = np.zeros( (iterations, len(times), s.shape[0], s.shape[1]), dtype=np.complex128) else: outputs = np.zeros((iterations, s.shape[0], s.shape[1]), dtype=np.complex128) dt = times[1] - times[0] for k in range(iterations): jump_time = [] jump_indices_iter = [] num_jump = 0 out = s.copy() if verbose: print('Iteration', k) for (j, time) in zip(range(times.shape[0]), times): # Update energies schedule(time) for i in range(len(self.jump_operators)): if i == 0: jumped_states, jump_probabilities = self.jump_operators[ i].jump_rate( out, list(range(out.number_physical_qudits))) jump_probabilities = jump_probabilities * dt elif i > 0: js, jp = self.jump_operators[i].jump_rate( out, list(range(out.number_physical_qudits))) jump_probabilities = np.concatenate( [jump_probabilities, jp * dt]) jumped_states = np.concatenate([jumped_states, js]) if len(self.jump_operators) == 0: jump_probability = 0 else: jump_probability = np.sum(jump_probabilities) if np.random.uniform() < jump_probability and len( self.jump_operators) != 0: # Then we should do a jump num_jump += 1 jump_time.append(time) if verbose: print('Jumped with probability', jump_probability, 'at time', time) jump_index = np.random.choice( list(range(len(jump_probabilities))), p=jump_probabilities / np.sum(jump_probabilities)) jump_indices_iter.append(jump_index) out = State(jumped_states[jump_index, ...] * np.sqrt(dt / jump_probabilities[jump_index]), is_ket=is_ket, code=code, IS_subspace=IS_subspace, graph=graph) # Normalization factor else: state_asarray = np.asarray(out) if method == 'odeint': z = odeintw(f, state_asarray, [0, dt], full_output=False) out = State(z[-1], code=code, IS_subspace=IS_subspace, is_ket=is_ket, graph=graph) else: for hamiltonian in self.hamiltonians: out = hamiltonian.evolve(out, dt) for jump_operator in self.jump_operators: if isinstance(jump_operator, LindbladJumpOperator): # Non-hermitian evolve out = jump_operator.nh_evolve(out, dt) elif isinstance(jump_operator, QuantumChannel): out = jump_operator.evolve(out, dt) out = State(out, code=code, IS_subspace=IS_subspace, is_ket=is_ket, graph=graph) # Normalize the output out = out / np.linalg.norm(out) # We don't do np.sqrt(1 - jump_probability) because it is only a first order expansion, # and is often inaccurate. Things will quickly diverge if the state is not normalized if full_output: outputs[k, j, ...] = out jump_times.append(jump_time) jump_indices.append(jump_indices_iter) num_jumps.append(num_jump) if not full_output: outputs[k, ...] = out return outputs, { 't': times, 'jump_times': jump_times, 'num_jumps': num_jumps, 'jump_indices': jump_indices }
def energy_difference(t): schedule(t, 1) eigvals, eigvecs = SchrodingerEquation( hamiltonians=eq.hamiltonians).eig() return eigvals[-1] - eigvals[0]
def optimize_schedule(times, bad, graph=line_graph(n=2), initial_guesses=10): # Good is a list of good eigenvalues # Bad is a list of bad eigenvalues. If 'other', defaults to all remaining eigenvalues outside of 'good' def schedule_hybrid(t, tf): phi = (tf - t) / tf * np.pi / 2 laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) laser = EffectiveOperatorHamiltonian(graph=graph, IS_subspace=True, energies=(1, ), omega_g=np.cos(np.pi / 4), omega_r=np.sin(np.pi / 4)) energy_shift = hamiltonian.HamiltonianEnergyShift(IS_subspace=True, graph=graph, energies=(2.5, ), index=0) dissipation = EffectiveOperatorDissipation(graph=graph, omega_r=1, omega_g=1, rates=(1, )) schedule = schedule_hybrid eq = SchrodingerEquation(hamiltonians=[laser, energy_shift]) def optimize_delta_r(delta_r): delta_r = delta_r[0] energy_shift.energies = (delta_r, ) if bad == 'other': ground_energy, ground_state = eq.ground_state() overlap = 0 for op in dissipation.jump_operators: overlap = overlap + (np.abs(ground_state.conj().T @ op @ ground_state) ** 2)[0, 0] - \ (ground_state.conj().T @ op.conj().T @ op @ ground_state)[0, 0] return overlap.real else: # Construct the first order transition matrix energies, states = eq.eig(k='all') states = states.T rates = np.zeros(energies.shape[0]**2) for op in dissipation.jump_operators: rates = rates + (np.abs(states.conj().T @ op @ states)** 2).flatten() # Select the relevant rates from 'good' to 'bad' rates = np.reshape( rates.real, (int(np.sqrt(rates.shape[0])), int(np.sqrt(rates.shape[0])))) rate = 0 for i in bad: rate += rates[i, 0] # print(time, rates, rate, delta_r) return rate delta_rs = [] rates = [] for time in times: print(time) schedule(time, 1) min_rate = np.inf min_delta_r = np.inf for i in range(initial_guesses): bounds = [-3, 3] rand = np.random.uniform(bounds[0], bounds[1], 1) delta_r = scipy.optimize.minimize(lambda dr: optimize_delta_r(dr), rand, bounds=[bounds]) print(time, delta_r.x[0], delta_r.fun) if delta_r.fun < min_rate: min_delta_r = delta_r.x[0] min_rate = delta_r.fun delta_rs.append(min_delta_r) rates.append(min_rate) return delta_rs, rates
def k_beta(delta_r_bar, graph: Graph, verbose=False, mode='hybrid'): def schedule_hybrid(t, tf, delta_r_bar=0): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = (delta_r_bar * np.sin(phi) ** 2,) laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_adiabatic(t, tf, delta_r_bar=0): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = ((delta_r_bar+1) * np.sin(phi) ** 2-np.cos(phi) ** 2,) laser.omega_g = np.sqrt(np.abs(np.cos(phi)*np.sin(phi))) laser.omega_r = np.sqrt(np.abs(np.cos(phi)*np.sin(phi))) dissipation.omega_g = np.sqrt(np.abs(np.cos(phi)*np.sin(phi))) dissipation.omega_r = np.sqrt(np.abs(np.cos(phi)*np.sin(phi))) if mode == 'hybrid': schedule = schedule_hybrid elif mode=='adiabatic': schedule = schedule_adiabatic laser = EffectiveOperatorHamiltonian(graph=graph, IS_subspace=True, energies=(1,), omega_g=1, omega_r=1) energy_shift = hamiltonian.HamiltonianEnergyShift(IS_subspace=True, graph=graph, index=0) dissipation = EffectiveOperatorDissipation(graph=graph, omega_r=1, omega_g=1, rates=(1,)) eq = LindbladMasterEquation(hamiltonians=[laser, energy_shift], jump_operators=[dissipation]) dt = 0.001 overlap = 0 for time in [0, 1-2*dt]: def normalize_phase(eig): where_nonzero = np.argwhere(np.absolute(eig) > 1e-9)[0] eig = np.e**(-1j*np.angle(eig[where_nonzero[0], where_nonzero[1]])) * eig # We can take the eigenvalues to be real for this Hamiltonian eig = eig.real return eig / np.linalg.norm(eig) schedule(time, 1, delta_r_bar=delta_r_bar) # Construct the first order transition matrix ground_energy, ground_state = SchrodingerEquation(hamiltonians=eq.hamiltonians).ground_state() ground_state = normalize_phase(ground_state) ham = scipy.sparse.csr_matrix((-ground_energy * np.ones(graph.num_independent_sets), (range(graph.num_independent_sets), range(graph.num_independent_sets))), shape=(graph.num_independent_sets, graph.num_independent_sets)) for h in eq.hamiltonians: ham = ham + h.hamiltonian schedule(time+dt, 1, delta_r_bar=delta_r_bar) # Construct the first order transition matrix ground_energy_dt, ground_state_dt = SchrodingerEquation(hamiltonians=eq.hamiltonians).ground_state() ground_state_dt = normalize_phase(ground_state_dt) d_ground_state_dt = (ground_state_dt - ground_state) / dt # Construct a projector out of the ground subspace proj = np.identity(graph.num_independent_sets) proj = proj - tools.outer_product(ground_state, ground_state) if graph.degeneracy>1 and time == 0: # Project out of the ground subspace energies, states = SchrodingerEquation(hamiltonians=eq.hamiltonians).eig(k=graph.degeneracy) for i in range(graph.degeneracy-1): proj = proj - tools.outer_product(states[i+1, np.newaxis].T, states[i+1, np.newaxis].T) d_ground_state_dt = proj @ d_ground_state_dt # Multiply by 1/(H-E_0) # If at the end, remove the rows corresponding to degeneracies if time != 0: d_ground_state_dt = d_ground_state_dt[graph.degeneracy:] ham = ham[graph.degeneracy:, graph.degeneracy:] else: d_ground_state_dt = d_ground_state_dt[:-1] ham = ham[:-1, :-1] res = scipy.sparse.linalg.spsolve(ham, d_ground_state_dt).real overlap += np.linalg.norm(res)**2 return overlap
diag_energy = adjacent_energy/8 second_nearest_energy = adjacent_energy/64 for i in range(layout.shape[0] - 1): for j in range(layout.shape[1] - 1): if layout[i, j] == 1: # There is a spin here pass #imperfect_blockade_performance() graph = line_graph(n=3, return_mis=False) phi = np.pi/2 laser = EffectiveOperatorHamiltonian(omega_g=np.cos(phi), omega_r=np.sin(phi), graph=graph) eq = SchrodingerEquation(hamiltonians=[laser]) state = State(np.ones((5, 1), dtype=np.complex128)/np.sqrt(5)) print(np.round(eq.eig(k='all')[1], decimals=3)) def performance_vs_alpha(): # alpha = gamma * omega^2/(delta^2 T) graph = line_graph(n=3) gammas = np.array([1, 5, 10]) times = np.array([1, 5, 10]) deltas = np.array([20, 30]) omegas = np.array([1, 5, 10]) for (g, d, o) in zip(gammas, deltas, omegas): # Find the performance vs alpha laser = EffectiveOperatorHamiltonian(graph=graph) dissipation = EffectiveOperatorDissipation(graph=graph)
def rate_vs_eigenenergy(times, graph=line_graph(n=2), mode='hybrid', which='S'): """For REIT, compute the total leakage from the ground state to a given state. Plot the total leakage versus the final eigenenergy""" if which == 'S': index = 0 elif which == 'L': index = -1 else: index = which # Good is a list of good eigenvalues # Bad is a list of bad eigenvalues. If 'other', defaults to all remaining eigenvalues outside of 'good' def schedule_hybrid(t, tf): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = (np.sin(2 * ((tf - t) / tf - 1 / 2) * np.pi), ) laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_reit(t, tf): phi = (tf - t) / tf * np.pi / 2 laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_adiabatic(t, tf): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = (np.sin(2 * ((tf - t) / tf - 1 / 2) * np.pi), ) laser.omega_g = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) laser.omega_r = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) dissipation.omega_g = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) dissipation.omega_r = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) laser = EffectiveOperatorHamiltonian(graph=graph, IS_subspace=True, energies=(1, ), omega_g=np.cos(np.pi / 4), omega_r=np.sin(np.pi / 4)) energy_shift = hamiltonian.HamiltonianEnergyShift(IS_subspace=True, graph=graph, energies=(2.5, ), index=0) dissipation = EffectiveOperatorDissipation(graph=graph, omega_r=1, omega_g=1, rates=(1, )) if mode == 'hybrid': schedule = schedule_hybrid elif mode == 'adiabatic': schedule = schedule_adiabatic elif mode == 'reit': schedule = schedule_reit if mode != 'reit': eq = SchrodingerEquation(hamiltonians=[laser, energy_shift]) else: eq = SchrodingerEquation(hamiltonians=[laser]) def compute_rate(): # Construct the first order transition matrix global full_rates energies, states = eq.eig(k='all') states = states.T rates = np.zeros(energies.shape[0]**2) for op in dissipation.jump_operators: rates = rates + (np.abs(states.conj().T @ op @ states)** 2).flatten() # Select the relevant rates from 'good' to 'bad' rates = np.reshape( rates.real, (graph.num_independent_sets, graph.num_independent_sets)) return rates[:, index].flatten() for i in range(len(times)): print(times[i]) schedule(times[i], 1) full_rates = compute_rate() eigval, eigvec = eq.eig(k='all') return full_rates, eigval
def dissipation_over_time(times, delta_rs, graph=line_graph(n=2), mode='hybrid', which='S'): rates = np.zeros((len(delta_rs), len(times))) if which == 'S': index = 0 if which == 'L': index = graph.num_independent_sets - 1 optimal_energy = graph.independent_sets[index][1] degeneracy = 0 bad = [] for IS in graph.independent_sets: if graph.independent_sets[IS][1] == optimal_energy: degeneracy += 1 else: bad.append(IS) if degeneracy == 1: bad = 'other' # Good is a list of good eigenvalues # Bad is a list of bad eigenvalues. If 'other', defaults to all remaining eigenvalues outside of 'good' def schedule_hybrid(t, tf): phi = (tf - t) / tf * np.pi / 2 laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_adiabatic(t, tf): phi = (tf - t) / tf * np.pi / 2 laser.omega_g = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) laser.omega_r = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) dissipation.omega_g = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) dissipation.omega_r = np.sqrt(np.abs(np.sin(phi) * np.cos(phi))) laser = EffectiveOperatorHamiltonian(graph=graph, IS_subspace=True, energies=(1, ), omega_g=np.cos(np.pi / 4), omega_r=np.sin(np.pi / 4)) energy_shift = hamiltonian.HamiltonianEnergyShift(IS_subspace=True, graph=graph, energies=(2.5, ), index=0) dissipation = EffectiveOperatorDissipation(graph=graph, omega_r=1, omega_g=1, rates=(1, )) if mode == 'hybrid': schedule = schedule_hybrid elif mode == 'adiabatic': schedule = schedule_adiabatic eq = SchrodingerEquation(hamiltonians=[laser, energy_shift]) def compute_rate(delta_r): energy_shift.energies = (delta_r, ) if bad == 'other' and which == 'S': ground_energy, ground_state = eq.ground_state() overlap = 0 for op in dissipation.jump_operators: overlap = overlap - (np.abs(ground_state.conj().T @ op @ ground_state) ** 2)[0, 0] + \ (ground_state.conj().T @ op.conj().T @ op @ ground_state)[0, 0] return overlap.real else: # Construct the first order transition matrix energies, states = eq.eig(k='all') states = states.T rates = np.zeros(energies.shape[0]**2) for op in dissipation.jump_operators: rates = rates + (np.abs(states.conj().T @ op @ states)** 2).flatten() # Select the relevant rates from 'good' to 'bad' rates = np.reshape( rates.real, (int(np.sqrt(rates.shape[0])), int(np.sqrt(rates.shape[0])))) rate = 0 for i in bad: rate += rates[i, index] return rate for i in range(len(times)): print(times[i]) schedule(times[i], 1) for j in range(len(delta_rs)): rates[j, i] = compute_rate(delta_rs[j]) return rates
plt.legend() plt.tight_layout() plt.show() energies = [ 1.2679491924311228, 1.5505102572168215, 1.9999999999999991, 2.369723378886908, 2.7797808497772705, 3.169041327665013, 3.569285832151517, 3.963676830741, 4.361224193455282, 4.757059445281552, 5.153829596112403, 5.550087062995971, 5.946626869831041, 6.343010696247533, 6.739480949302909, 7.135903198980722, 7.532352169228637, 7.928786237774574, 8.325228630906286 ] #plt.scatter(range(3, 22), energies) #plt.show() fidelity_vs_alpha() for i in range(0, 0): graph = ring_graph(i) laser = EffectiveOperatorHamiltonian(1, 1, energies=(1, ), graph=graph, IS_subspace=True) #energy_shift = hamiltonian.HamiltonianEnergyShift(graph=graph, IS_subspace=True) eq = SchrodingerEquation(hamiltonians=[laser]) energy, ground_state = eq.ground_state() print(energy) #, np.abs(ground_state)**2) """plt.scatter(np.arange(len(ground_state)), ground_state) plt.show()"""
def fidelity_vs_alpha(): graph = line_graph(5) omega = 100 T = 100 delta_e = 1000 beta = delta_e / (omega**2 * T) print('beta = ' + str(beta)) gammas = 10**np.linspace(1.75, 3.25, 3) print(gammas * omega**2 * T / delta_e**2) dissipation = EffectiveOperatorDissipation(0, 0, graph=graph, IS_subspace=True, rates=(1 / delta_e**2, )) laser = EffectiveOperatorHamiltonian(0, 0, energies=(1 / delta_e, ), graph=graph, IS_subspace=True) energy_shift = hamiltonian.HamiltonianEnergyShift(graph=graph, IS_subspace=True) def schedule_exp_fixed(t, tf=T): x = t / tf * np.pi / 2 amplitude = np.abs(np.cos(x) * np.sin(x)) # Now we need to figure out what the driver strengths should be for STIRAP omega_g = np.sin(x) * omega omega_r = np.cos(x) * omega offset = omega_r**2 - omega_g**2 # Now, choose the opposite of the STIRAP sequence energy_shift.energies = (offset / 1000, ) laser.omega_g = np.sqrt(amplitude) * omega laser.omega_r = np.sqrt(amplitude) * omega dissipation.omega_g = np.sqrt(amplitude) * omega dissipation.omega_r = np.sqrt(amplitude) * omega def schedule_reit(t, tf=T): x = t / tf * np.pi / 2 omega_g = np.sin(x) * omega omega_r = np.cos(x) * omega energy_shift.energies = (0, ) laser.omega_g = omega_g laser.omega_r = omega_r dissipation.omega_g = omega_g dissipation.omega_r = omega_r eq = LindbladMasterEquation(hamiltonians=[laser, energy_shift], jump_operators=[dissipation]) nonoise_eq = SchrodingerEquation(hamiltonians=[laser, energy_shift]) state = State(np.array([[0, 0], [0, 1]], dtype=np.complex128), IS_subspace=True, graph=graph, is_ket=False) state = State(np.array([[0, 0, 0], [0, 0, 0], [0, 0, 1]], dtype=np.complex128), IS_subspace=True, graph=graph, is_ket=False) state = State(np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 1]], dtype=np.complex128), IS_subspace=True, graph=graph, is_ket=False) state = State(np.zeros((13, 13), dtype=np.complex128), IS_subspace=True, graph=graph, is_ket=False) state[-1, -1] = 1 # nonoise_state = State(np.array([[0], [0], [1]], dtype=np.complex128), IS_subspace=True, graph=graph, is_ket=True) # print(np.abs(nonoise_eq.run_ode_solver(nonoise_state, 0, T, num=500, schedule=schedule_exp_fixed, method='odeint')[0][-1])**2) for a in [1]: delta_e = a * 1000 laser.energies = (1 / delta_e, ) fidelities = [] ars = [] for i in range(len(gammas)): dissipation.rates = (gammas[i] / delta_e**2, ) states = eq.run_ode_solver(state, 0, T, num=50, schedule=schedule_reit, make_valid_state=False, method='odeint') times = states[1]['t'] states = states[0] """print(gammas[i], i) for j in range(len(times)): for op in dissipation.jump_operators: print(np.trace(op.conj().T@op@states[j])-np.abs(np.trace(op@states[j]))**2)""" """print(times[25]) schedule_exp_fixed(times[25], tf=T) eigvals, eigvecs = nonoise_eq.eig(k='all') print(eigvals) print(eigvecs.conj()@states[25]@eigvecs.T)""" final_state = states[-1] #print(final_state) #print(final_state[0, 0], np.trace(final_state)) ar = (final_state[0, 0] + 2 / 3 * (final_state[1, 1] + final_state[2, 2] + final_state[3, 3] + final_state[6, 6] + final_state[5, 5] + final_state[8, 8]) + 1 / 2 * (final_state[7, 7] + final_state[4, 4] + final_state[9, 9] + final_state[10, 10] + final_state[11, 11])) print(ar) print(final_state[0, 0]) print(final_state.diagonal()) ars.append(ar) fidelities.append(final_state[0, 0]) fidelities = np.array(fidelities) ars = np.array(ars) plt.loglog() plt.ylabel(r'$1-$ fidelity') plt.xlabel(r'$\frac{\gamma \Omega^2 T}{\delta_e^2}$') plt.plot(gammas * omega**2 * T / delta_e**2, 1 - fidelities, label='fidelity') plt.plot(gammas * omega**2 * T / delta_e**2, 1 - ars, label='ar') res = np.polyfit(np.log(gammas * omega**2 * T / delta_e**2), np.log(1 - fidelities), 1) print(res) #plt.plot(gammas * omega ** 2 * T / delta_e ** 2, # 1 - np.e ** (-gammas * omega ** 2 * T / delta_e ** 2 * 0.033875094811638556), # label='bound') """if a == -1: plt.plot(gammas * omega ** 2 * T / delta_e ** 2, 0.28187096704733217 * gammas * omega ** 2 * T / delta_e ** 2, label='first order correction') else: plt.plot(gammas * omega ** 2 * T / delta_e ** 2, 0.033875094811638556 * gammas * omega ** 2 * T / delta_e ** 2, label='first order correction')""" plt.legend() plt.tight_layout() plt.show()
def alpha_correction_ar(graph=line_graph(n=2), mode='hybrid', angle=np.pi / 4, which='S', verbose=False): """For REIT, compute the total leakage from the ground state to a given state. Plot the total leakage versus the final eigenenergy""" if which == 'S': index = 0 elif which == 'L': index = graph.num_independent_sets - 1 else: index = which energies = [] bad_indices = [] target_energy = graph.independent_sets[index][1] degeneracy = 0 # Compute maximum independent set size and "bad" energies for IS in graph.independent_sets: if graph.independent_sets[IS][1] != target_energy: energies.append(graph.independent_sets[IS][1]) bad_indices.append(IS) else: degeneracy += 1 energies = np.array(energies) energies = np.abs(target_energy - energies) # Good is a list of good eigenvalues # Bad is a list of bad eigenvalues. If 'other', defaults to all remaining eigenvalues outside of 'good' def schedule_hybrid(t, tf): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = (np.sin(2 * ((tf - t) / tf - 1 / 2) * np.pi), ) laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_reit(t, tf): phi = (tf - t) / tf * np.pi / 2 laser.omega_g = np.cos(phi) laser.omega_r = np.sin(phi) dissipation.omega_g = np.cos(phi) dissipation.omega_r = np.sin(phi) def schedule_adiabatic(t, tf): phi = (tf - t) / tf * np.pi / 2 energy_shift.energies = (np.sin(2 * ((tf - t) / tf - 1 / 2) * np.pi), ) laser.omega_g = np.cos(angle) * np.sin(phi) * np.cos(phi) laser.omega_r = np.sin(angle) * np.sin(phi) * np.cos(phi) dissipation.omega_g = np.cos(angle) * np.cos(phi) * np.sin(phi) dissipation.omega_r = np.sin(angle) * np.cos(phi) * np.sin(phi) laser = EffectiveOperatorHamiltonian(graph=graph, IS_subspace=True, energies=(1, ), omega_g=np.cos(np.pi / 4), omega_r=np.sin(np.pi / 4)) energy_shift = hamiltonian.HamiltonianEnergyShift(IS_subspace=True, graph=graph, energies=(2.5, ), index=0) dissipation = EffectiveOperatorDissipation(graph=graph, omega_r=1, omega_g=1, rates=(1, )) if mode == 'hybrid': schedule = schedule_hybrid elif mode == 'adiabatic': schedule = schedule_adiabatic elif mode == 'reit': schedule = schedule_reit if mode != 'reit': eq = SchrodingerEquation(hamiltonians=[laser, energy_shift]) else: eq = SchrodingerEquation(hamiltonians=[laser]) def compute_rate(t): if verbose: print(t) schedule(t, 1) # Construct the first order transition matrix eigval, eigvec = eq.eig(k='all') eigvec = eigvec.T rates = np.zeros(eigval.shape[0]**2) for op in dissipation.jump_operators: rates = rates + ((eigvec.conj().T @ op @ eigvec)**2).flatten() # Select the relevant rates from 'good' to 'bad' rates = np.reshape( rates.real, (graph.num_independent_sets, graph.num_independent_sets)) bad_rates = np.zeros(len(bad_indices)) for i in range(len(bad_indices)): bad_rates[i] = bad_rates[i] + rates[bad_indices[i], index] return np.dot(bad_rates, energies) # Integrate to find the correction res, error = scipy.integrate.quad_vec(compute_rate, 1e-5, .9999) return res / target_energy, error