예제 #1
0
    def test_liouville_to_choi(self):
        """Test converting Liouville superops to choi matrices."""
        for d in rng.randint(2, 9, (15, )):
            # unitary channel
            U = testutil.rand_unit(d, rng.randint(1, 8)).squeeze()
            n = np.log2(d)
            if n % 1 == 0:
                basis = ff.Basis.pauli(int(n))
            else:
                basis = ff.Basis.ggm(d)

            U_sup = superoperator.liouville_representation(U, basis)
            choi = superoperator.liouville_to_choi(U_sup, basis).view(ff.Basis)

            self.assertTrue(choi.isherm)
            self.assertArrayAlmostEqual(np.einsum('...ii', choi), d)

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

            U_sup = ff.error_transfer_matrix(pulse, S, omega)
            choi = superoperator.liouville_to_choi(U_sup, basis).view(ff.Basis)

            self.assertTrue(choi.isherm)
            self.assertAlmostEqual(np.einsum('ii', choi), d)
예제 #2
0
    def test_basis_generation_from_partial_pauli(self):
        """"Generate complete basis from partial elements of a Pauli basis"""
        # Do 100 test runs with random elements from a Pauli basis in (2 ... 8)
        # dimensions
        for _ in range(50):
            n = rng.randint(1, 4)
            d = 2**n
            b = ff.Basis.pauli(n)
            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))

            with self.assertWarns(UserWarning):
                b = [basis[0], 1j * basis[1]]
                ff.Basis(b)

            with self.assertRaises(ValueError):
                b = [basis[0], basis[0] + basis[1]]
                ff.Basis(b)
예제 #3
0
    def test_multi_qubit_error_transfer_matrix(self):
        """Test the calculation of the multi-qubit transfer matrix"""
        n_cops = 4
        n_nops = 2
        for d, n_dt in zip(rng.randint(3, 9, 10), rng.randint(1, 11, 10)):
            f, n = np.modf(np.log2(d))
            btype = 'Pauli' if f == 0.0 else 'GGM'
            pulse = testutil.rand_pulse_sequence(d, n_dt, n_cops, n_nops,
                                                 btype)
            omega = util.get_sample_frequencies(pulse, n_samples=51)

            # Assert fidelity is same as computed by infidelity()
            S = 1e-8 / omega**2
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = 1 - np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(Up, U)
            self.assertArrayAlmostEqual(I_transfer,
                                        I_fidelity.sum(),
                                        atol=1e-4)

            S = np.outer(1e-7 * (np.arange(n_nops) + 1),
                         400 / (omega**2 + 400))
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = 1 - np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(Up, U)
            self.assertArrayAlmostEqual(I_transfer,
                                        I_fidelity.sum(),
                                        atol=1e-4)

            S = np.tile(1e-8 / abs(omega)**2,
                        (n_nops, n_nops, 1)).astype(complex)
            S[np.triu_indices(n_nops, 1)].imag = 1e-10 * omega
            S[np.tril_indices(n_nops, -1)].imag = \
                - S[np.triu_indices(n_nops, 1)].imag
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = 1 - np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(Up, U)
            self.assertArrayAlmostEqual(I_transfer,
                                        I_fidelity.sum(),
                                        atol=1e-4)
예제 #4
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)
예제 #5
0
    def test_tensor_transpose(self):
        # Test basic functionality
        paulis = np.array(util.paulis)
        I, X, Y, Z = paulis
        arr = util.tensor(I, X, Y, Z)
        arr_dims = [[2] * 4] * 2
        order = np.arange(4)

        for _ in range(20):
            order = rng.permutation(order)
            r = util.tensor_transpose(arr, order, arr_dims)
            self.assertArrayAlmostEqual(r, util.tensor(*paulis[order]))

        # Check exceptions
        with self.assertRaises(ValueError):
            # wrong arr_dims (too few dims)
            r = util.tensor_transpose(arr, order, [[2] * 3] * 2)

        with self.assertRaises(ValueError):
            # wrong arr_dims (too few dims)
            r = util.tensor_transpose(arr, order, [[2] * 4] * 1)

        with self.assertRaises(ValueError):
            # wrong arr_dims (dims too large)
            r = util.tensor_transpose(arr, order, [[3] * 4] * 2)

        with self.assertRaises(ValueError):
            # wrong order (too few axes)
            r = util.tensor_transpose(arr, (0, 1, 2), arr_dims)

        with self.assertRaises(ValueError):
            # wrong order (index 4 too large)
            r = util.tensor_transpose(arr, (1, 2, 3, 4), arr_dims)

        with self.assertRaises(ValueError):
            # wrong order (not unique axes)
            r = util.tensor_transpose(arr, (1, 1, 1, 1), arr_dims)

        with self.assertRaises(TypeError):
            # wrong order (floats instead of ints)
            r = util.tensor_transpose(arr, (0., 1., 2., 3.), arr_dims)

        # random tests
        for rank, n_args, n_broadcast in zip(rng.randint(1, 4, 10),
                                             rng.randint(3, 6, 10),
                                             rng.randint(1, 11, 10)):
            arrs = rng.standard_normal((n_args, n_broadcast, *[2] * rank))
            order = rng.permutation(n_args)
            arr_dims = [[2] * n_args] * rank

            r = util.tensor_transpose(util.tensor(*arrs, rank=rank),
                                      order=order,
                                      arr_dims=arr_dims,
                                      rank=rank)
            self.assertArrayAlmostEqual(r, util.tensor(*arrs[order],
                                                       rank=rank))
예제 #6
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]))
예제 #7
0
    def test_tensor(self):
        shapes = [(1, 2, 3, 4, 5), (5, 4, 3, 2, 1)]
        A = rng.standard_normal(shapes[0])
        B = rng.standard_normal(shapes[1])
        with self.assertRaises(ValueError):
            util.tensor(A, B)

        shapes = [(3, 2, 1), (3, 4, 2)]
        A = rng.standard_normal(shapes[0])
        B = rng.standard_normal(shapes[1])
        with self.assertRaises(ValueError):
            util.tensor(A, B, rank=1)

        self.assertEqual(util.tensor(A, B, rank=2).shape, (3, 8, 2))
        self.assertEqual(util.tensor(A, B, rank=3).shape, (9, 8, 2))

        shapes = [(10, 1, 3, 2), (10, 1, 2, 3)]
        A = rng.standard_normal(shapes[0])
        B = rng.standard_normal(shapes[1])
        self.assertEqual(util.tensor(A, B).shape, (10, 1, 6, 6))

        shapes = [(3, 5, 4, 4), (3, 5, 4, 4)]
        A = rng.standard_normal(shapes[0])
        B = rng.standard_normal(shapes[1])
        self.assertEqual(util.tensor(A, B).shape, (3, 5, 16, 16))

        d = rng.randint(2, 9)
        eye = np.eye(d)
        for i in range(d):
            for j in range(d):
                A, B = eye[i:i + 1, :], eye[:, j:j + 1]
                self.assertArrayEqual(util.tensor(A, B, rank=1), np.kron(A, B))

        i, j = rng.randint(0, 4, (2, ))
        A, B = util.paulis[i], util.paulis[j]
        self.assertArrayEqual(util.tensor(A, B), np.kron(A, B))

        args = [
            rng.standard_normal((4, 1, 2)),
            rng.standard_normal((3, 2)),
            rng.standard_normal((4, 3, 5))
        ]
        self.assertEqual(util.tensor(*args, rank=1).shape, (4, 3, 20))
        self.assertEqual(util.tensor(*args, rank=2).shape, (4, 9, 20))
        self.assertEqual(util.tensor(*args, rank=3).shape, (16, 9, 20))

        args = [rng.standard_normal((2, 3, 4)), rng.standard_normal((4, 3))]
        with self.assertRaises(ValueError) as err:
            util.tensor(*args, rank=1)

        msg = ('Incompatible shapes (2, 3, 4) and (4, 3) for tensor ' +
               'product of rank 1.')
        self.assertEqual(msg, str(err.exception))
