def rotated_gradient(self, basis, sites, sample):
        Upsi, vp, Us, rotated_grad = self.init_gradient(basis, sites)
        int_sample = sample[sites].round().int().cpu().numpy()
        vp = sample.round().clone()

        grad_size = (self.num_visible * self.num_hidden + self.num_hidden +
                     self.num_visible)

        Upsi_v = torch.zeros_like(Upsi, device=self.device)
        Z = torch.zeros(grad_size, dtype=torch.double, device=self.device)
        Z2 = torch.zeros((2, grad_size),
                         dtype=torch.double,
                         device=self.device)
        U = torch.tensor([1.0, 1.0], dtype=torch.double, device=self.device)
        Ut = np.zeros_like(Us[:, 0], dtype=complex)
        ints_size = np.arange(sites.size)

        for x in range(2**sites.size):
            # overwrite rotated elements
            vp = sample.round().clone()
            vp[sites] = self.subspace_vector(x, size=sites.size)
            int_vp = vp[sites].int().cpu().numpy()
            all_Us = Us[ints_size, :, int_sample, int_vp]

            # Gradient from the rotation
            Ut = np.prod(all_Us[:, 0] + (1j * all_Us[:, 1]))
            U[0] = Ut.real
            U[1] = Ut.imag

            cplx.scalar_mult(U, self.psi(vp), out=Upsi_v)
            Upsi += Upsi_v

            # Gradient on the current configuration
            grad_vp0 = self.rbm_am.effective_energy_gradient(vp)
            grad_vp1 = self.rbm_ph.effective_energy_gradient(vp)
            rotated_grad[0] += cplx.scalar_mult(Upsi_v,
                                                cplx.make_complex(grad_vp0, Z),
                                                out=Z2)
            rotated_grad[1] += cplx.scalar_mult(Upsi_v,
                                                cplx.make_complex(grad_vp1, Z),
                                                out=Z2)

        grad = [
            cplx.scalar_divide(rotated_grad[0], Upsi)[0, :],  # Real
            -cplx.scalar_divide(rotated_grad[1], Upsi)[1, :],  # Imaginary
        ]

        return grad
示例#2
0
    def test_vector_scalar_divide(self):
        scalar = torch.tensor([1, 2], dtype=torch.double)
        vector = torch.tensor([[1, 2], [3, 4]], dtype=torch.double)
        expect = torch.tensor([[1.4, 2.0], [0.2, 0.0]], dtype=torch.double)

        self.assertTensorsAlmostEqual(
            cplx.scalar_divide(vector, scalar),
            expect,
            msg="Vector / Scalar divide failed!",
        )
示例#3
0
    def test_matrix_scalar_divide(self):
        scalar = torch.tensor([1, 2], dtype=torch.double)
        matrix = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=torch.double)

        expect = torch.tensor(
            [[[2.2, 2.8], [3.4, 4.0]], [[0.6, 0.4], [0.2, 0.0]]], dtype=torch.double
        )

        self.assertTensorsAlmostEqual(
            cplx.scalar_divide(matrix, scalar),
            expect,
            msg="Matrix / Scalar divide failed!",
        )
