def propagate(self):
        r"""Given a wavepacket :math:`\Psi` at time :math:`t` compute the propagated
        wavepacket at time :math:`t + \tau`. We perform exactly one timestep of size
        :math:`\tau` here. This propagation is done for all packets in the list
        :math:`\{\Psi_i\}_i` and neglects any interaction between two packets.
        """
        # Cache some parameter values
        dt = self._dt
        Mi = self._Minv

        # Propagate all packets
        for packet in self._packets:
            # Unpack, no codata:
            packet = packet[0]
            eps = packet.get_eps()
            key = ("q", "p", "Q", "P", "S", "adQ")

            # Do a kinetic step of dt/2
            for component in xrange(self._number_components):
                q, p, Q, P, S, adQ = packet.get_parameters(component=component, key=key)
                q = q + 0.5 * dt * dot(Mi, p)
                Q = Q + 0.5 * dt * dot(Mi, P)
                S = S + 0.25 * dt * dot(p.T, dot(Mi, p))
                adQn = cont_angle(det(Q), reference=adQ)[0]
                packet.set_parameters((q, p, Q, P, S, adQn), component=component, key=key)

            # Do a potential step with the local quadratic part
            for component in xrange(self._number_components):
                q, p, Q, P, S = packet.get_parameters(component=component)
                V = self._potential.evaluate_local_quadratic_at(q, diagonal_component=component)

                p = p - dt * V[1]
                P = P - dt * dot(V[2], Q)
                S = S - dt * V[0]
                packet.set_parameters((q, p, Q, P, S), component=component)

            # Do a potential step with the local non-quadratic Taylor remainder
            innerproduct = packet.get_innerproduct()
            F = innerproduct.build_matrix(packet, packet, self._potential.evaluate_local_remainder_at)

            coefficients = packet.get_coefficient_vector()
            coefficients = self._matrix_exponential(F, coefficients, dt/eps**2)
            packet.set_coefficient_vector(coefficients)

            # Do a kinetic step of dt/2
            for component in xrange(self._number_components):
                q, p, Q, P, S, adQ = packet.get_parameters(component=component, key=key)
                q = q + 0.5 * dt * dot(Mi, p)
                Q = Q + 0.5 * dt * dot(Mi, P)
                S = S + 0.25 * dt * dot(p.T, dot(Mi, p))
                adQn = cont_angle(det(Q), reference=adQ)[0]
                packet.set_parameters((q, p, Q, P, S, adQn), component=component, key=key)
 def _propkin(self, h, packet):
     """Do a kinetic step of size h.
     """
     Mi = self._Minv
     key = ("q", "p", "Q", "P", "S", "adQ")
     q, p, Q, P, S, adQ = packet.get_parameters(key=key)
     q = q + h * dot(Mi, p)
     Q = Q + h * dot(Mi, P)
     S = S + 0.5 * h * dot(p.T, dot(Mi, p))
     adQn = cont_angle(det(Q), reference=adQ)[0]
     packet.set_parameters((q, p, Q, P, S, adQn), key=key)