예제 #8
0
    def test_multi_qubit_error_transfer_matrix(self):
        """Test the calculation of the multi-qubit transfer matrix"""
        n_cops = 4
        n_nops = 2
        for d, n_dt in zip(rng.randint(3, 9, 10), rng.randint(1, 11, 10)):
            f, n = np.modf(np.log2(d))
            btype = 'Pauli' if f == 0.0 else 'GGM'
            pulse = testutil.rand_pulse_sequence(d, n_dt, n_cops, n_nops,
                                                 btype)
            omega = ff.util.get_sample_frequencies(pulse, n_samples=51)

            # Assert fidelity is same as computed by infidelity()
            S = 1e-2 / omega**2
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            self.assertArrayAlmostEqual(Up, U)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(I_transfer, I_fidelity)

            S = np.outer(1e-2 * (np.arange(n_nops) + 1),
                         400 / (omega**2 + 400))
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            self.assertArrayAlmostEqual(Up, U)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(I_transfer, I_fidelity)

            S = np.einsum('i,j,o->ijo', 1e-2 * (np.arange(n_nops) + 1),
                          1e-2 * (np.arange(n_nops) + 1),
                          400 / (omega**2 + 400))
            U = ff.error_transfer_matrix(pulse, S, omega)
            # Calculate U in loop
            Up = ff.error_transfer_matrix(pulse,
                                          S,
                                          omega,
                                          memory_parsimonious=True)
            self.assertArrayAlmostEqual(Up, U)
            I_fidelity = ff.infidelity(pulse, S, omega)
            I_transfer = np.einsum('...ii', U) / d**2
            self.assertArrayAlmostEqual(I_transfer, I_fidelity)
예제 #9
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)
예제 #10
0
    def test_liouville_is_cCP(self):
        for d in rng.randint(2, 9, (15, )):
            # (anti-) Hermitian generator, should always be cCP
            H = 1j * testutil.rand_herm(d, rng.randint(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])
예제 #11
0
    def test_basis_expansion_and_normalization(self):
        """Correct expansion of operators and normalization of bases"""
        for _ in range(10):
            d = rng.randint(2, 16)
            ggm_basis = ff.Basis.ggm(d)
            basis = ff.Basis(np.einsum('i,ijk->ijk', rng.standard_normal(d**2),
                                       ggm_basis),
                             skip_check=True)
            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.randint(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])
예제 #12
0
    def test_plot_pulse_correlation_filter_function(self):
        omega = np.linspace(-1, 1, 50)
        concatenated_simple_pulse = ff.concatenate(
            (simple_pulse, simple_pulse),
            calc_pulse_correlation_FF=True,
            omega=omega)
        concatenated_complicated_pulse = ff.concatenate(
            (complicated_pulse, complicated_pulse),
            calc_pulse_correlation_FF=True,
            omega=omega)

        # Exception if not pulse correl. FF is cached
        with self.assertRaises(ff.util.CalculationError):
            plotting.plot_pulse_correlation_filter_function(simple_pulse)

        # Call with default args
        fig, ax, leg = plotting.plot_pulse_correlation_filter_function(
            concatenated_simple_pulse)

        # Non-default args
        n_oper_identifiers = sample(
            complicated_pulse.n_oper_identifiers.tolist(), rng.randint(2, 4))

        fig, ax = plt.subplots()
        omega = np.linspace(-10, 10, 50)
        fig, ax, leg = plotting.plot_pulse_correlation_filter_function(
            concatenated_complicated_pulse,
            omega=omega,
            n_oper_identifiers=n_oper_identifiers,
            fig=fig,
            omega_in_units_of_tau=False)

        # invalid identifiers
        with self.assertRaises(ValueError):
            plotting.plot_pulse_correlation_filter_function(
                concatenated_complicated_pulse,
                n_oper_identifiers=['foo'],
                fig=fig,
                axes=ax)

        # Test different axis scales
        scales = ('linear', 'log')
        for xscale in scales:
            for yscale in scales:
                fig, ax, leg = plotting.plot_pulse_correlation_filter_function(
                    concatenated_simple_pulse, xscale=xscale, yscale=yscale)

        # Test various keyword args for matplotlib
        plot_kw = {'linewidth': 1}
        subplot_kw = {'facecolor': 'r'}
        gridspec_kw = {'hspace': 0.2, 'wspace': 0.1}
        figure_kw = {'num': 1}
        fig, ax, leg = plotting.plot_pulse_correlation_filter_function(
            concatenated_simple_pulse,
            plot_kw=plot_kw,
            subplot_kw=subplot_kw,
            gridspec_kw=gridspec_kw,
            **figure_kw)

        plt.close('all')
예제 #13
0
    def test_calculation_with_unitaries(self):
        for d, G in zip(rng.randint(2, 9, (11, )), rng.randint(2, 11, (11, ))):
            pulses = [
                testutil.rand_pulse_sequence(d, rng.randint(1, 11), 2, 4)
                for g in range(G)
            ]

            for pulse in pulses:
                # All pulses should have same n_opers for xxx_from_atomic
                pulse.n_opers = pulses[0].n_opers
                pulse.n_oper_identifiers = pulses[0].n_oper_identifiers
                if rng.randint(0, 2):
                    pulse.t = None

            omega = rng.rand(17)
            B_atomic = np.array([
                numeric.calculate_noise_operators_from_scratch(
                    pulse.eigvals, pulse.eigvecs, pulse.propagators, omega,
                    pulse.n_opers, pulse.n_coeffs, pulse.dt, pulse.t)
                for pulse in pulses
            ])

            R_atomic = np.array([
                numeric.calculate_control_matrix_from_scratch(
                    pulse.eigvals, pulse.eigvecs, pulse.propagators, omega,
                    pulse.basis, pulse.n_opers, pulse.n_coeffs, pulse.dt,
                    pulse.t) for pulse in pulses
            ])

            self.assertArrayAlmostEqual(ff.basis.expand(B_atomic, pulse.basis),
                                        R_atomic.transpose(0, 3, 1, 2),
                                        atol=1e-14)

            B = numeric.calculate_noise_operators_from_atomic(
                np.array([pulse.get_total_phases(omega)
                          for pulse in pulses]), B_atomic,
                np.array([pulse.total_propagator for pulse in pulses]))

            R = numeric.calculate_control_matrix_from_atomic(
                np.array([pulse.get_total_phases(omega) for pulse in pulses]),
                R_atomic,
                np.array(
                    [pulse.total_propagator_liouville for pulse in pulses]))

            self.assertArrayAlmostEqual(ff.basis.expand(B, pulse.basis),
                                        R.transpose(2, 0, 1),
                                        atol=1e-14)
