Пример #1
0
def rotate_rho(nn_state, basis, space, unitaries=None, rho=None):
    r"""Computes the density matrix rotated into some basis.

    :param nn_state: The density matrix neural network state.
    :type nn_state: qucumber.nn_states.DensityMatrix
    :param basis: The basis to rotate the density matrix to.
    :type basis: str
    :param space: The basis elements of the Hilbert space of the system :math:`\mathcal{H}`.
    :type space: torch.Tensor
    :param unitaries: A dictionary of unitary matrices associated with
                        rotation into each basis
    :type unitaries: dict(str, torch.Tensor)
    :param rho: A density matrix that the user can input to override the neural
                network state's density matrix.
    :type rho: torch.Tensor

    :returns: The rotated density matrix
    :rtype: torch.Tensor
    """
    rho = (
        nn_state.rho(space, space)
        if rho is None
        else rho.to(dtype=torch.double, device=nn_state.device)
    )

    unitaries = unitaries if unitaries else nn_state.unitary_dict
    unitaries = {k: v.to(device=nn_state.device) for k, v in unitaries.items()}
    us = [unitaries[b] for b in basis]

    rho_r = _kron_mult(us, rho)
    rho_r = _kron_mult(us, cplx.conjugate(rho_r))

    return rho_r
Пример #2
0
    def rotate_rho(self, basis, space, Z, unitaries, rho=None):
        r"""Computes the density matrix rotated into some basis

        :param basis: The basis into which to rotate the density matrix
        :type basis: numpy.ndarray
        :param space: The Hilbert space of the system
        :type space: torch.Tensor
        :param unitaries: A dictionary of unitary matrices associated with
                          rotation into each basis
        :type unitaries: dict[str, torch.Tensor]
        :returns: The rotated density matrix
        :rtype: torch.Tensor
        """
        rho = self.rhoRBM(space, space) if rho is None else rho

        unitaries = {k: v for k, v in unitaries.items()}
        us = [unitaries[b] for b in basis]
        if len(us) == 0:
            return rho

        # After ensuring there is more than one measurement, compute the
        # composite unitary by repeated Kronecker products
        U = us[0]
        for index in range(len(us) - 1):
            U = cplx.kronecker_prod(U, us[index + 1])

        U = U.to(rho)
        U_dag = cplx.conjugate(U)

        rot_rho = cplx.matmul(rho, U_dag)
        rot_rho_ = cplx.matmul(U, rot_rho)

        return rot_rho_
Пример #3
0
    def init_gradient(self, basis, sites):
        r"""Initializes all required variables for gradient computation

        :param basis: The bases of the measurements
        :type basis: numpy.ndarray
        :param sites: The sites where the measurements are not
                      in the computational basis
        """
        UrhoU = torch.zeros(2, dtype=torch.double, device=self.device)
        v = torch.zeros(self.num_visible,
                        dtype=torch.double,
                        device=self.device)
        vp = torch.zeros(self.num_visible,
                         dtype=torch.double,
                         device=self.device)
        Us = torch.stack([self.unitary_dict[b]
                          for b in basis[sites]]).cpu().numpy()
        Us_dag = (torch.stack([
            cplx.conjugate(self.unitary_dict[b]) for b in basis[sites]
        ]).cpu().numpy())

        rotated_grad = [
            torch.zeros(2,
                        getattr(self, net).num_pars,
                        dtype=torch.double,
                        device=self.device) for net in self.networks
        ]

        return UrhoU, v, vp, Us, Us_dag, rotated_grad
Пример #4
0
 def test_matrix_conjugate(self):
     matrix = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=torch.double)
     expect = torch.tensor(
         [[[1, 3], [2, 4]], [[-5, -7], [-6, -8]]], dtype=torch.double
     )
     self.assertTensorsEqual(
         cplx.conjugate(matrix), expect, msg="Matrix conjugate failed!"
     )
Пример #5
0
    def test_conjugate(self):
        vector = torch.tensor([[1, 2], [3, 4]], dtype=torch.double)

        expect = torch.tensor([[1, 2], [-3, -4]], dtype=torch.double)

        self.assertTensorsEqual(
            cplx.conjugate(vector), expect, msg="Vector conjugate failed!"
        )
Пример #6
0
def test_density_matrix_hermiticity():
    nn_state = DensityMatrix(5, 5, 5, gpu=False)

    space = nn_state.generate_hilbert_space(5)
    Z = nn_state.normalization(space)
    rho = nn_state.rho(space, space) / Z

    assert torch.equal(rho, cplx.conjugate(rho)), "DensityMatrix should be Hermitian!"
Пример #7
0
    def apply(self, nn_state, samples):
        r"""Computes the swap operator which an estimator for the 2nd Renyi
        entropy.

        The swap operator requires access to two identical copies of a
        wavefunction. In practice, this translates to the requirement of
        having two independent sets of samples from the wavefunction replicas.
        For this purpose, the batch of samples stored in the param samples is
        split into two subsets. Although this procedure is designed to break
        the autocorrelation between the samples, it must be used with caution.
        For a fully unbiased estimate of the entanglement entropy, the batch
        of samples needs to be built from two independent initializations of
        the wavefunction each having a different random number generator.

        :param nn_state: The WaveFunction that drew the samples.
        :type nn_state: qucumber.nn_states.WaveFunctionBase
        :param samples: A batch of samples to calculate the observable on.
                        Must be using the :math:`\sigma_i = 0, 1` convention.
        :type samples: torch.Tensor
        """
        samples = samples.to(device=nn_state.device, copy=True)

        # split the batch of samples into two equal batches
        # if their total number is odd, the last sample is ignored
        _ns = samples.shape[0] // 2
        samples1 = samples[:_ns, :]
        samples2 = samples[_ns:_ns * 2, :]

        psi_ket1 = nn_state.psi(samples1)
        psi_ket2 = nn_state.psi(samples2)

        psi_ket = cplx.elementwise_mult(psi_ket1, psi_ket2)
        psi_ket_star = cplx.conjugate(psi_ket)

        samples1_, samples2_ = swap(samples1, samples2, self.A)
        psi_bra1 = nn_state.psi(samples1_)
        psi_bra2 = nn_state.psi(samples2_)

        psi_bra = cplx.elementwise_mult(psi_bra1, psi_bra2)
        psi_bra_star = cplx.conjugate(psi_bra)
        return cplx.real(cplx.elementwise_division(psi_bra_star, psi_ket_star))
Пример #8
0
def test_rotate_rho(num_visible, state_type):
    nn_state = state_type(num_visible, gpu=False)
    basis = "X" * num_visible
    unitary_dict = create_dict()

    space = nn_state.generate_hilbert_space()
    rho = nn_state.rho(space, space)

    rho_r_fast = rotate_rho(nn_state, basis, space, unitary_dict, rho=rho)

    U = reduce(cplx.kronecker_prod, [unitary_dict[b] for b in basis])
    rho_r_correct = cplx.matmul(U, rho)
    rho_r_correct = cplx.matmul(rho_r_correct, cplx.conjugate(U))

    assertAlmostEqual(rho_r_fast, rho_r_correct, msg="Fast rho rotation failed!")