示例#4
0
    def rotated_gradient(self, basis, sites, sample):
        Upsi, vp, Us, rotated_grad = self.init_gradient(basis, sites)
        int_sample = sample[sites].round().int().cpu().numpy()

        Upsi_v = torch.zeros_like(Upsi, device=self.device)
        ints_size = np.arange(sites.size)

        # if the number of rotated sites is too large, fallback to loop
        #  since memory may be unable to store the entire expanded set of
        #  visible states
        if sites.size > self.max_size or (
            hasattr(self, "debug_gradient_rotation") and self.debug_gradient_rotation
        ):
            grad_size = (
                self.num_visible * self.num_hidden + self.num_hidden + self.num_visible
            )
            vp = sample.round().clone()
            Z = torch.zeros(grad_size, dtype=torch.double, device=self.device)
            Z2 = torch.zeros((2, grad_size), dtype=torch.double, device=self.device)
            U = torch.tensor([1.0, 1.0], dtype=torch.double, device=self.device)
            Ut = np.zeros_like(Us[:, 0], dtype=complex)

            for x in range(2 ** sites.size):
                # overwrite rotated elements
                vp = sample.round().clone()
                vp[sites] = self.subspace_vector(x, size=sites.size)
                int_vp = vp[sites].int().cpu().numpy()
                all_Us = Us[ints_size, :, int_sample, int_vp]

                # Gradient from the rotation
                Ut = np.prod(all_Us[:, 0] + (1j * all_Us[:, 1]))
                U[0] = Ut.real
                U[1] = Ut.imag

                cplx.scalar_mult(U, self.psi(vp), out=Upsi_v)
                Upsi += Upsi_v

                # Gradient on the current configuration
                grad_vp0 = self.rbm_am.effective_energy_gradient(vp)
                grad_vp1 = self.rbm_ph.effective_energy_gradient(vp)
                rotated_grad[0] += cplx.scalar_mult(
                    Upsi_v, cplx.make_complex(grad_vp0, Z), out=Z2
                )
                rotated_grad[1] += cplx.scalar_mult(
                    Upsi_v, cplx.make_complex(grad_vp1, Z), out=Z2
                )
        else:
            vp = sample.round().clone().unsqueeze(0).repeat(2 ** sites.size, 1)
            vp[:, sites] = self.generate_hilbert_space(size=sites.size)
            vp = vp.contiguous()

            # overwrite rotated elements
            int_vp = vp[:, sites].long().cpu().numpy()
            all_Us = Us[ints_size, :, int_sample, int_vp]

            Ut = np.prod(all_Us[..., 0] + (1j * all_Us[..., 1]), axis=1)
            U = (
                cplx.make_complex(torch.tensor(Ut.real), torch.tensor(Ut.imag))
                .to(vp)
                .contiguous()
            )

            Upsi_v = cplx.scalar_mult(U, self.psi(vp).detach())

            Upsi = torch.sum(Upsi_v, dim=1)

            grad_vp0 = self.rbm_am.effective_energy_gradient(vp, reduce=False)
            grad_vp1 = self.rbm_ph.effective_energy_gradient(vp, reduce=False)

            # since grad_vp0/1 are real, can just treat the scalar multiplication
            #  and addition as a matrix multiplication
            torch.matmul(Upsi_v, grad_vp0, out=rotated_grad[0])
            torch.matmul(Upsi_v, grad_vp1, out=rotated_grad[1])

        grad = [
            cplx.scalar_divide(rotated_grad[0], Upsi)[0, :],  # Real
            -cplx.scalar_divide(rotated_grad[1], Upsi)[1, :],  # Imaginary
        ]

        return grad
示例#5
0
    def rotated_gradient(self, basis, sites, sample):
        r"""Computes the gradients rotated into the measurement basis

        :param basis: The bases in which the measurement is made
        :type basis: numpy.ndarray
        :param sites: The sites where the measurements are not made
                      in the computational basis
        :type sites: numpy.ndarray
        :param sample: The measurement (either 0 or 1)
        :type sample: torch.Tensor
        :returns: A list of two tensors, representing the rotated gradients
                  of the amplitude and phase RBMS
        :rtype: list[torch.Tensor, torch.Tensor]
        """
        UrhoU, v, vp, Us, Us_dag, rotated_grad = self.init_gradient(
            basis, sites)
        int_sample = sample[sites].round().int().cpu().numpy()
        ints_size = np.arange(sites.size)

        U_ = torch.tensor([1.0, 1.0], dtype=torch.double, device=self.device)
        UrhoU = torch.zeros(2, dtype=torch.double, device=self.device)
        UrhoU_ = torch.zeros_like(UrhoU)

        grad_size = (self.num_visible * self.num_hidden +
                     self.num_visible * self.num_aux + self.num_visible +
                     self.num_hidden + self.num_aux)
        Z2 = torch.zeros((2, grad_size),
                         dtype=torch.double,
                         device=self.device)

        v = sample.round().clone()
        vp = sample.round().clone()

        for x in range(2**sites.size):
            v = sample.round().clone()
            v[sites] = self.subspace_vector(x, sites.size)
            int_v = v[sites].int().cpu().numpy()
            all_Us = Us[ints_size, :, int_sample, int_v]

            for y in range(2**sites.size):
                vp = sample.round().clone()
                vp[sites] = self.subspace_vector(y, sites.size)
                int_vp = vp[sites].int().cpu().numpy()
                all_Us_dag = Us[ints_size, :, int_sample, int_vp]

                Ut = np.prod(all_Us[:, 0] + (1j * all_Us[:, 1]))
                Ut *= np.prod(
                    np.conj(all_Us_dag[:, 0] + (1j * all_Us_dag[:, 1])))
                U_[0] = Ut.real
                U_[1] = Ut.imag

                cplx.scalar_mult(U_, self.rhoRBM_tilde(v, vp), out=UrhoU_)
                UrhoU += UrhoU_

                grad0 = self.am_grads(v, vp)
                grad1 = self.ph_grads(v, vp)

                rotated_grad[0] += cplx.scalar_mult(UrhoU_, grad0, out=Z2)
                rotated_grad[1] += cplx.scalar_mult(UrhoU_, grad1, out=Z2)

        grad = [
            cplx.scalar_divide(rotated_grad[0], UrhoU),
            cplx.scalar_divide(rotated_grad[1], UrhoU),
        ]

        return grad