예제 #14
0
 def test_basis_generation_from_partial_ggm(self):
     """"Generate complete basis from partial elements of a GGM basis"""
     # Do 100 test runs with random elements from a GGM basis in (2 ... 8)
     # dimensions
     for _ in range(50):
         d = rng.randint(2, 9)
         b = ff.Basis.ggm(d)
         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))
예제 #15
0
    def test_liouville_is_CP(self):
        def partial_transpose(A):
            d = A.shape[-1]
            sqd = int(np.sqrt(d))
            return A.reshape(-1, sqd, sqd, sqd,
                             sqd).swapaxes(-1, -3).reshape(A.shape)

        # Partial transpose map should be non-CP
        basis = ff.Basis.pauli(2)
        Phi = ff.basis.expand(partial_transpose(basis), basis).T
        CP = superoperator.liouville_is_CP(Phi, basis)
        self.assertFalse(CP)

        for d in rng.randint(2, 9, (15, )):
            # unitary channel
            U = testutil.rand_unit(d, rng.randint(1, 8)).squeeze()
            n = np.log2(d)
            if n % 1 == 0:
                basis = ff.Basis.pauli(int(n))
            else:
                basis = ff.Basis.ggm(d)

            U_sup = superoperator.liouville_representation(U, basis)
            CP, (D, V) = superoperator.liouville_is_CP(U_sup, basis, True)
            _CP = superoperator.liouville_is_CP(U_sup, basis, False)

            self.assertArrayEqual(CP, _CP)
            self.assertTrue(np.all(CP))
            if U_sup.ndim == 2:
                self.assertIsInstance(CP, (bool, np.bool8))
            else:
                self.assertEqual(CP.shape[0], U_sup.shape[0])
            # Only one nonzero eigenvalue
            self.assertArrayAlmostEqual(D[..., :-1], 0, atol=basis._atol)

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

            U_sup = ff.error_transfer_matrix(pulse, S, omega)
            CP = superoperator.liouville_is_CP(U_sup, pulse.basis)

            self.assertTrue(np.all(CP))
            self.assertIsInstance(CP, (bool, np.bool8))
예제 #16
0
 def test_remove_float_errors(self):
     for eps_scale in (None, 2):
         scale = eps_scale or 1
         for dtype in (float, complex):
             arr = np.zeros((10, 10), dtype=dtype)
             arr += scale*np.finfo(arr.dtype).eps *\
                 rng.random_sample(arr.shape)
             arr[rng.randint(0, 2, arr.shape, dtype=bool)] *= -1
             arr = util.remove_float_errors(arr, eps_scale)
             self.assertArrayEqual(arr, np.zeros(arr.shape, dtype=dtype))
예제 #17
0
    def test_plot_filter_function(self):
        # Call with default args
        simple_pulse.cleanup('all')
        fig, ax, leg = plotting.plot_filter_function(simple_pulse)

        # Call with no axes but figure
        fig = plt.figure()
        fig, ax, leg = plotting.plot_filter_function(simple_pulse, fig=fig)

        # Call with axes but no figure
        fig, ax, leg = plotting.plot_filter_function(simple_pulse, axes=ax)

        # Non-default args
        n_oper_identifiers = sample(
            complicated_pulse.n_oper_identifiers.tolist(), rng.randint(2, 4))

        fig, ax = plt.subplots()
        omega = ff.util.get_sample_frequencies(complicated_pulse,
                                               n_samples=50,
                                               spacing='log')
        fig, ax, leg = plotting.plot_filter_function(
            complicated_pulse,
            omega=omega,
            n_oper_identifiers=n_oper_identifiers,
            fig=fig,
            axes=ax,
            omega_in_units_of_tau=False)

        # invalid identifier
        with self.assertRaises(ValueError):
            plotting.plot_filter_function(complicated_pulse,
                                          n_oper_identifiers=['foo'],
                                          fig=fig,
                                          axes=ax)

        # Test different axis scales
        scales = ('linear', 'log')
        for xscale in scales:
            for yscale in scales:
                fig, ax, leg = plotting.plot_filter_function(simple_pulse,
                                                             xscale=xscale,
                                                             yscale=yscale)

        # Test various keyword args for matplotlib
        plot_kw = {'linewidth': 1}
        subplot_kw = {'facecolor': 'r'}
        gridspec_kw = {'hspace': 0.2, 'wspace': 0.1}
        figure_kw = {'num': 1}
        fig, ax, leg = plotting.plot_filter_function(simple_pulse,
                                                     plot_kw=plot_kw,
                                                     subplot_kw=subplot_kw,
                                                     gridspec_kw=gridspec_kw,
                                                     **figure_kw)

        plt.close('all')
예제 #18
0
    def test_all_array_equal(self):
        for n in rng.randint(2, 10, (10, )):
            gen = (np.ones((10, 10)) for _ in range(n))
            lst = [np.ones((10, 10)) for _ in range(n)]
            self.assertTrue(util.all_array_equal(gen))
            self.assertTrue(util.all_array_equal(lst))

            gen = (np.arange(9).reshape(3, 3) + i for i in range(n))
            lst = [np.arange(9).reshape(3, 3) + i for i in range(n)]
            self.assertFalse(util.all_array_equal(gen))
            self.assertFalse(util.all_array_equal(lst))
