Example #1
0
    def post_select_generaldyne(self, covmat, modes, vals):
        r"""Simulates a general-dyne measurement on a set of modes with a specified measurement
        outcome.

        Args:
            covmat (array): covariance matrix of the generaldyne measurement
            modes (list): modes to be measured
            vals (array): measurement outcome to postselect

        Raises:
            ValueError: if the dimension of covmat does not match the number of quadratures
                        associated with modes
            ValueError: if any of the modes are not in the list of active modes
        """
        if covmat.shape != (2 * len(modes), 2 * len(modes)):
            raise ValueError(
                "The size of the covariance matrix does not match the indices provided."
            )

        for i in modes:
            if self.active[i] is None:
                raise ValueError(
                    "Cannot apply measurement, mode does not exist")

        expind = np.concatenate((2 * np.array(modes), 2 * np.array(modes) + 1))
        mp = self.get_covmat()
        A, B, C = ops.chop_in_blocks_multi(mp, expind)
        V = A - B @ np.linalg.inv(C + covmat) @ B.transpose(0, 2, 1)
        self.covs = ops.reassemble_multi(V, expind)

        r = self.get_mean()
        (va, vc) = ops.chop_in_blocks_vector_multi(r, expind)
        va = va + np.einsum("...ij,...j", B @ np.linalg.inv(C + covmat),
                            (vals - vc))
        self.means = ops.reassemble_vector_multi(va, expind)

        # Reweight each peak based on how likely a given peak was to have
        # contributed to the observed outcome
        reweights_exp_arg = np.einsum("...j,...jk,...k", (vals - vc),
                                      np.linalg.inv(C + covmat), (vals - vc))
        reweights = np.exp(-0.5 * reweights_exp_arg) / (np.sqrt(
            np.linalg.det(2 * np.pi * (C + covmat))))
        self.weights *= reweights
        self.weights /= np.sum(self.weights)

        self.means = self.means[abs(self.weights) > 0]
        self.covs = self.covs[abs(self.weights) > 0]
        self.weights = self.weights[abs(self.weights) > 0]
    def test_reassemble_multi(self, id_to_delete):
        r"""Checks that ops.reassemble_multi generates the correct output"""

        # Create matrix
        A = np.random.rand(2, 2)
        reps = np.random.randint(1, 10)
        Atile = np.tile(A, [reps, 1, 1])
        # Create indices
        m = ops.reassemble_multi(Atile, id_to_delete)
        dim = len(A) + len(id_to_delete)
        id_to_keep = list(set(range(dim)) - set(id_to_delete))
        id_to_keep.sort()
        assert m.shape == (reps, dim, dim)

        A2, B2, C2 = ops.chop_in_blocks_multi(m, id_to_keep)
        assert np.allclose(C2, Atile)
        assert np.allclose(B2, 0)
        assert np.allclose(A2, np.tile(np.eye(len(id_to_delete)), [reps, 1, 1]))
    def test_chop_in_blocks_multi(self, reps):
        r"""Checks that ops.chop_in_block_multi partitions arrays of matrices correctly"""
        # Create submatrices
        A = np.random.rand(2, 2)
        B = np.random.rand(2, 3)
        C = np.random.rand(3, 3)

        # Repeat them in an array
        Atile = np.tile(A, [reps, 1, 1])
        Btile = np.tile(B, [reps, 1, 1])
        Ctile = np.tile(C, [reps, 1, 1])

        # Make a new block matrix out of them and repeat it
        m = np.block([[A, B], [B.T, C]])
        m = np.tile(m, [reps, 1, 1])

        # Choose to delete the indices corresponding to C
        id_to_delete = np.arange(2, 5, dtype=int)

        A2, B2, C2 = ops.chop_in_blocks_multi(m, id_to_delete)

        assert np.allclose(A2, Atile)
        assert np.allclose(B2, Btile)
        assert np.allclose(C2, Ctile)
    def measure_threshold(self, modes):
        r"""Performs photon number measurement on the given modes"""
        if len(modes) == 1:
            if self.active[modes[0]] is None:
                raise ValueError(
                    "Cannot apply measurement, mode does not exist")

            Idmat = self.hbar * np.eye(2) / 2
            vacuum_fidelity = np.abs(self.fidelity_vacuum(modes))
            measurement = np.random.choice(
                (0, 1), p=[vacuum_fidelity, 1 - vacuum_fidelity])
            samples = measurement

            # If there are no more modes to measure simply set everything to vacuum
            if len(modes) == len(self.active):
                for mode in modes:
                    self.loss(0, mode)
            # If there are other active modes simply update based on measurement
            else:
                mode_ind = np.concatenate(
                    (2 * np.array(modes), 2 * np.array(modes) + 1))
                sigma_A, sigma_AB, sigma_B = ops.chop_in_blocks_multi(
                    self.covs, mode_ind)
                sigma_A_prime = sigma_A - sigma_AB @ np.linalg.inv(
                    sigma_B + Idmat) @ sigma_AB.transpose(0, 2, 1)
                r_A, r_B = ops.chop_in_blocks_vector_multi(
                    self.means, mode_ind)
                r_A_prime = r_A - np.einsum(
                    "...ij,...j", sigma_AB @ np.linalg.inv(sigma_B + Idmat),
                    r_B)

                reweights_exp_arg = np.einsum("...j,...jk,...k", -r_B,
                                              np.linalg.inv(sigma_B + Idmat),
                                              -r_B)
                reweights = np.exp(-0.5 * reweights_exp_arg) / (np.sqrt(
                    np.linalg.det(2 * np.pi * (sigma_B + Idmat))))

                if measurement == 1:
                    self.means = np.append(
                        ops.reassemble_vector_multi(r_A, mode_ind),
                        ops.reassemble_vector_multi(r_A_prime, mode_ind),
                        axis=0,
                    )
                    self.covs = np.append(
                        ops.reassemble_multi(sigma_A, mode_ind),
                        ops.reassemble_multi(sigma_A_prime, mode_ind),
                        axis=0,
                    )
                    self.weights = np.append(
                        self.weights / (1 - vacuum_fidelity),
                        self.weights * (reweights * 2 * np.pi * self.hbar /
                                        (vacuum_fidelity - 1)),
                        axis=0,
                    )
                else:
                    self.post_select_heterodyne(modes[0], 0)
            self.loss(0, modes[0])
            return samples

        raise ValueError(
            "Measure Threshold can only be applied to one mode at a time")