def test_control_matrix(self):
        """Test control matrix for traceless and non-traceless bases"""

        n_opers = testutil.rand_herm(3, 4)
        n_opers_traceless = testutil.rand_herm_traceless(3, 4)

        basis = ff.Basis(testutil.rand_herm(3), traceless=False)
        basis_traceless = ff.Basis(testutil.rand_herm_traceless(3),
                                   traceless=True)

        base_pulse = testutil.rand_pulse_sequence(3, 10, 4, 4)

        omega = np.logspace(-1, 1, 51)

        for i, base in enumerate((basis, basis_traceless)):
            for j, n_ops in enumerate((n_opers, n_opers_traceless)):
                pulse = copy(base_pulse)
                pulse.n_opers = n_ops
                pulse.basis = base

                R = pulse.get_control_matrix(omega)

                if i == 0 and j == 0:
                    # base not traceless, nopers not traceless
                    self.assertTrue((R[:, 0] != 0).all())
                elif i == 0 and j == 1:
                    # base not traceless, nopers traceless
                    self.assertTrue((R[:, 0] != 0).all())
                elif i == 1 and j == 0:
                    # base traceless, nopers not traceless
                    self.assertTrue((R[:, 0] != 0).all())
                elif i == 1 and j == 1:
                    # base traceless, nopers traceless
                    self.assertTrue(np.allclose(R[:, 0], 0))
    def test_basis_properties(self):
        """Basis orthonormal and of correct dimensions"""
        d = rng.randint(2, 17)
        n = rng.randint(1, 5)

        ggm_basis = ff.Basis.ggm(d)
        pauli_basis = ff.Basis.pauli(n)
        custom_basis = ff.Basis(testutil.rand_herm(d), traceless=False)

        btypes = ('Pauli', 'GGM', 'Custom')
        bases = (pauli_basis, ggm_basis, custom_basis)
        for btype, base in zip(btypes, bases):
            base.tidyup(eps_scale=0)
            self.assertTrue(base == base)
            self.assertFalse(base == ff.Basis.ggm(d + 1))
            self.assertEqual(btype, base.btype)
            if not btype == 'Pauli':
                self.assertEqual(d, base.d)
                # Check if __contains__ works as expected
                self.assertTrue(base[rng.randint(0, d**2)] in base)
            else:
                self.assertEqual(2**n, base.d)
                # Check if __contains__ works as expected
                self.assertTrue(base[rng.randint(0, (2**n)**2)] in base)
            # Check if all elements of each basis are orthonormal and hermitian
            self.assertArrayEqual(base.T,
                                  base.view(np.ndarray).swapaxes(-1, -2))
            self.assertTrue(base.isorthonorm)
            self.assertTrue(base.isherm)
            # Check if basis spans the whole space and all elems are traceless
            if not btype == 'Custom':
                self.assertTrue(base.istraceless)
            else:
                self.assertFalse(base.istraceless)

            self.assertTrue(base.iscomplete)
            # Check sparse representation
            self.assertArrayEqual(base.sparse.todense(), base)
            # Test sparse cache
            self.assertArrayEqual(base.sparse.todense(), base)

            if base.d < 8:
                # Test very resource intense
                ref = np.einsum('iab,jbc,kcd,lda', *(base, ) * 4)
                self.assertArrayAlmostEqual(base.four_element_traces.todense(),
                                            ref,
                                            atol=1e-16)

                # Test setter
                base._four_element_traces = None
                base.four_element_traces = ref
                self.assertArrayEqual(base.four_element_traces, ref)

            base._print_checks()

        basis = ff.util.paulis[1].view(ff.Basis)
        self.assertTrue(basis.isorthonorm)
        self.assertArrayEqual(basis.T, basis.view(np.ndarray).T)