예제 #19
0
    def test_concatenate_4_spin_echos(self):
        """Concatenate four Spin Echos with a random one having a filter
        function
        """
        tau = 1
        tau_pi = 1e-4
        omega = np.logspace(-2, 1, 200)
        n = 1

        H_c_SE, dt_SE = testutil.generate_dd_hamiltonian(n, tau=tau,
                                                         tau_pi=tau_pi,
                                                         dd_type='cpmg')

        H_n_SE = [[util.paulis[3], np.ones_like(dt_SE)]]
        SE = [ff.PulseSequence(H_c_SE, H_n_SE, dt_SE) for _ in range(4)]

        H_c_CPMG, dt_CPMG = testutil.generate_dd_hamiltonian(4*n, tau=4*tau,
                                                             tau_pi=tau_pi,
                                                             dd_type='cpmg')

        H_n_CPMG = [[util.paulis[3], np.ones_like(dt_CPMG)]]
        CPMG = ff.PulseSequence(H_c_CPMG, H_n_CPMG, dt_CPMG)

        SE[rng.randint(0, len(SE)-1)].cache_filter_function(omega)
        CPMG.cache_filter_function(omega)

        CPMG_concat_1 = ff.concatenate(SE)
        # Clean up so that we start from one SE with cached filter_function again
        for se in SE:
            se.cleanup('all')

        SE[rng.randint(0, len(SE)-1)].cache_filter_function(omega)
        CPMG_concat_2 = SE[0] @ SE[1] @ SE[2] @ SE[3]

        self.assertEqual(CPMG, CPMG_concat_1)
        self.assertEqual(CPMG, CPMG_concat_2)
        self.assertArrayAlmostEqual(CPMG_concat_1._filter_function,
                                    CPMG._filter_function, rtol=1e-10)
        self.assertArrayAlmostEqual(CPMG_concat_2._filter_function,
                                    CPMG._filter_function, rtol=1e-10)
예제 #20
0
    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])
예제 #21
0
    def test_dot_HS(self):
        for d in rng.randint(2, 10, (5, )):
            U = qutip.rand_herm(d)
            V = qutip.rand_herm(d)
            self.assertArrayAlmostEqual(util.dot_HS(U, V), (U.dag() * V).tr())

            U = qutip.rand_unitary(d)
            self.assertEqual(util.dot_HS(U, U), d)

            self.assertEqual(util.dot_HS(U, U + 1e-14, eps=1e-10), d)
            self.assertArrayAlmostEqual(util.dot_HS(U, V), (U.dag() * V).tr())

            U = qutip.rand_unitary(d)
            self.assertEqual(util.dot_HS(U, U), d)

            self.assertEqual(util.dot_HS(U, U + 1e-14, eps=1e-10), d)
예제 #22
0
    def test_infidelity_convergence(self):
        omega = {
            'omega_IR': 0,
            'omega_UV': 2,
            'spacing': 'linear',
            'n_min': 10,
            'n_max': 50,
            'n_points': 4
        }

        def spectrum(omega):
            return omega**0

        simple_pulse = testutil.rand_pulse_sequence(2, 1, 1, 1)
        complicated_pulse = testutil.rand_pulse_sequence(2, 100, 3, 3)

        with self.assertRaises(TypeError):
            n, infids = ff.infidelity(simple_pulse,
                                      spectrum, [],
                                      test_convergence=True)

        with self.assertRaises(TypeError):
            n, infids = ff.infidelity(simple_pulse, [1, 2, 3],
                                      dict(spacing='foobar'),
                                      test_convergence=True)

        with self.assertRaises(ValueError):
            n, infids = ff.infidelity(simple_pulse,
                                      spectrum,
                                      dict(spacing='foobar'),
                                      test_convergence=True)

        # Test with default args
        n, infids = ff.infidelity(simple_pulse,
                                  spectrum, {},
                                  test_convergence=True)

        # Test with non-default args
        identifiers = rng.choice(complicated_pulse.n_oper_identifiers,
                                 rng.randint(1, 4))

        n, infids = ff.infidelity(complicated_pulse,
                                  spectrum,
                                  omega,
                                  test_convergence=True,
                                  n_oper_identifiers=identifiers)
예제 #23
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))
예제 #24
0
    def test_plot_pulse_train(self):
        # Call with default args
        fig, ax, leg = plotting.plot_pulse_train(simple_pulse)

        # Call with no axes but figure
        fig = plt.figure()
        fig, ax, leg = plotting.plot_pulse_train(simple_pulse, fig=fig)

        # Call with axes but no figure
        fig, ax, leg = plotting.plot_pulse_train(simple_pulse, axes=ax)

        # Call with custom args
        c_oper_identifiers = sample(
            complicated_pulse.c_oper_identifiers.tolist(), rng.randint(2, 4))

        fig, ax = plt.subplots()
        fig, ax, leg = plotting.plot_pulse_train(complicated_pulse,
                                                 c_oper_identifiers,
                                                 fig=fig,
                                                 axes=ax)

        # invalid identifier
        with self.assertRaises(ValueError):
            plotting.plot_pulse_train(complicated_pulse,
                                      c_oper_identifiers=['foo'],
                                      fig=fig,
                                      axes=ax)

        # Test various keyword args for matplotlib
        plot_kw = {'linewidth': 1}
        subplot_kw = {'facecolor': 'r'}
        gridspec_kw = {'hspace': 0.2, 'wspace': 0.1}
        figure_kw = {'num': 1}

        fig, ax, leg = plotting.plot_pulse_train(simple_pulse,
                                                 plot_kw=plot_kw,
                                                 subplot_kw=subplot_kw,
                                                 gridspec_kw=gridspec_kw,
                                                 **figure_kw)

        plt.close('all')
예제 #25
0
    def test_oper_equiv(self):
        self.assertFalse(
            util.oper_equiv(qutip.rand_ket(2), qutip.rand_dm(2))[0])

        for d in rng.randint(2, 10, (5, )):
            psi = qutip.rand_ket(d)
            U = qutip.rand_dm(d)
            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)

            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)
