def set_quantum_sys(self): """ Initialize quantum propagator :param self: :return: """ omega = 1. self.quant_sys = SplitOpWignerMoyal( t=0, dt=0.05, x_grid_dim=256, x_amplitude=10., p_grid_dim=256, p_amplitude=10., # kinetic energy part of the hamiltonian k=lambda p: 0.5 * p**2, # potential energy part of the hamiltonian v=lambda x: 0.5 * (omega * x)**2, # these functions are used for evaluating the Ehrenfest theorems x_rhs=lambda p: p, p_rhs=lambda x, p: -omega**2 * x, ) # set randomised initial condition sigma = np.random.uniform(1., 3.) p0 = np.random.uniform(-3., 3.) x0 = np.random.uniform(-3., 3.) self.quant_sys.set_wignerfunction(lambda x, p: np.exp(-sigma * ( x - x0)**2 - (1. / sigma) * (p - p0)**2))
def __init__(self, *, D=0, gamma=0, **kwargs): """ :param D: the dephasing coefficient (see Eq. (36) in Ref. [*]) :param gamma: the decay coefficient (see Eq. (62) in Ref. [*]) :param kwargs: parameters passed to the parent class """ # initialize the parent class SplitOpWignerMoyal.__init__(self, **kwargs) # save new parameters self.D = D self.gamma = gamma # if the dephasing term is nonzero, modify the potential energy phase # Note that the dephasing term does not modify the Ehrenfest theorems if D: # introduce some aliases v = self.v x = self.x dt = self.dt Theta = self.Theta # Introduce the action of the dephasing term by redefining the potential term if self.time_independent_v: # pre-calculate the potential dependent phase since it is time-independent _expV = np.exp(-0.5j * dt * (v(x - 0.5 * Theta) - v(x + 0.5 * Theta)) - 0.5 * dt * D * Theta**2) @njit def expV(wignerfunction, t): wignerfunction *= _expV else: # the phase factor is time-dependent @njit def expV(wignerfunction, t): wignerfunction *= np.exp( -0.5j * dt * (v(x - 0.5 * Theta, t) - v(x + 0.5 * Theta, t) - 0.5 * dt * D * Theta**2)) self.expV = expV # if the decay term is nonzero, prepare for if gamma: # allocate an array for the extra copy of the Wigner function, i.e., for W^{(1)} in Eq. (63) of Ref. [*] self.wigner_1 = pyfftw.empty_aligned( self.wignerfunction.shape, dtype=self.wignerfunction.dtype) # p x -> theta x for self.wigner_1 self.wigner_1_transform_p2theta = pyfftw.builders.rfft( self.wigner_1, axis=0, **self.fft_params) # theta x -> p x for self.wigner_1 self.wigner_1_transform_theta2p = pyfftw.builders.irfft( self.wigner_1_transform_p2theta(), axis=0, **self.fft_params)
def __init__(self, *, dbeta=None, beta=None, **kwargs): """ :param dbeta: inverse temperature time step :param beta: inverse temperature :param kwargs: the rest of the arguments to be passed to the parent class """ SplitOpWignerMoyal.__init__(self, **kwargs) self.dbeta = dbeta self.beta = beta
def single_step_propagation(self): """ Overload the method SplitOpWignerMoyal.single_step_propagation. Perform single step propagation. The final Wigner function is not normalized. :return: self.wignerfunction """ # First follow the unitary evolution SplitOpWignerMoyal.single_step_propagation(self) # Dissipation requites to updated rho as # W = W0 * (1 - exp(-gamma*dt)) + exp(-gamma*dt) * W, self.wignerfunction *= self.w_coeff self.wignerfunction += self.w0_coeff * self.gibbs_state return self.wignerfunction
def single_step_propagation(self): """ Overload the method in the parent class. Perform single step propagation. The final Wigner function is not normalized. :return: self.wignerfunction """ # In order to get the third order propagator, we incorporate the decay term (Eq. (62) from Ref. [*]) using # the splitting scheme where we first apply the decay term for dt / 2, then the unitary propagator for full dt, # and finally the decay term for dt / 2. self.decay_term_half_step() SplitOpWignerMoyal.single_step_propagation(self) self.decay_term_half_step() return self.wignerfunction
def __init__(self, **kwargs): # Initialize the parent class SplitOpWignerMoyal.__init__(self, **kwargs) # Make sure gamma was specified try: self.gamma except AttributeError: raise AttributeError("Collision rate (gamma) was not specified") # Calculate the Gibbs state (W_0) self.gibbs_state = SplitOpWignerBloch(**kwargs).get_gibbs_state() # Pre-calculate the constants needed for the dissipator propagation self.w_coeff = np.exp(-self.gamma * self.dt) self.w0_coeff = 1 - self.w_coeff
class VisualizeDynamicsPhaseSpace: """ Class to visualize the Wigner function function dynamics in phase space. """ def __init__(self, fig): """ Initialize all propagators and frame :param fig: matplotlib figure object """ # Initialize systems self.set_quantum_sys() ################################################################# # # Initialize plotting facility # ################################################################# self.fig = fig ax = fig.add_subplot(111) ax.set_title('Wigner function, $W(x,p,t)$') extent = [ self.quant_sys.x.min(), self.quant_sys.x.max(), self.quant_sys.p.min(), self.quant_sys.p.max() ] # import utility to visualize the wigner function from wigner_normalize import WignerNormalize # generate empty plot self.img = ax.imshow([[]], extent=extent, origin='lower', cmap='seismic', norm=WignerNormalize(vmin=-0.01, vmax=0.1)) self.fig.colorbar(self.img) ax.set_xlabel('$x$ (a.u.)') ax.set_ylabel('$p$ (a.u.)') def set_quantum_sys(self): """ Initialize quantum propagator :param self: :return: """ omega = 1. self.quant_sys = SplitOpWignerMoyal( t=0, dt=0.05, x_grid_dim=256, x_amplitude=10., p_grid_dim=256, p_amplitude=10., # kinetic energy part of the hamiltonian k=lambda p: 0.5 * p**2, # potential energy part of the hamiltonian v=lambda x: 0.5 * (omega * x)**2, # these functions are used for evaluating the Ehrenfest theorems x_rhs=lambda p: p, p_rhs=lambda x, p: -omega**2 * x, ) # set randomised initial condition sigma = np.random.uniform(1., 3.) p0 = np.random.uniform(-3., 3.) x0 = np.random.uniform(-3., 3.) self.quant_sys.set_wignerfunction(lambda x, p: np.exp(-sigma * ( x - x0)**2 - (1. / sigma) * (p - p0)**2)) def __call__(self, frame_num): """ Draw a new frame :param frame_num: current frame number :return: image objects """ # propagate the wigner function self.img.set_array(self.quant_sys.propagate(20)) return self.img,
# kinetic energy part of the hamiltonian K="0.5 * {P} ** 2", # potential energy part of the hamiltonian V="0.5 * {X} ** 4", ) print("Calculating the Gibbs state...") gibbs_state = SplitOpWignerBloch(**qsys_params).get_thermal_state() # Propagate this state via the Wigner-Moyal equation print( "Check that the obtained Gibbs state is stationary under the Wigner-Moyal propagation..." ) propagator = SplitOpWignerMoyal(**qsys_params) final_state = propagator.set_wignerfunction(gibbs_state).propagate(3000) ############################################################################## # # Plot the results # ############################################################################## from wigner_normalize import WignerSymLogNorm # save common plotting parameters plot_params = dict( origin='lower', extent=[ propagator.X.min(),
beta=1. / 0.7, # kinetic energy part of the hamiltonian K="0.5 * {P} ** 2", # potential energy part of the hamiltonian V="0.5 * {X} ** 4", ) print("Calculating the Gibbs state...") gibbs_state = SplitOpWignerBloch(**qsys_params).get_thermal_state() # Propagate this state via the Wigner-Moyal equation print("Check that the obtained Gibbs state is stationary under the Wigner-Moyal propagation...") propagator = SplitOpWignerMoyal(**qsys_params) final_state = propagator.set_wignerfunction(gibbs_state).propagate(3000) ############################################################################## # # Plot the results # ############################################################################## from wigner_normalize import WignerSymLogNorm # save common plotting parameters plot_params = dict( origin='lower', extent=[propagator.X.min(), propagator.X.max(), propagator.P.min(), propagator.P.max()], cmap='seismic',