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))
Beispiel #2
0
    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)
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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 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
Beispiel #7
0
    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
    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',