예제 #26
0
    def test_pulse_sequence_constructor(self):
        """Test constructing a PulseSequence."""
        base_pulse = testutil.rand_pulse_sequence(2, 5, 3, 3)
        H_c = list(zip(base_pulse.c_opers, base_pulse.c_coeffs,
                       base_pulse.c_oper_identifiers))
        H_n = list(zip(base_pulse.n_opers, base_pulse.n_coeffs,
                       base_pulse.n_oper_identifiers))
        dt = base_pulse.dt

        for i in range(3):
            H_c[i] = list(H_c[i])
            H_n[i] = list(H_n[i])

        with self.assertRaises(TypeError):
            # Not enough positional arguments
            ff.PulseSequence(H_c, H_n)

        with self.assertRaises(TypeError):
            # dt not a sequence
            ff.PulseSequence(H_c, H_n, dt[0])

        idx = rng.randint(0, 5)
        with self.assertRaises(ValueError):
            # negative dt
            dt[idx] *= -1
            ff.PulseSequence(H_c, H_n, dt)

        dt[idx] *= -1
        with self.assertRaises(ValueError):
            # imaginary dt
            dt = dt.astype(complex)
            dt[idx] += 1j
            ff.PulseSequence(H_c, H_n, dt)

        dt = dt.real
        basis = ff.Basis.pauli(1)
        with self.assertRaises(ValueError):
            # basis not Basis instance
            ff.PulseSequence(H_c, H_n, dt, basis.view(np.ndarray))

        with self.assertRaises(ValueError):
            # basis not square
            ff.PulseSequence(H_c, H_n, dt, basis.reshape(4, 1, 4))

        with self.assertRaises(TypeError):
            # Control Hamiltonian not list or tuple
            ff.PulseSequence(np.array(H_c, dtype=object), H_n, dt)

        with self.assertRaises(TypeError):
            # Noise Hamiltonian not list or tuple
            ff.PulseSequence(H_c, np.array(H_n, dtype=object), dt)

        with self.assertRaises(TypeError):
            # Element of control Hamiltonian not list or tuple
            ff.PulseSequence([np.array(H_c[0], dtype=object)], H_n, dt)

        with self.assertRaises(TypeError):
            # Element of noise Hamiltonian not list or tuple
            ff.PulseSequence(H_c, [np.array(H_n[0], dtype=object)], dt)

        idx = rng.randint(0, 3)
        with self.assertRaises(TypeError):
            # Control Hamiltonian element not list or tuple
            H_c[idx] = dict(H_c[idx])
            ff.PulseSequence(H_c, H_n, dt)

        H_c[idx] = list(H_c[idx])
        with self.assertRaises(TypeError):
            # Noise Hamiltonian element not list or tuple
            H_n[idx] = dict(H_n[idx])
            ff.PulseSequence(H_c, H_n, dt)

        H_n[idx] = list(H_n[idx])
        with self.assertRaises(TypeError):
            # Control operators wrong type
            oper = H_c[idx][0].copy()
            H_c[idx][0] = dict(H_c[idx][0])
            ff.PulseSequence(H_c, H_n, dt)

        H_c[idx][0] = oper
        with self.assertRaises(TypeError):
            # Noise operators wrong type
            oper = H_n[idx][0].copy()
            H_n[idx][0] = dict(H_n[idx][0])
            ff.PulseSequence(H_c, H_n, dt)

        H_n[idx][0] = oper
        with self.assertRaises(TypeError):
            # Control coefficients wrong type
            coeff = H_c[idx][1].copy()
            H_c[idx][1] = H_c[idx][1][0]
            ff.PulseSequence(H_c, H_n, dt)

        H_c[idx][1] = coeff
        with self.assertRaises(TypeError):
            # Noise coefficients wrong type
            coeff = H_n[idx][1].copy()
            H_n[idx][1] = H_n[idx][1][0]
            ff.PulseSequence(H_c, H_n, dt)

        H_n[idx][1] = coeff
        with self.assertRaises(ValueError):
            # Control operators not 2d
            for hc in H_c:
                hc[0] = np.tile(hc[0], (rng.randint(2, 11), 1, 1))
            ff.PulseSequence(H_c, H_n, dt)

        for hc in H_c:
            hc[0] = hc[0][0]
        with self.assertRaises(ValueError):
            # Noise operators not 2d
            for hn in H_n:
                hn[0] = np.tile(hn[0], (rng.randint(2, 11), 1, 1))
            ff.PulseSequence(H_c, H_n, dt)

        for hn in H_n:
            hn[0] = hn[0][0]
        with self.assertRaises(ValueError):
            # Control operators not square
            for hc in H_c:
                hc[0] = np.tile(hc[0].reshape(1, 4), (2, 1))
            ff.PulseSequence(H_c, H_n, dt)

        for hc in H_c:
            hc[0] = hc[0][0].reshape(2, 2)
        with self.assertRaises(ValueError):
            # Noise operators not square
            for hn in H_n:
                hn[0] = np.tile(hn[0].reshape(1, 4), (2, 1))
            ff.PulseSequence(H_c, H_n, dt)

        for hn in H_n:
            hn[0] = hn[0][0].reshape(2, 2)
        with self.assertRaises(ValueError):
            # Control and noise operators not same dimension
            for hn in H_n:
                hn[0] = np.block([[hn[0], hn[0]], [hn[0], hn[0]]])
            ff.PulseSequence(H_c, H_n, dt)

        for hn in H_n:
            hn[0] = hn[0][:2, :2]
        with self.assertRaises(ValueError):
            # Control identifiers not unique
            identifier = H_c[idx][2]
            H_c[idx][2] = H_c[idx-1][2]
            ff.PulseSequence(H_c, H_n, dt)

        H_c[idx][2] = identifier
        with self.assertRaises(ValueError):
            # Noise identifiers not unique
            identifier = H_n[idx][2]
            H_n[idx][2] = H_n[idx-1][2]
            ff.PulseSequence(H_c, H_n, dt)

        H_n[idx][2] = identifier
        coeffs = []
        with self.assertRaises(ValueError):
            # Control coefficients not same length as dt
            for hc in H_c:
                coeffs.append(hc[1][-2:])
                hc[1] = hc[1][:-2]
            ff.PulseSequence(H_c, H_n, dt)

        for i, c in enumerate(coeffs):
            H_c[i][1] = np.concatenate((H_c[i][1], c))
        with self.assertRaises(ValueError):
            # Noise coefficients not same length as dt
            for hn in H_n:
                hn[1] = hn[1][:-2]
            ff.PulseSequence(H_c, H_n, dt)

        for i, c in enumerate(coeffs):
            H_n[i][1] = np.concatenate((H_n[i][1], c))
        pulse = ff.PulseSequence(H_c, H_n, dt)
        # Hit __str__ and __repr__ methods
        pulse
        print(pulse)

        # Hit __copy__ method
        _ = copy(pulse)

        # Fewer identifiers than opers
        pulse_2 = ff.PulseSequence(
            [[util.paulis[1], [1], 'X'],
             [util.paulis[2], [1]]],
            [[util.paulis[1], [1]],
             [util.paulis[2], [1], 'Y']],
            [1]
        )
        self.assertArrayEqual(pulse_2.c_oper_identifiers, ('A_1', 'X'))
        self.assertArrayEqual(pulse_2.n_oper_identifiers, ('B_0', 'Y'))
