def gradient_S3_pullback(gamma, psi, sigma):
    """
    Compute the gradient on S3 by first calculating the gradient on S2
    and pulling back the result to S3.

    """

    x = hopf(psi)
    grad_S2 = gradient_S2_slow(gamma, x, sigma)
    
    return np.einsum('ij, abj, ib -> ia', grad_S2, pauli, psi)
    def test_compare_S2_optimized(self):
        """
        Compare different implementations of gradient on S2.

        """

        # Pre-allocate buffer
        DH2a = np.zeros(self.x.shape)

        gradient_S2(DH2a, self.gamma, self.x, self.sigma)
        DH2b = gradient_S2_slow(self.gamma, self.x, self.sigma)

        self.assertTrue( np.allclose(DH2a, DH2b, atol=1e-12) )