Exemple #3
0
    def test_oper_equiv(self):
        with self.assertRaises(ValueError):
            util.oper_equiv(rng.standard_normal((2, 2)),
                            rng.standard_normal((3, 3)))

        for d in rng.randint(2, 10, (5, )):
            psi = rng.standard_normal((d, 1)) + 1j * rng.standard_normal(
                (d, 1))
            # Also test broadcasting
            U = testutil.rand_herm(d, rng.randint(1, 11)).squeeze()
            phase = rng.standard_normal()

            result = util.oper_equiv(psi, psi * np.exp(1j * phase))
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], phase, places=5)

            result = util.oper_equiv(psi * np.exp(1j * phase), psi)
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], -phase, places=5)

            psi /= np.linalg.norm(psi, ord=2)

            result = util.oper_equiv(psi,
                                     psi * np.exp(1j * phase),
                                     normalized=True,
                                     eps=1e-13)
            self.assertTrue(result[0])
            self.assertArrayAlmostEqual(result[1], phase, atol=1e-5)

            result = util.oper_equiv(psi, psi + 1)
            self.assertFalse(result[0])

            result = util.oper_equiv(U, U * np.exp(1j * phase))
            self.assertTrue(np.all(result[0]))
            self.assertArrayAlmostEqual(result[1], phase, atol=1e-5)

            result = util.oper_equiv(U * np.exp(1j * phase), U)
            self.assertTrue(np.all(result[0]))
            self.assertArrayAlmostEqual(result[1], -phase, atol=1e-5)

            norm = np.sqrt(util.dot_HS(U, U))
            norm = norm[:, None, None] if U.ndim == 3 else norm
            U /= norm
            # TIP: In numpy 1.18 we could just do:
            # U /= np.expand_dims(np.sqrt(util.dot_HS(U, U)), axis=(-1, -2))
            result = util.oper_equiv(U,
                                     U * np.exp(1j * phase),
                                     normalized=True,
                                     eps=1e-10)
            self.assertTrue(np.all(result[0]))
            self.assertArrayAlmostEqual(result[1], phase)

            result = util.oper_equiv(U, U + 1)
            self.assertFalse(np.all(result[0]))
    def test_basis_generation_from_partial_random(self):
        """"Generate complete basis from partial elements of a random basis"""
        # Do 25 test runs with random elements from a random basis in
        # (2 ... 8) dimensions
        for _ in range(25):
            d = rng.randint(2, 7)
            # Get a random traceless hermitian operator
            oper = testutil.rand_herm_traceless(d)
            # ... and build a basis from it
            b = ff.Basis(oper)
            self.assertTrue(b.isorthonorm)
            self.assertTrue(b.isherm)
            self.assertTrue(b.istraceless)
            self.assertTrue(b.iscomplete)
            # Choose random elements from that basis and generate a new basis
            # from it
            inds = [i for i in range(d**2)]
            tup = tuple(
                inds.pop(rng.randint(0, len(inds)))
                for _ in range(rng.randint(1, d**2)))
            elems = b[tup, ...]
            basis = ff.Basis(elems)
            self.assertTrue(basis.isorthonorm)
            self.assertTrue(basis.isherm)
            self.assertTrue(basis.istraceless)
            self.assertTrue(basis.iscomplete)
            self.assertTrue(all(elem in basis for elem in elems))

        # Test runs with non-traceless opers
        for _ in range(25):
            d = rng.randint(2, 7)
            # Get a random hermitian operator
            oper = testutil.rand_herm(d)
            # ... and build a basis from it
            b = ff.Basis(oper)
            self.assertTrue(b.isorthonorm)
            self.assertTrue(b.isherm)
            self.assertFalse(b.istraceless)
            self.assertTrue(b.iscomplete)
            # Choose random elements from that basis and generate a new basis
            # from it
            inds = [i for i in range(d**2)]
            tup = tuple(
                inds.pop(rng.randint(0, len(inds)))
                for _ in range(rng.randint(1, d**2)))
            elems = b[tup, ...]
            basis = ff.Basis(elems)
            self.assertTrue(basis.isorthonorm)
            self.assertTrue(basis.isherm)
            self.assertFalse(basis.istraceless)
            self.assertTrue(basis.iscomplete)
            self.assertTrue(all(elem in basis for elem in elems))
    def test_liouville_is_cCP(self):
        for d in rng.integers(2, 9, (15, )):
            # (anti-) Hermitian generator, should always be cCP
            H = 1j * testutil.rand_herm(d, rng.integers(1, 8)).squeeze()
            n = np.log2(d)
            if n % 1 == 0:
                basis = ff.Basis.pauli(int(n))
            else:
                basis = ff.Basis.ggm(d)

            H_sup = (np.einsum('iab,...bc,jca',
                               basis,
                               H,
                               basis,
                               optimize=['einsum_path', (0, 1), (0, 1)]) -
                     np.einsum('iab,jbc,...ca',
                               basis,
                               basis,
                               H,
                               optimize=['einsum_path', (0, 2), (0, 1)]))
            cCP, (D, V) = superoperator.liouville_is_cCP(H_sup, basis, True)
            _cCP = superoperator.liouville_is_cCP(H_sup, basis, False)

            self.assertArrayEqual(cCP, _cCP)
            self.assertTrue(np.all(cCP))
            if H_sup.ndim == 2:
                self.assertIsInstance(cCP, (bool, np.bool8))
            else:
                self.assertEqual(cCP.shape[0], H_sup.shape[0])
            self.assertArrayAlmostEqual(D, 0, atol=1e-14)
            self.assertTrue(
                ff.util.oper_equiv(V,
                                   np.eye(d**2)[None, :, :],
                                   normalized=True))

            pulse = testutil.rand_pulse_sequence(d, 1)
            omega = ff.util.get_sample_frequencies(pulse)
            S = 1 / abs(omega)**2

            K_sup = ff.numeric.calculate_cumulant_function(pulse, S, omega)
            cCP = superoperator.liouville_is_cCP(K_sup,
                                                 pulse.basis,
                                                 False,
                                                 atol=1e-13)

            self.assertTrue(np.all(cCP))
            if K_sup.ndim == 2:
                self.assertIsInstance(cCP, (bool, np.bool8))
            else:
                self.assertEqual(cCP.shape[0], K_sup.shape[0])
    def test_dot_HS(self):
        U, V = rng.randint(0, 100, (2, 2, 2))
        S = util.dot_HS(U, V)
        T = util.dot_HS(U, V, eps=0)
        self.assertArrayEqual(S, T)

        for d in rng.randint(2, 10, (5, )):
            U, V = testutil.rand_herm(d, 2)
            self.assertArrayAlmostEqual(util.dot_HS(U, V),
                                        (U.conj().T @ V).trace())

            U = testutil.rand_unit(d).squeeze()
            self.assertEqual(util.dot_HS(U, U), d)
            self.assertEqual(util.dot_HS(U, U + 1e-14, eps=1e-10), d)
    def test_oper_equiv(self):
        with self.assertRaises(ValueError):
            util.oper_equiv(*[np.ones((1, 2, 3))] * 2)

        for d in rng.randint(2, 10, (5, )):
            psi = rng.standard_normal((d, 1)) + 1j * rng.standard_normal(
                (d, 1))
            U = testutil.rand_herm(d).squeeze()
            phase = rng.standard_normal()

            result = util.oper_equiv(psi, psi * np.exp(1j * phase))
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], phase, places=5)

            result = util.oper_equiv(psi * np.exp(1j * phase), psi)
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], -phase, places=5)

            psi /= np.linalg.norm(psi, ord=2)

            result = util.oper_equiv(psi,
                                     psi * np.exp(1j * phase),
                                     normalized=True,
                                     eps=1e-13)
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], phase, places=5)

            result = util.oper_equiv(psi, psi + 1)
            self.assertFalse(result[0])

            result = util.oper_equiv(U, U * np.exp(1j * phase))
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], phase, places=5)

            result = util.oper_equiv(U * np.exp(1j * phase), U)
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], -phase, places=5)

            U /= np.sqrt(util.dot_HS(U, U))
            result = util.oper_equiv(U,
                                     U * np.exp(1j * phase),
                                     normalized=True,
                                     eps=1e-10)
            self.assertTrue(result[0])
            self.assertAlmostEqual(result[1], phase)

            result = util.oper_equiv(U, U + 1)
            self.assertFalse(result[0])
    def test_basis_constructor(self):
        """Test the constructor for several failure modes"""
        # Constructing from given elements should check for __getitem__
        with self.assertRaises(TypeError):
            _ = ff.Basis(1)

        # All elements should be either sparse, Qobj, or ndarray
        elems = [
            util.paulis[1],
            COO.from_numpy(util.paulis[3]), [[0, 1], [1, 0]]
        ]
        with self.assertRaises(TypeError):
            _ = ff.Basis(elems)

        # Too many elements
        with self.assertRaises(ValueError):
            _ = ff.Basis(rng.standard_normal((5, 2, 2)))

        # Non traceless elems but traceless basis requested
        with self.assertRaises(ValueError):
            _ = ff.Basis.from_partial(np.ones((2, 2)), traceless=True)

        # Incorrect number of labels for default constructor
        with self.assertRaises(ValueError):
            _ = ff.Basis(util.paulis, labels=['a', 'b', 'c'])

        # Incorrect number of labels for from_partial constructor
        with self.assertRaises(ValueError):
            _ = ff.Basis.from_partial(util.paulis[:2], labels=['a', 'b', 'c'])

        # from_partial constructor should move identity label to the front
        basis = ff.Basis.from_partial([util.paulis[1], util.paulis[0]],
                                      labels=['x', 'i'])
        self.assertEqual(basis.labels[:2], ['i', 'x'])
        self.assertEqual(basis.labels[2:], ['$C_{2}$', '$C_{3}$'])

        # from_partial constructor should copy labels if it can
        partial_basis = ff.Basis.pauli(1)[[1, 3]]
        partial_basis.labels = [
            partial_basis.labels[1], partial_basis.labels[3]
        ]
        basis = ff.Basis.from_partial(partial_basis)
        self.assertEqual(basis.labels[:2], partial_basis.labels)
        self.assertEqual(basis.labels[2:], ['$C_{2}$', '$C_{3}$'])

        # Default constructor should return 3d array also for single 2d element
        basis = ff.Basis(rng.standard_normal((2, 2)))
        self.assertEqual(basis.shape, (1, 2, 2))

        # from_partial constructor should return same basis for 2d or 3d input
        elems = testutil.rand_herm(3)
        basis1 = ff.Basis.from_partial(elems, labels=['weif'])
        basis2 = ff.Basis.from_partial(elems.squeeze(), labels=['weif'])
        self.assertEqual(basis1, basis2)

        # Calling with only the identity should work with traceless true or false
        self.assertEqual(ff.Basis(np.eye(2), traceless=False),
                         ff.Basis(np.eye(2), traceless=True))

        # Constructing a basis from a basis should work
        _ = ff.Basis(ff.Basis.ggm(2)[1:])

        # Constructing should not change the elements
        elems = rng.standard_normal((6, 3, 3))
        basis = ff.Basis(elems)
        self.assertArrayEqual(elems, basis)
    def test_basis_expansion_and_normalization(self):
        """Correct expansion of operators and normalization of bases"""
        # dtype
        b = ff.Basis.ggm(3)
        r = ff.basis.expand(rng.standard_normal((3, 3)), b, hermitian=False)
        self.assertTrue(r.dtype == 'complex128')
        r = ff.basis.expand(testutil.rand_herm(3), b, hermitian=True)
        self.assertTrue(r.dtype == 'float64')
        b._isherm = False
        r = ff.basis.expand(testutil.rand_herm(3), b, hermitian=True)
        self.assertTrue(r.dtype == 'complex128')

        r = ff.basis.ggm_expand(testutil.rand_herm(3), hermitian=True)
        self.assertTrue(r.dtype == 'float64')
        r = ff.basis.ggm_expand(rng.standard_normal((3, 3)), hermitian=False)
        self.assertTrue(r.dtype == 'complex128')

        for _ in range(10):
            d = rng.integers(2, 16)
            ggm_basis = ff.Basis.ggm(d)
            basis = ff.Basis(
                np.einsum('i,ijk->ijk', rng.standard_normal(d**2), ggm_basis))
            M = rng.standard_normal((d, d)) + 1j * rng.standard_normal((d, d))
            M -= np.trace(M) / d
            coeffs = ff.basis.expand(M, basis, normalized=False)
            self.assertArrayAlmostEqual(M, np.einsum('i,ijk', coeffs, basis))
            self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis),
                                        ff.basis.ggm_expand(M),
                                        atol=1e-14)
            self.assertArrayAlmostEqual(ff.basis.ggm_expand(M),
                                        ff.basis.ggm_expand(M, traceless=True),
                                        atol=1e-14)

            n = rng.integers(1, 50)
            M = rng.standard_normal((n, d, d)) + 1j * rng.standard_normal(
                (n, d, d))
            coeffs = ff.basis.expand(M, basis, normalized=False)
            self.assertArrayAlmostEqual(
                M, np.einsum('li,ijk->ljk', coeffs, basis))
            self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis),
                                        ff.basis.ggm_expand(M),
                                        atol=1e-14)

            # Argument to ggm_expand not square in last two dimensions
            with self.assertRaises(ValueError):
                ff.basis.ggm_expand(basis[..., 0])

            self.assertTrue(ff.basis.normalize(basis).isorthonorm)

            # Basis method and function should give the same
            normalized = ff.basis.normalize(basis)
            basis.normalize()
            self.assertEqual(normalized, basis)

            # normalize single element
            elem = basis[1]
            normalized = ff.basis.normalize(elem)
            elem.normalize()
            self.assertEqual(normalized, elem)

            # Not matrix or sequence of matrices
            with self.assertRaises(ValueError):
                ff.basis.normalize(basis[0, 0])

            # Test copy
            arr = rng.standard_normal((3, 2, 2))
            basis = ff.Basis(arr)
            normalized = basis.normalize(copy=True)
            self.assertIsNot(normalized, basis)
            self.assertFalse(np.array_equal(normalized, basis))
            basis.normalize()
            self.assertArrayEqual(normalized, basis)