예제 #27
0
    def test_pulse_sequence_attributes(self):
        """Test attributes of single instance"""
        X, Y, Z = util.paulis[1:]
        n_dt = rng.randint(1, 10)

        # trivial case
        A = ff.PulseSequence([[X, rng.standard_normal(n_dt), 'X']],
                             [[Z, rng.standard_normal(n_dt), 'Z']],
                             np.abs(rng.standard_normal(n_dt)))
        self.assertFalse(A == 1)
        self.assertTrue(A != 1)

        # different number of time steps
        B = ff.PulseSequence([[X, rng.standard_normal(n_dt+1), 'X']],
                             [[Z, rng.standard_normal(n_dt+1), 'Z']],
                             np.abs(rng.standard_normal(n_dt+1)))
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different time steps
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)),
            list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)),
            np.abs(rng.standard_normal(n_dt))
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different control opers
        B = ff.PulseSequence(
            list(zip([Y], A.c_coeffs, A.c_oper_identifiers)),
            list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different control coeffs
        B = ff.PulseSequence(
            list(zip(A.c_opers, [rng.standard_normal(n_dt)],
                     A.c_oper_identifiers)),
            list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different noise opers
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)),
            list(zip([Y], A.n_coeffs, A.n_oper_identifiers)),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different noise coeffs
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)),
            list(zip(A.n_opers, [rng.standard_normal(n_dt)],
                     A.n_oper_identifiers)),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different control oper identifiers
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, ['foobar'])),
            list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different noise oper identifiers
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)),
            list(zip(A.n_opers, A.n_coeffs, ['foobar'])),
            A.dt
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # different bases
        elem = testutil.rand_herm_traceless(2)
        B = ff.PulseSequence(
            list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)),
            list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)),
            A.dt,
            ff.Basis(elem)
        )
        self.assertFalse(A == B)
        self.assertTrue(A != B)

        # Test sparse operators for whatever reason
        A = ff.PulseSequence([[util.paulis[1], [1]]],
                             [[sparse.COO.from_numpy(util.paulis[2]), [2]]],
                             [3])
        B = ff.PulseSequence([[sparse.COO.from_numpy(util.paulis[1]), [1]]],
                             [[util.paulis[2], [2]]],
                             [3])
        self.assertEqual(A, B)

        # Test for attributes
        for attr in A.__dict__.keys():
            if not (attr.startswith('_') or '_' + attr in A.__dict__.keys()):
                # not a cached attribute
                with self.assertRaises(AttributeError):
                    _ = A.is_cached(attr)
            else:
                # set mock attribute at random
                if rng.randint(0, 2):
                    setattr(A, attr, 'foo')
                    assertion = self.assertTrue
                else:
                    setattr(A, attr, None)
                    assertion = self.assertFalse

                assertion(A.is_cached(attr))

        # Diagonalization attributes
        A.diagonalize()
        self.assertIsNotNone(A.eigvals)
        self.assertIsNotNone(A.eigvecs)
        self.assertIsNotNone(A.propagators)

        A.cleanup('conservative')
        self.assertIsNotNone(A.eigvals)
        A.cleanup('conservative')
        self.assertIsNotNone(A.eigvecs)
        A.cleanup('conservative')
        self.assertIsNotNone(A.propagators)

        aliases = {'eigenvalues': '_eigvals',
                   'eigenvectors': '_eigvecs',
                   'total propagator': '_total_propagator',
                   'total propagator liouville': '_total_propagator_liouville',
                   'frequencies': '_omega',
                   'total phases': '_total_phases',
                   'filter function': '_filter_function',
                   'fidelity filter function': '_filter_function',
                   'generalized filter function': '_filter_function_gen',
                   'pulse correlation filter function': '_filter_function_pc',
                   'fidelity pulse correlation filter function': '_filter_function_pc',
                   'generalized pulse correlation filter function': '_filter_function_pc_gen',
                   'control matrix': '_control_matrix',
                   'pulse correlation control matrix': '_control_matrix_pc'}

        for alias, attr in aliases.items():
            # set mock attribute at random
            if rng.randint(0, 2):
                setattr(A, attr, 'foo')
                assertion = self.assertTrue
            else:
                setattr(A, attr, None)
                assertion = self.assertFalse

            assertion(A.is_cached(alias))
            assertion(A.is_cached(alias.upper()))
            assertion(A.is_cached(alias.replace(' ', '_')))

        A.cleanup('all')

        # Test cleanup
        C = ff.concatenate((A, A), calc_pulse_correlation_FF=True,
                           which='generalized',
                           omega=util.get_sample_frequencies(A))
        C.diagonalize()
        attrs = ['_eigvals', '_eigvecs', '_propagators']
        for attr in attrs:
            self.assertIsNotNone(getattr(C, attr))

        C.cleanup()
        for attr in attrs:
            self.assertIsNone(getattr(C, attr))

        C.diagonalize()
        C.cache_control_matrix(A.omega)
        attrs.extend(['_control_matrix', '_total_phases', '_total_propagator',
                      '_total_propagator_liouville'])
        for attr in attrs:
            self.assertIsNotNone(getattr(C, attr))

        C.cleanup('greedy')
        for attr in attrs:
            self.assertIsNone(getattr(C, attr))

        C.cache_filter_function(A.omega, which='generalized')
        for attr in attrs + ['omega', '_filter_function_gen',
                             '_filter_function_pc_gen']:
            self.assertIsNotNone(getattr(C, attr))

        C = ff.concatenate((A, A), calc_pulse_correlation_FF=True,
                           which='fidelity', omega=A.omega)
        C.diagonalize()
        C.cache_filter_function(A.omega, which='fidelity')
        attrs.extend(['omega', '_filter_function', '_filter_function_pc'])
        for attr in attrs:
            self.assertIsNotNone(getattr(C, attr))

        C.cleanup('all')
        for attr in attrs + ['_filter_function_gen',
                             '_filter_function_pc_gen']:
            self.assertIsNone(getattr(C, attr))

        C.cache_filter_function(A.omega, which='fidelity')
        C.cleanup('frequency dependent')
        freq_attrs = {'omega', '_control_matrix', '_filter_function',
                      '_filter_function_gen', '_filter_function_pc',
                      '_filter_function_pc_gen', '_total_phases'}
        for attr in freq_attrs:
            self.assertIsNone(getattr(C, attr))

        for attr in set(attrs).difference(freq_attrs):
            self.assertIsNotNone(getattr(C, attr))