示例#6
0
    def gradient(self, basis, sample):
        r"""Compute the gradient of a set (v_state) of samples, measured
        in different bases
        
        :param basis: A set of basis, (i.e.vector of strings)
        :type basis: np.array
        """
        num_U = 0  # Number of 1-local unitary rotations
        rotated_sites = []  # List of site where the rotations are applied
        grad = []  # Gradient

        # Read where the unitary rotations are applied
        for j in range(self.num_visible):
            if (basis[j] != 'Z'):
                num_U += 1
                rotated_sites.append(j)

        # If the basis is the reference one ('ZZZ..Z')
        if (num_U == 0):
            grad.append(self.rbm_am.effective_energy_gradient(sample))  # Real
            grad.append(0.0)  # Imaginary

        else:
            # Initialize
            vp = torch.zeros(self.num_visible,
                             dtype=torch.double,
                             device=self.device)
            rotated_grad = [
                torch.zeros(2,
                            self.rbm_am.num_pars,
                            dtype=torch.double,
                            device=self.device),
                torch.zeros(2,
                            self.rbm_ph.num_pars,
                            dtype=torch.double,
                            device=self.device)
            ]
            Upsi = torch.zeros(2, dtype=torch.double, device=self.device)

            # Sum over the full subspace where the rotation are applied
            #sub_state = self.generate_visible_space(num_U)
            sub_space = self.generate_Hilbert_space(num_U)
            for x in range(1 << num_U):
                # Create the correct state for the full system (given the data)
                cnt = 0
                for j in range(self.num_visible):
                    if (basis[j] != 'Z'):
                        #vp[j]=sub_state[x][cnt] # This site sums (it is rotated)
                        vp[j] = sub_space[x][cnt]
                        cnt += 1
                    else:
                        vp[j] = sample[j]  # This site is left unchanged

                U = torch.tensor(
                    [1., 0.], dtype=torch.double, device=self.device
                )  #Product of the matrix elements of the unitaries
                for ii in range(num_U):
                    tmp = self.unitary_dict[basis[
                        rotated_sites[ii]]][:,
                                            int(sample[rotated_sites[ii]]),
                                            int(vp[rotated_sites[ii]])]
                    U = cplx.scalar_mult(U, tmp.to(self.device))

                # Gradient on the current configuration
                grad_vp = [
                    self.rbm_am.effective_energy_gradient(vp),
                    self.rbm_ph.effective_energy_gradient(vp)
                ]

                # NN state rotated in this bases
                Upsi_v = cplx.scalar_mult(U, self.psi(vp))

                Upsi += Upsi_v
                rotated_grad[0] += cplx.scalar_mult(
                    Upsi_v,
                    cplx.make_complex(grad_vp[0],
                                      torch.zeros_like(grad_vp[0])))
                rotated_grad[1] += cplx.scalar_mult(
                    Upsi_v,
                    cplx.make_complex(grad_vp[1],
                                      torch.zeros_like(grad_vp[1])))

            grad.append(cplx.scalar_divide(rotated_grad[0], Upsi)[0, :])
            grad.append(-cplx.scalar_divide(rotated_grad[1], Upsi)[1, :])

        return grad