예제 #28
0
    def test_filter_function(self):
        """Test the filter function calculation and related methods"""
        for d, n_dt in zip(rng.randint(2, 10, (3,)),
                           rng.randint(10, 200, (3,))):
            total_pulse = testutil.rand_pulse_sequence(d, n_dt, 4, 6)
            c_opers, c_coeffs = total_pulse.c_opers, total_pulse.c_coeffs
            n_opers, n_coeffs = total_pulse.n_opers, total_pulse.n_coeffs
            dt = total_pulse.dt

            total_eigvals, total_eigvecs, _ = numeric.diagonalize(
                np.einsum('il,ijk->ljk', c_coeffs, c_opers), total_pulse.dt
            )
            omega = util.get_sample_frequencies(total_pulse, n_samples=100)
            # Try the progress bar
            control_matrix = total_pulse.get_control_matrix(
                omega, show_progressbar=True)

            # Check that some attributes are cached
            self.assertIsNotNone(total_pulse._total_phases)
            self.assertIsNotNone(total_pulse._total_propagator)
            self.assertIsNotNone(total_pulse._total_propagator_liouville)

            # Calculate everything 'on foot'
            pulses = [ff.PulseSequence(list(zip(c_opers, c_coeffs[:, i:i+1])),
                                       list(zip(n_opers, n_coeffs[:, i:i+1])),
                                       dt[i:i+1])
                      for i in range(n_dt)]

            phases = np.empty((n_dt, len(omega)), dtype=complex)
            L = np.empty((n_dt, d**2, d**2))
            control_matrix_g = np.empty((n_dt, 6, d**2, len(omega)),
                                        dtype=complex)
            for g, pulse in enumerate(pulses):
                phases[g] = np.exp(1j*total_pulse.t[g]*omega)
                L[g] = ff.superoperator.liouville_representation(total_pulse.propagators[g],
                                                                 total_pulse.basis)
                control_matrix_g[g] = pulse.get_control_matrix(omega)

            # Check that both methods of calculating the control are the same
            control_matrix_from_atomic = numeric.calculate_control_matrix_from_atomic(
                phases, control_matrix_g, L
            )
            control_matrix_from_scratch = numeric.calculate_control_matrix_from_scratch(
                eigvals=total_eigvals,
                eigvecs=total_eigvecs,
                propagators=total_pulse.propagators,
                omega=omega,
                basis=total_pulse.basis,
                n_opers=n_opers,
                n_coeffs=n_coeffs,
                dt=total_pulse.dt
            )
            self.assertArrayAlmostEqual(control_matrix, control_matrix_from_scratch)
            # first column (identity element) always zero but susceptible to
            # floating point error, increase atol
            self.assertArrayAlmostEqual(control_matrix_from_scratch,
                                        control_matrix_from_atomic,
                                        atol=1e-13)

            # Check if the filter functions for autocorrelated noise are real
            filter_function = total_pulse.get_filter_function(omega)
            self.assertTrue(np.isreal(
                filter_function[np.eye(len(n_opers), dtype=bool)]
            ).all())

            # Check switch between fidelity and generalized filter function
            F_generalized = total_pulse.get_filter_function(
                omega, which='generalized')

            F_fidelity = total_pulse.get_filter_function(
                omega, which='fidelity')

            # Check that F_fidelity is correctly reduced from F_generalized
            self.assertArrayAlmostEqual(F_fidelity,
                                        F_generalized.trace(axis1=2, axis2=3))

            # Hit getters again to check caching functionality
            F_generalized = total_pulse.get_filter_function(
                omega, which='generalized')

            F_fidelity = total_pulse.get_filter_function(
                omega, which='fidelity')

            # Check that F_fidelity is correctly reduced from F_generalized
            self.assertArrayAlmostEqual(F_fidelity,
                                        F_generalized.trace(axis1=2, axis2=3))

            # Different set of frequencies than cached
            F_generalized = total_pulse.get_filter_function(
                omega + 1, which='generalized')

            F_fidelity = total_pulse.get_filter_function(
                omega + 1, which='fidelity')

            # Check that F_fidelity is correctly reduced from F_generalized
            self.assertArrayAlmostEqual(F_fidelity,
                                        F_generalized.trace(axis1=2, axis2=3))
예제 #29
0
    def test_pulse_sequence_attributes_concat(self):
        """Test attributes of concatenated sequence."""
        X, Y, Z = util.paulis[1:]
        n_dt_1 = rng.randint(5, 11)
        x_coeff_1 = rng.standard_normal(n_dt_1)
        z_coeff_1 = rng.standard_normal(n_dt_1)
        dt_1 = np.abs(rng.standard_normal(n_dt_1))
        n_dt_2 = rng.randint(5, 11)
        y_coeff_2 = rng.standard_normal(n_dt_2)
        z_coeff_2 = rng.standard_normal(n_dt_2)
        dt_2 = np.abs(rng.standard_normal(n_dt_2))
        pulse_1 = ff.PulseSequence([[X, x_coeff_1]],
                                   [[Z, z_coeff_1]],
                                   dt_1)
        pulse_2 = ff.PulseSequence([[Y, y_coeff_2]],
                                   [[Z, z_coeff_2]],
                                   dt_2)
        pulse_3 = ff.PulseSequence([[Y, rng.standard_normal(2)],
                                    [X, rng.standard_normal(2)]],
                                   [[Z, np.abs(rng.standard_normal(2))]],
                                   [1, 1])

        # Concatenate with different noise opers
        pulses = [testutil.rand_pulse_sequence(2, 1) for _ in range(2)]
        pulses[0].omega = np.arange(10)
        pulses[1].omega = np.arange(10)
        newpulse = ff.concatenate(pulses, calc_filter_function=True)
        self.assertTrue(newpulse.is_cached('filter function'))

        pulse_12 = pulse_1 @ pulse_2
        pulse_21 = pulse_2 @ pulse_1

        with self.assertRaises(TypeError):
            _ = pulse_1 @ rng.standard_normal((2, 2))

        # Concatenate pulses with same operators but different labels
        with self.assertRaises(ValueError):
            pulse_1 @ pulse_3

        # Test nbytes property
        _ = pulse_1.nbytes

        self.assertArrayEqual(pulse_12.dt, [*dt_1, *dt_2])
        self.assertArrayEqual(pulse_21.dt, [*dt_2, *dt_1])

        self.assertArrayEqual(pulse_12.c_opers, [X, Y])
        self.assertArrayEqual(pulse_21.c_opers, [Y, X])

        self.assertArrayEqual(pulse_12.c_oper_identifiers, ['A_0_0', 'A_0_1'])
        self.assertArrayEqual(pulse_21.c_oper_identifiers, ['A_0_0', 'A_0_1'])

        self.assertArrayEqual(pulse_12.c_coeffs,
                              [[*x_coeff_1, *np.zeros(n_dt_2)],
                               [*np.zeros(n_dt_1), *y_coeff_2]])
        self.assertArrayEqual(pulse_21.c_coeffs,
                              [[*y_coeff_2, *np.zeros(n_dt_1)],
                               [*np.zeros(n_dt_2), *x_coeff_1]])

        self.assertArrayEqual(pulse_12.n_opers, [Z])
        self.assertArrayEqual(pulse_21.n_opers, [Z])

        self.assertArrayEqual(pulse_12.n_oper_identifiers, ['B_0'])
        self.assertArrayEqual(pulse_21.n_oper_identifiers, ['B_0'])

        self.assertArrayEqual(pulse_12.n_coeffs, [[*z_coeff_1, *z_coeff_2]])
        self.assertArrayEqual(pulse_21.n_coeffs, [[*z_coeff_2, *z_coeff_1]])

        omega = np.linspace(-100, 100, 101)
        pulses = (pulse_1, pulse_2, pulse_12, pulse_21)
        for pulse in pulses:
            self.assertIsNone(pulse._total_phases)
            self.assertIsNone(pulse._total_propagator)
            self.assertIsNone(pulse._total_propagator_liouville)

            total_phases = pulse.get_total_phases(omega)
            total_propagator = pulse.total_propagator
            total_propagator_liouville = pulse.total_propagator_liouville

            self.assertArrayEqual(total_phases, pulse._total_phases)
            self.assertArrayEqual(total_propagator, pulse._total_propagator)
            self.assertArrayEqual(total_propagator_liouville,
                                  pulse._total_propagator_liouville)

        # Test custom identifiers
        letters = rng.choice(list(string.ascii_letters), size=(6, 5),
                             replace=False)
        ids = [''.join(c) for c in letters[:3]]
        labels = [''.join(c) for c in letters[3:]]
        pulse = ff.PulseSequence(
            list(zip([X, Y, Z], rng.standard_normal((3, 2)), ids, labels)),
            list(zip([X, Y, Z], rng.standard_normal((3, 2)), ids, labels)),
            [1, 1]
        )

        self.assertArrayEqual(pulse.c_oper_identifiers, sorted(ids))
        self.assertArrayEqual(pulse.n_oper_identifiers, sorted(ids))
예제 #30
0
    def test_pulse_correlation_filter_function(self):
        """
        Test calculation of pulse correlation filter function and control
        matrix.
        """
        X, Y, Z = util.paulis[1:]
        T = 1
        omega = np.linspace(-2e1, 2e1, 250)
        H_c, H_n, dt = dict(), dict(), dict()
        H_c['X'] = [[X, [np.pi/2/T]]]
        H_n['X'] = [[X, [1]],
                    [Y, [1]],
                    [Z, [1]]]
        dt['X'] = [T]
        H_c['Y'] = [[Y, [np.pi/4/T]]]
        H_n['Y'] = [[X, [1]],
                    [Y, [1]],
                    [Z, [1]]]
        dt['Y'] = [T]
        n_nops = 3

        # Check if an exception is raised if we want to calculate the PC-FF but
        # one pulse has different frequencies
        with self.assertRaises(ValueError):
            pulses = dict()
            for i, key in enumerate(('X', 'Y')):
                pulses[key] = ff.PulseSequence(H_c[key], H_n[key], dt[key])
                pulses[key].cache_filter_function(omega + i)

            ff.concatenate([pulses['X'], pulses['Y']],
                           calc_pulse_correlation_FF=True)

        # Get filter functions at same frequencies
        [pulse.cache_filter_function(omega) for pulse in pulses.values()]

        pulse_1 = pulses['X'] @ pulses['Y']
        pulse_2 = ff.concatenate([pulses['X'], pulses['Y']],
                                 calc_pulse_correlation_FF=True,
                                 which='fidelity')
        pulse_3 = ff.concatenate([pulses['X'], pulses['Y']],
                                 calc_pulse_correlation_FF=True,
                                 which='generalized')

        self.assertTrue(pulse_2.is_cached('control_matrix_pc'))
        self.assertTrue(pulse_2.is_cached('filter_function_pc'))
        self.assertTrue(pulse_3.is_cached('control_matrix_pc'))
        self.assertTrue(pulse_3.is_cached('filter_function_pc_gen'))

        # Check if the filter functions on the diagonals are real
        filter_function = pulse_2.get_pulse_correlation_filter_function()
        diag_1 = np.eye(2, dtype=bool)
        diag_2 = np.eye(3, dtype=bool)
        self.assertTrue(np.isreal(filter_function[diag_1][:, diag_2]).all())

        self.assertEqual(pulse_1, pulse_2)
        self.assertEqual(pulse_2.get_pulse_correlation_filter_function().shape,
                         (2, 2, n_nops, n_nops, len(omega)))
        self.assertArrayAlmostEqual(
            pulse_1.get_filter_function(omega),
            pulse_2.get_pulse_correlation_filter_function().sum((0, 1))
        )
        self.assertArrayAlmostEqual(pulse_1.get_filter_function(omega),
                                    pulse_2._filter_function)

        # Test the behavior of the pulse correlation control matrix
        with self.assertRaises(ValueError):
            # R wrong dimension
            numeric.calculate_pulse_correlation_filter_function(
                pulse_1._control_matrix)

        with self.assertRaises(util.CalculationError):
            # not calculated
            pulse_1.get_pulse_correlation_control_matrix()

        control_matrix_pc = pulse_2.get_pulse_correlation_control_matrix()
        self.assertArrayEqual(
            filter_function,
            numeric.calculate_pulse_correlation_filter_function(
                control_matrix_pc, 'fidelity'
            )
        )

        control_matrix_pc = pulse_3.get_pulse_correlation_control_matrix()
        filter_function = \
            pulse_3.get_pulse_correlation_filter_function(which='fidelity')
        self.assertArrayEqual(
            filter_function,
            numeric.calculate_pulse_correlation_filter_function(
                control_matrix_pc, 'fidelity'
            )
        )

        filter_function = \
            pulse_3.get_pulse_correlation_filter_function(which='generalized')
        self.assertArrayEqual(
            filter_function,
            numeric.calculate_pulse_correlation_filter_function(
                control_matrix_pc, 'generalized'
            )
        )

        # If for some reason filter_function_pc_xy is removed, check if
        # recovered from control_matrix_pc
        pulse_2._filter_function_pc = None
        pulse_3._filter_function_pc_gen = None

        control_matrix_pc = pulse_3.get_pulse_correlation_control_matrix()
        filter_function = \
            pulse_3.get_pulse_correlation_filter_function(which='fidelity')
        self.assertArrayEqual(
            filter_function,
            numeric.calculate_pulse_correlation_filter_function(
                control_matrix_pc, 'fidelity'
            )
        )

        filter_function = \
            pulse_3.get_pulse_correlation_filter_function(which='generalized')
        self.assertArrayEqual(
            filter_function,
            numeric.calculate_pulse_correlation_filter_function(
                control_matrix_pc, 'generalized'
            )
        )

        spectrum = omega**0*1e-2
        with self.assertRaises(util.CalculationError):
            infid_1 = ff.infidelity(pulse_1, spectrum, omega,
                                    which='correlations')

        with self.assertRaises(ValueError):
            infid_1 = ff.infidelity(pulse_1, spectrum, omega, which='foobar')

        for _ in range(10):
            n_nops = rng.randint(1, 4)
            identifiers = sample(['B_0', 'B_1', 'B_2'], n_nops)

            infid_X = ff.infidelity(pulses['X'], spectrum, omega,
                                    which='total',
                                    n_oper_identifiers=identifiers)
            infid_Y = ff.infidelity(pulses['Y'], spectrum, omega,
                                    which='total',
                                    n_oper_identifiers=identifiers)
            infid_1 = ff.infidelity(pulse_1, spectrum, omega, which='total',
                                    n_oper_identifiers=identifiers)
            infid_2 = ff.infidelity(pulse_2, spectrum, omega,
                                    which='correlations',
                                    n_oper_identifiers=identifiers)

            self.assertAlmostEqual(infid_1.sum(), infid_2.sum())
            self.assertArrayAlmostEqual(infid_X, infid_2[0, 0])
            self.assertArrayAlmostEqual(infid_Y, infid_2[1, 1])

        # Test function for correlated noise spectra
        spectrum = np.array([[1e-4/omega**2, 1e-4*np.exp(-omega**2)],
                             [1e-4*np.exp(-omega**2), 1e-4/omega**2]])
        infid_1 = ff.infidelity(pulse_1, spectrum, omega, which='total',
                                n_oper_identifiers=['B_0', 'B_2'])
        infid_2 = ff.infidelity(pulse_2, spectrum, omega, which='correlations',
                                n_oper_identifiers=['B_0', 'B_2'])

        self.assertAlmostEqual(infid_1.sum(), infid_2.sum())
        self.assertArrayAlmostEqual(infid_1, infid_2.sum(axis=(0, 1)))