def test_liouville_to_choi(self):
        """Test converting Liouville superops to choi matrices."""
        for d in rng.integers(2, 9, (15, )):
            # unitary channel
            U = testutil.rand_unit(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)

            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)
Exemple #2
0
    def test_second_order_filter_function(self):
        for d, n_nops in zip(rng.integers(2, 7, 5), rng.integers(1, 5, 5)):
            pulse = testutil.rand_pulse_sequence(d, 3, 2, n_nops)
            omega = util.get_sample_frequencies(pulse, n_samples=42)

            # Make sure result is the same with or without intermediates
            pulse.cache_control_matrix(omega, cache_intermediates=True)
            F = pulse.get_filter_function(omega, order=1)
            F_1 = pulse.get_filter_function(omega, order=2)
            # Test caching
            F_2 = pulse.get_filter_function(omega, order=2)
            F_3 = numeric.calculate_second_order_filter_function(
                pulse.eigvals,
                pulse.eigvecs,
                pulse.propagators,
                omega,
                pulse.basis,
                pulse.n_opers,
                pulse.n_coeffs,
                pulse.dt,
                show_progressbar=False,
                intermediates=None)
            # Make sure first and second order are of same order of magnitude
            rel = np.linalg.norm(F) / np.linalg.norm(F_1)

            self.assertIs(F_1, F_2)
            self.assertArrayEqual(F_1, F_3)
            self.assertEqual(F_1.shape, (n_nops, n_nops, d**2, d**2, 42))
            self.assertLessEqual(rel, 10)
            self.assertGreaterEqual(rel, 1 / 10)
    def test_basis_generation_from_partial_pauli(self):
        """"Generate complete basis from partial elements of a Pauli basis"""
        # Do test runs with random elements from a Pauli basis in (2 ... 8)
        # dimensions
        for _ in range(50):
            n = rng.integers(1, 4)
            d = 2**n
            b = ff.Basis.pauli(n)
            inds = [i for i in range(d**2)]
            tup = tuple(
                inds.pop(rng.integers(0, len(inds)))
                for _ in range(rng.integers(1, d**2)))
            elems = b[tup, ...]
            basis = ff.Basis.from_partial(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):
                ff.Basis.from_partial([basis[0], 1j * basis[1]])

            with self.assertRaises(ValueError):
                ff.Basis.from_partial([basis[0], basis[0] + basis[1]])
Exemple #4
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.integers(1, 4, 10),
                                             rng.integers(3, 6, 10),
                                             rng.integers(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))
Exemple #5
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.integers(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.integers(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))
    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])
Exemple #7
0
    def test_dot_HS(self):
        U, V = rng.integers(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.integers(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_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.integers(3, 7, 10), rng.integers(1, 6, 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)

            # Single spectrum, different spectra for each noise oper
            spectra = [
                1e-8 / omega**2,
                np.outer(1e-7 * (np.arange(n_nops) + 1),
                         400 / (omega**2 + 400))
            ]
            # Cross-correlated spectra which are complex, real part symmetric and
            # imaginary part antisymmetric
            spec = np.tile(1e-8 / abs(omega)**2,
                           (n_nops, n_nops, 1)).astype(complex)
            spec[np.triu_indices(n_nops, 1)].imag = 1e-10 * omega
            spec[np.tril_indices(
                n_nops, -1)].imag = -spec[np.triu_indices(n_nops, 1)].imag
            spectra.append(spec)

            for S in spectra:
                # Assert fidelity is same as computed by infidelity()
                U = ff.error_transfer_matrix(pulse, S, omega)
                # Calculate U in loop
                Up = ff.error_transfer_matrix(pulse,
                                              S,
                                              omega,
                                              memory_parsimonious=True)
                # Calculate second order
                U2 = ff.error_transfer_matrix(pulse,
                                              S,
                                              omega,
                                              second_order=True)
                I_fidelity = ff.infidelity(pulse, S, omega)
                I_transfer = 1 - np.einsum('...ii', U) / d**2
                I_transfer_2 = 1 - np.einsum('...ii', U2) / d**2
                self.assertArrayAlmostEqual(Up, U)
                self.assertArrayAlmostEqual(I_transfer,
                                            I_fidelity.sum(),
                                            atol=1e-4)
                self.assertArrayAlmostEqual(I_transfer_2,
                                            I_fidelity.sum(),
                                            atol=1e-4)
    def test_calculation_with_unitaries(self):
        for d, G in zip(rng.integers(2, 9, (11, )),
                        rng.integers(2, 11, (11, ))):
            pulses = [
                testutil.rand_pulse_sequence(d, rng.integers(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.integers(0, 2):
                    pulse._t = None

            omega = rng.random(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)
Exemple #10
0
    def test_oper_equiv(self):
        self.assertFalse(
            util.oper_equiv(qutip.rand_ket(2), qutip.rand_dm(2))[0])

        for d in rng.integers(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(np.mod(result[1], 2 * np.pi),
                                   np.mod(phase, 2 * np.pi),
                                   places=5)

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

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

            result = util.oper_equiv(U * np.exp(1j * phase), U)
            self.assertTrue(result[0])
            self.assertAlmostEqual(np.mod(result[1], 2 * np.pi),
                                   np.mod(-phase, 2 * np.pi),
                                   places=5)
Exemple #11
0
 def test_basis_generation_from_partial_ggm(self):
     """"Generate complete basis from partial elements of a GGM basis"""
     # Do test runs with random elements from a GGM basis in (2 ... 8)
     # dimensions
     for _ in range(50):
         d = rng.integers(2, 9)
         b = ff.Basis.ggm(d)
         inds = [i for i in range(d**2)]
         tup = tuple(
             inds.pop(rng.integers(0, len(inds)))
             for _ in range(rng.integers(1, d**2)))
         elems = b[tup, ...]
         basis = ff.Basis.from_partial(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))
    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.integers(2, 9, (15, )):
            # unitary channel
            U = testutil.rand_unit(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)

            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))
Exemple #13
0
    def test_all_array_equal(self):
        for n in rng.integers(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))
Exemple #14
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(arr.shape)
                arr[rng.integers(0, 2, arr.shape, dtype=bool)] *= -1
                arr = util.remove_float_errors(arr, eps_scale)
                self.assertArrayEqual(arr, np.zeros(arr.shape, dtype=dtype))

                arr = np.array(0, dtype=dtype)
                arr += scale * np.finfo(arr.dtype).eps * rng.random()
                arr = util.remove_float_errors(arr, eps_scale)
                self.assertArrayEqual(arr, np.zeros(arr.shape, dtype=dtype))
Exemple #15
0
    def test_dot_HS(self):
        for d in rng.integers(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)
Exemple #16
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.integers(1, 4))

        n, infids = ff.infidelity(complicated_pulse,
                                  spectrum,
                                  omega,
                                  test_convergence=True,
                                  n_oper_identifiers=identifiers)
Exemple #17
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.integers(2, 7)
            # Get a random traceless hermitian operator
            oper = testutil.rand_herm_traceless(d)
            # ... and build a basis from it
            b = ff.Basis.from_partial(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.integers(0, len(inds)))
                for _ in range(rng.integers(1, d**2)))
            elems = b[tup, ...]
            basis = ff.Basis.from_partial(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.integers(2, 7)
            # Get a random hermitian operator
            oper = testutil.rand_herm(d)
            # ... and build a basis from it
            b = ff.Basis.from_partial(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.integers(0, len(inds)))
                for _ in range(rng.integers(1, d**2)))
            elems = b[tup, ...]
            basis = ff.Basis.from_partial(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))
Exemple #18
0
    def test_cumulant_function(self):
        pulse = testutil.rand_pulse_sequence(2, 1, 1, 1)
        omega = rng.standard_normal(43)
        spectrum = rng.standard_normal(43)
        Gamma = numeric.calculate_decay_amplitudes(pulse, spectrum, omega)
        Delta = numeric.calculate_frequency_shifts(pulse, spectrum, omega)
        K_1 = numeric.calculate_cumulant_function(pulse, spectrum, omega)
        K_2 = numeric.calculate_cumulant_function(pulse,
                                                  decay_amplitudes=Gamma)
        K_3 = numeric.calculate_cumulant_function(pulse,
                                                  spectrum,
                                                  omega,
                                                  second_order=True)
        K_4 = numeric.calculate_cumulant_function(pulse,
                                                  decay_amplitudes=Gamma,
                                                  frequency_shifts=Delta,
                                                  second_order=True)
        self.assertArrayAlmostEqual(K_1, K_2)
        self.assertArrayAlmostEqual(K_3, K_4)

        with self.assertRaises(ValueError):
            # Neither spectrum + frequencies nor decay amplitudes supplied
            numeric.calculate_cumulant_function(pulse,
                                                None,
                                                None,
                                                decay_amplitudes=None)

        with self.assertRaises(ValueError):
            # Neither spectrum + frequencies nor frequency shifts supplied
            numeric.calculate_cumulant_function(pulse,
                                                None,
                                                None,
                                                frequency_shifts=None,
                                                second_order=True)

        with self.assertRaises(ValueError):
            # Trying to get correlation cumulant function for second order
            numeric.calculate_cumulant_function(pulse,
                                                spectrum,
                                                omega,
                                                second_order=True,
                                                which='correlations')

        with self.assertRaises(ValueError):
            # Using precomputed frequency shifts or decay amplitudes but different shapes
            numeric.calculate_cumulant_function(pulse,
                                                spectrum,
                                                omega,
                                                second_order=True,
                                                decay_amplitudes=Gamma[1:])

        with self.assertWarns(UserWarning):
            # Memory parsimonious only works for decay amplitudes
            numeric.calculate_cumulant_function(pulse,
                                                spectrum,
                                                omega,
                                                second_order=True,
                                                memory_parsimonious=True)

        for d in [2, *rng.integers(2, 7, 5)]:
            pulse = testutil.rand_pulse_sequence(d, 3, 2, 2)
            omega = util.get_sample_frequencies(pulse, n_samples=42)
            spectrum = 4e-3 / abs(omega)

            pulse.cache_control_matrix(omega, cache_intermediates=True)
            cumulant_function_first_order = numeric.calculate_cumulant_function(
                pulse, spectrum, omega, second_order=False)
            cumulant_function_second_order = numeric.calculate_cumulant_function(
                pulse, spectrum, omega, second_order=True)
            # Make sure first and second order are --roughly -- of same order
            # of magnitude. Unlike the frequency shifts themselves, the
            # second order contributions to the cumulant function vanish on the
            # diagonal, whereas the first order contributions dominate. Hence,
            # be quite lenient.
            second_order_contribution = (cumulant_function_second_order -
                                         cumulant_function_first_order)
            rel = (np.linalg.norm(cumulant_function_first_order) /
                   np.linalg.norm(second_order_contribution))

            # Second order terms should be anti-hermitian
            self.assertArrayAlmostEqual(
                second_order_contribution,
                -second_order_contribution.transpose(0, 2, 1),
                atol=1e-16)
            self.assertEqual(cumulant_function_first_order.shape,
                             cumulant_function_second_order.shape)
            self.assertLessEqual(rel, 200)
            self.assertGreaterEqual(rel, 1 / 10)
    def test_single_qubit_error_transfer_matrix(self):
        """Test the calculation of the single-qubit transfer matrix"""
        d = 2
        for n_dt in rng.integers(1, 11, 10):
            pulse = testutil.rand_pulse_sequence(d, n_dt, 3, 2, btype='Pauli')
            traceless = rng.integers(2, dtype=bool)
            if not traceless:
                # Test that result correct for finite trace n_oper (edge case)
                pulse.n_opers[0] = np.eye(d) / np.sqrt(d)

            omega = util.get_sample_frequencies(pulse, n_samples=51)
            n_oper_identifiers = pulse.n_oper_identifiers
            traces = pulse.basis.four_element_traces.todense()

            # Single spectrum, different spectra for each noise oper, and
            # Cross-correlated spectra which are complex, real part symmetric and
            # imaginary part antisymmetric
            spectra = [
                1e-8 / omega**2,
                np.outer(1e-6 * np.arange(1, 3), 400 / (omega**2 + 400)),
                np.array([[
                    1e-6 / abs(omega), 1e-8 / abs(omega) + 1j * 1e-8 / omega
                ], [1e-8 / abs(omega) - 1j * 1e-8 / omega, 2e-6 / abs(omega)]])
            ]

            for S in spectra:
                # Assert fidelity is same as computed by infidelity()
                U = ff.error_transfer_matrix(pulse, S, omega)
                # Calculate U in loop
                Up = ff.error_transfer_matrix(pulse,
                                              S,
                                              omega,
                                              memory_parsimonious=True)
                # Calculate on foot (multi-qubit way)
                Gamma = numeric.calculate_decay_amplitudes(
                    pulse, S, omega, n_oper_identifiers)
                Delta = numeric.calculate_frequency_shifts(
                    pulse, S, omega, n_oper_identifiers)
                K = -(np.einsum('...kl,klji->...ij', Gamma, traces) -
                      np.einsum('...kl,kjli->...ij', Gamma, traces) -
                      np.einsum('...kl,kilj->...ij', Gamma, traces) +
                      np.einsum('...kl,kijl->...ij', Gamma, traces)) / 2
                U_onfoot = sla.expm(K.sum(tuple(range(K.ndim - 2))))
                U_from_K = ff.error_transfer_matrix(cumulant_function=K)
                self.assertArrayAlmostEqual(Up, U)
                self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14)
                self.assertArrayAlmostEqual(U_from_K, U_onfoot)
                if traceless:
                    # Simplified fidelity calculation relies on traceless n_opers
                    I_fidelity = ff.infidelity(pulse, S, omega)
                    I_decayamps = -np.einsum('...ii', K) / d**2
                    I_transfer = 1 - np.einsum('...ii', U) / d**2
                    self.assertArrayAlmostEqual(I_fidelity, I_decayamps)
                    self.assertArrayAlmostEqual(I_transfer,
                                                I_fidelity.sum(),
                                                rtol=1e-4,
                                                atol=1e-10)

                # second order
                K -= (np.einsum('...kl,klji->...ij', Delta, traces) -
                      np.einsum('...kl,lkji->...ij', Delta, traces) -
                      np.einsum('...kl,klij->...ij', Delta, traces) +
                      np.einsum('...kl,lkij->...ij', Delta, traces)) / 2
                U = ff.error_transfer_matrix(pulse,
                                             S,
                                             omega,
                                             second_order=True)
                U_onfoot = sla.expm(K.sum(tuple(range(K.ndim - 2))))
                U_from_K = ff.error_transfer_matrix(cumulant_function=K)
                self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14)
                self.assertArrayAlmostEqual(U_from_K, U_onfoot)
                if traceless:
                    # Simplified fidelity calculation relies on traceless n_opers
                    I_transfer = 1 - np.einsum('...ii', U) / d**2
                    self.assertArrayAlmostEqual(I_transfer,
                                                I_fidelity.sum(),
                                                rtol=1e-4,
                                                atol=1e-10)
Exemple #20
0
    def test_pulse_sequence_attributes(self):
        """Test attributes of single instance"""
        X, Y, Z = util.paulis[1:]
        n_dt = rng.integers(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.integers(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.integers(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')
        A._t = None
        A._tau = None

        # 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))

        # Test t, tau, and duration properties
        pulse = testutil.rand_pulse_sequence(2, 3, 1, 1)
        self.assertIs(pulse._t, None)
        self.assertIs(pulse._tau, None)
        self.assertArrayEqual(pulse.t, [0, *pulse.dt.cumsum()])
        self.assertEqual(pulse.tau, pulse.t[-1])
        self.assertEqual(pulse.duration, pulse.tau)
Exemple #21
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.integers(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.integers(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.integers(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.integers(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)

        # 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'))
Exemple #22
0
    def test_pulse_sequence_attributes_concat(self):
        """Test attributes of concatenated sequence."""
        X, Y, Z = util.paulis[1:]
        n_dt_1 = rng.integers(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.integers(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])
        pulse_4 = ff.PulseSequence(
            [[Y, rng.standard_normal(2)], [X, rng.standard_normal(2)]],
            [[Z, np.ones(2)]], [1, 1])
        pulse_5 = ff.PulseSequence([[Y, np.zeros(5), 'A_0']],
                                   [[Y, np.zeros(5), 'B_1']],
                                   1 - rng.random(5))

        # 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'))

        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

        pulse_12 = pulse_1 @ pulse_2
        pulse_21 = pulse_2 @ pulse_1
        pulse_45 = pulse_4 @ pulse_5

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

        self.assertIs(pulse_12._t, None)
        self.assertIs(pulse_21._t, None)

        self.assertEqual(pulse_12._tau, pulse_1.tau + pulse_2.tau)
        self.assertEqual(pulse_21._tau, pulse_1.tau + pulse_2.tau)

        self.assertAlmostEqual(pulse_12.duration,
                               pulse_1.duration + pulse_2.duration)
        self.assertAlmostEqual(pulse_21.duration,
                               pulse_2.duration + pulse_1.duration)
        self.assertAlmostEqual(pulse_12.duration, pulse_21.duration)

        self.assertArrayAlmostEqual(
            pulse_12.t, [*pulse_1.t, *(pulse_2.t[1:] + pulse_1.tau)])
        self.assertArrayAlmostEqual(
            pulse_21.t, [*pulse_2.t, *(pulse_1.t[1:] + pulse_2.tau)])

        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]])

        # Make sure zero coefficients are handled correctly
        self.assertFalse(np.any(np.isnan(pulse_45.c_coeffs)))
        self.assertFalse(np.any(np.isnan(pulse_45.n_coeffs)))
        self.assertArrayEqual(pulse_45.c_coeffs,
                              [[*pulse_4.c_coeffs[0], *np.zeros(5)],
                               [*pulse_4.c_coeffs[1], *np.zeros(5)]])
        self.assertArrayEqual(
            pulse_45.n_coeffs,
            [[*pulse_4.n_coeffs[0], *[pulse_4.n_coeffs[0, 0]] * 5],
             [*[pulse_5.n_coeffs[0, 0]] * 2, *pulse_5.n_coeffs[0]]])

        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))

        pulse = testutil.rand_pulse_sequence(2, 7, 1, 2)
        periodic_pulse = ff.concatenate_periodic(pulse, 7)

        self.assertIs(periodic_pulse._t, None)
        self.assertEqual(periodic_pulse._tau, pulse.tau * 7)
        self.assertArrayAlmostEqual(periodic_pulse.t,
                                    [0, *periodic_pulse.dt.cumsum()])
Exemple #23
0
    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)
Exemple #24
0
    def test_basis_properties(self):
        """Basis orthonormal and of correct dimensions"""
        d = rng.integers(2, 17)
        n = rng.integers(1, 5)

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

        btypes = ('Pauli', 'GGM', 'From partial', 'Custom')
        bases = (pauli_basis, ggm_basis, from_partial_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.integers(0, len(base))] in base)
            else:
                self.assertEqual(2**n, base.d)
                # Check if __contains__ works as expected
                self.assertTrue(base[rng.integers(0, len(base))] 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 == 'From partial':
                self.assertTrue(base.istraceless)
            else:
                self.assertFalse(base.istraceless)

            if not btype == 'Custom':
                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 = util.paulis[1].view(ff.Basis)
        self.assertTrue(basis.isorthonorm)
        self.assertArrayEqual(basis.T, basis.view(np.ndarray).T)
Exemple #25
0
    def test_filter_function(self):
        """Test the filter function calculation and related methods"""
        for d, n_dt in zip(rng.integers(2, 10, (3, )),
                           rng.integers(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))
Exemple #26
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')
        pulse_4 = copy.copy(pulse_3)
        pulse_4.cleanup('all')

        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'))

        # Test caching
        pulse_4.cache_filter_function(omega,
                                      control_matrix=control_matrix_pc,
                                      which='fidelity')
        self.assertTrue(pulse_4.is_cached('pulse correlation control matrix'))
        self.assertTrue(pulse_4.is_cached('pulse correlation filter function'))
        self.assertTrue(pulse_4.is_cached('filter function'))
        self.assertArrayAlmostEqual(
            pulse_3.get_pulse_correlation_control_matrix(),
            pulse_4.get_pulse_correlation_control_matrix())
        self.assertArrayAlmostEqual(
            pulse_3.get_pulse_correlation_filter_function(),
            pulse_4.get_pulse_correlation_filter_function())
        self.assertArrayAlmostEqual(pulse_3.get_filter_function(omega),
                                    pulse_4.get_filter_function(omega))

        pulse_4.cleanup('all')
        pulse_4.cache_filter_function(omega,
                                      control_matrix=control_matrix_pc,
                                      which='generalized')
        self.assertTrue(pulse_4.is_cached('pulse correlation control matrix'))
        self.assertTrue(
            pulse_4.is_cached('generalized pulse correlation filter function'))
        self.assertTrue(pulse_4.is_cached('generalized filter function'))
        self.assertTrue(pulse_4.is_cached('pulse correlation filter function'))
        self.assertTrue(pulse_4.is_cached('filter function'))
        self.assertArrayAlmostEqual(
            pulse_3.get_pulse_correlation_control_matrix(),
            pulse_4.get_pulse_correlation_control_matrix())
        self.assertArrayAlmostEqual(
            pulse_3.get_pulse_correlation_filter_function('fidelity'),
            pulse_4.get_pulse_correlation_filter_function('fidelity'))
        self.assertArrayAlmostEqual(
            pulse_3.get_pulse_correlation_filter_function('generalized'),
            pulse_4.get_pulse_correlation_filter_function('generalized'))
        self.assertArrayAlmostEqual(
            pulse_3.get_filter_function(omega, which='fidelity'),
            pulse_4.get_filter_function(omega, which='fidelity'))
        self.assertArrayAlmostEqual(
            pulse_3.get_filter_function(omega, which='generalized'),
            pulse_4.get_filter_function(omega, which='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.integers(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)))
Exemple #27
0
    def test_tensor_insert(self):
        I, X, Y, Z = util.paulis
        arr = util.tensor(X, I)

        with self.assertRaises(ValueError):
            # Test exception for empty args
            util.tensor_insert(arr, np.array([[]]), pos=0, arr_dims=[])

        r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=0)
        self.assertArrayAlmostEqual(r, util.tensor(Y, Z, X, I))

        r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=1)
        self.assertArrayAlmostEqual(r, util.tensor(X, Y, Z, I))

        r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=2)
        self.assertArrayAlmostEqual(r, util.tensor(X, I, Y, Z))

        # Test pos being negative
        r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=-1)
        self.assertArrayAlmostEqual(r, util.tensor(X, Y, Z, I))

        # Test pos exception
        with self.assertRaises(IndexError) as err:
            util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=3)

        msg = 'Invalid position 3 specified. Must be between -2 and 2.'
        self.assertEqual(msg, str(err.exception))

        # Test broadcasting and rank != 2
        A, B, C = (rng.standard_normal(
            (2, 3, 1, 2)), rng.standard_normal(
                (2, 3, 1, 2)), rng.standard_normal((3, 1, 3)))

        arr = util.tensor(A, C, rank=1)
        r = util.tensor_insert(arr, B, pos=1, rank=1, arr_dims=[[2, 3]])
        self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=1))

        # Test exceptions for wrong arr_dims format
        with self.assertRaises(ValueError):
            util.tensor_insert(arr,
                               B,
                               pos=1,
                               rank=1,
                               arr_dims=[[3, 3], [1, 2], [2, 1]])

        with self.assertRaises(ValueError):
            util.tensor_insert(arr, B, pos=1, rank=1, arr_dims=[[2], [2, 1]])

        A, B, C = (rng.standard_normal(
            (2, 3, 1, 2)), rng.standard_normal(
                (2, 3, 2, 2)), rng.standard_normal((3, 2, 1)))
        arr = util.tensor(A, C, rank=3)
        r = util.tensor_insert(arr,
                               B,
                               pos=1,
                               rank=3,
                               arr_dims=[[3, 3], [1, 2], [2, 1]])
        self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=3))

        # Test exceptions for wrong arr_dims format
        with self.assertRaises(ValueError):
            util.tensor_insert(arr,
                               B,
                               pos=1,
                               rank=3,
                               arr_dims=[[1, 2], [2, 1]])

        with self.assertRaises(ValueError):
            util.tensor_insert(arr,
                               B,
                               pos=1,
                               rank=2,
                               arr_dims=[[3, 3, 1], [1, 2], [2]])

        A, B, C = (rng.standard_normal((2, 1)), rng.standard_normal(
            (1, 2, 3)), rng.standard_normal(1))

        arr = util.tensor(A, C, rank=1)
        r = util.tensor_insert(arr, B, pos=0, rank=1, arr_dims=[[1, 1]])
        self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=1))

        arrs, args = [
            rng.standard_normal((2, 2, 2)),
            rng.standard_normal((2, 2, 2))
        ]
        arr_dims = [[2, 2], [2, 2]]

        r = util.tensor_insert(util.tensor(*arrs),
                               *args,
                               pos=(0, 1),
                               arr_dims=arr_dims)
        self.assertArrayAlmostEqual(
            r, util.tensor(args[0], arrs[0], args[1], arrs[1]))

        r = util.tensor_insert(util.tensor(*arrs),
                               *args,
                               pos=(0, 0),
                               arr_dims=arr_dims)
        self.assertArrayAlmostEqual(r, util.tensor(*args, *arrs))

        r = util.tensor_insert(util.tensor(*arrs),
                               *args,
                               pos=(1, 2),
                               arr_dims=arr_dims)
        self.assertArrayAlmostEqual(
            r, util.tensor(*np.insert(arrs, (1, 2), args, axis=0)))

        # Test exception for wrong pos argument
        with self.assertRaises(ValueError):
            util.tensor_insert(util.tensor(*arrs),
                               *args,
                               pos=(0, 1, 2),
                               arr_dims=arr_dims)

        # Test exception for wrong shapes
        arrs, args = (rng.standard_normal(
            (2, 4, 3, 2)), rng.standard_normal((2, 2, 3, 4)))
        with self.assertRaises(ValueError) as err:
            util.tensor_insert(util.tensor(*arrs),
                               *args,
                               pos=(1, 2),
                               arr_dims=[[3, 3], [2, 2]])

        err_msg = ('Could not insert arg 0 with shape (4, 9, 4) into the ' +
                   'array with shape (2, 3, 4) at position 1.')
        cause_msg = ('Incompatible shapes (2, 3, 4) and (4, 9, 4) for ' +
                     'tensor product of rank 2.')

        self.assertEqual(err_msg, str(err.exception))
        self.assertEqual(cause_msg, str(err.exception.__cause__))

        # Do some random tests
        for rank, n_args, n_broadcast in zip(rng.integers(1, 4, 10),
                                             rng.integers(3, 6, 10),
                                             rng.integers(1, 11, 10)):
            arrs = rng.standard_normal((n_args, n_broadcast, *[2] * rank))
            split_idx = rng.integers(1, n_args - 1)
            ins_idx = rng.integers(split_idx - n_args, n_args - split_idx)
            ins_arrs = arrs[:split_idx]
            arr = util.tensor(*arrs[split_idx:], rank=rank)
            sorted_arrs = np.insert(arrs[split_idx:],
                                    ins_idx,
                                    ins_arrs,
                                    axis=0)

            arr_dims = [[2] * (n_args - split_idx)] * rank
            r = util.tensor_insert(arr,
                                   *ins_arrs,
                                   pos=ins_idx,
                                   rank=rank,
                                   arr_dims=arr_dims)
            self.assertArrayAlmostEqual(r, util.tensor(*sorted_arrs,
                                                       rank=rank))

            pos = rng.integers(-split_idx + 1, split_idx, split_idx)
            r = util.tensor_insert(arr,
                                   *ins_arrs,
                                   pos=pos,
                                   rank=rank,
                                   arr_dims=arr_dims)
            sorted_arrs = np.insert(arrs[split_idx:], pos, ins_arrs, axis=0)
            self.assertArrayAlmostEqual(r,
                                        util.tensor(*sorted_arrs, rank=rank),
                                        atol=1e-10)
Exemple #28
0
    def test_tensor_merge(self):
        # Test basic functionality
        I, X, Y, Z = util.paulis
        arr = util.tensor(X, Y, Z)
        ins = util.tensor(I, I)
        r1 = util.tensor_merge(arr,
                               ins,
                               pos=[1, 2],
                               arr_dims=[[2] * 3, [2] * 3],
                               ins_dims=[[2] * 2, [2] * 2])
        r2 = util.tensor_merge(ins,
                               arr,
                               pos=[0, 1, 2],
                               arr_dims=[[2] * 2, [2] * 2],
                               ins_dims=[[2] * 3, [2] * 3])

        self.assertArrayAlmostEqual(r1, util.tensor(X, I, Y, I, Z))
        self.assertArrayAlmostEqual(r1, r2)

        # Test if tensor_merge and tensor_insert produce same results
        arr = util.tensor(Y, Z)
        ins = util.tensor(I, X)
        r1 = util.tensor_merge(arr,
                               ins,
                               pos=[0, 0],
                               arr_dims=[[2] * 2, [2] * 2],
                               ins_dims=[[2] * 2, [2] * 2])
        r2 = util.tensor_insert(arr,
                                I,
                                X,
                                pos=[0, 0],
                                arr_dims=[[2] * 2, [2] * 2])
        self.assertArrayAlmostEqual(r1, r2)

        # Test pos being negative
        r = util.tensor_merge(arr,
                              ins,
                              arr_dims=[[2, 2], [2, 2]],
                              ins_dims=[[2, 2], [2, 2]],
                              pos=(-1, -2))
        self.assertArrayAlmostEqual(r, util.tensor(X, Y, I, Z))

        # Test exceptions
        # Wrong dims format
        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 2], [2, 2], [2, 2]],
                              ins_dims=[[2, 2], [2, 2]])

        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 2], [2, 2]],
                              ins_dims=[[2, 2], [2, 2], [2, 2]])

        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 2], [2, 2, 2]],
                              ins_dims=[[2, 2], [2, 2]])

        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 2], [2, 2]],
                              ins_dims=[[2, 2], [2, 2, 2]])

        # Wrong pos
        with self.assertRaises(IndexError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 3),
                              arr_dims=[[2, 2], [2, 2]],
                              ins_dims=[[2, 2], [2, 2]])

        # Wrong dimensions given
        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 3], [2, 2]],
                              ins_dims=[[2, 2], [2, 2]])

        with self.assertRaises(ValueError):
            util.tensor_merge(arr,
                              ins,
                              pos=(1, 2),
                              arr_dims=[[2, 2], [2, 2]],
                              ins_dims=[[2, 3], [2, 2]])

        # Incompatible shapes
        arrs, args = (rng.standard_normal(
            (2, 4, 3, 2)), rng.standard_normal((2, 2, 3, 4)))
        with self.assertRaises(ValueError) as err:
            util.tensor_merge(util.tensor(*arrs),
                              util.tensor(*args),
                              pos=(1, 2),
                              arr_dims=[[3, 3], [2, 2]],
                              ins_dims=[[3, 3], [4, 4]])

        msg = ('Incompatible shapes (2, 9, 16) and (4, 9, 4) for tensor ' +
               'product of rank 2.')

        self.assertEqual(msg, str(err.exception))

        # Test rank 1 and broadcasting
        arr = rng.standard_normal((2, 10, 3, 4))
        ins = rng.standard_normal((2, 10, 3, 2))
        r = util.tensor_merge(util.tensor(*arr, rank=1),
                              util.tensor(*ins, rank=1),
                              pos=[0, 1],
                              arr_dims=[[4, 4]],
                              ins_dims=[[2, 2]],
                              rank=1)
        self.assertArrayAlmostEqual(
            r, util.tensor(ins[0], arr[0], ins[1], arr[1], rank=1))

        # Do some random tests
        for rank, n_args, n_broadcast in zip(rng.integers(1, 4, 10),
                                             rng.integers(3, 6, 10),
                                             rng.integers(1, 11, 10)):
            arrs = rng.standard_normal((n_args, n_broadcast, *[2] * rank))
            split_idx = rng.integers(1, n_args - 1)
            arr = util.tensor(*arrs[split_idx:], rank=rank)
            ins = util.tensor(*arrs[:split_idx], rank=rank)
            pos = rng.integers(0, split_idx, split_idx)
            sorted_arrs = np.insert(arrs[split_idx:],
                                    pos,
                                    arrs[:split_idx],
                                    axis=0)

            arr_dims = [[2] * (n_args - split_idx)] * rank
            ins_dims = [[2] * split_idx] * rank
            r = util.tensor_merge(arr,
                                  ins,
                                  pos=pos,
                                  rank=rank,
                                  arr_dims=arr_dims,
                                  ins_dims=ins_dims)
            self.assertArrayAlmostEqual(r, util.tensor(*sorted_arrs,
                                                       rank=rank))
Exemple #29
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.integers(2, 10, (5, )):
            psi = rng.standard_normal((d, 1)) + 1j * rng.standard_normal(
                (d, 1))
            # Also test broadcasting
            U = testutil.rand_herm(d, rng.integers(1, 11)).squeeze()
            phase = rng.standard_normal()

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

            result = util.oper_equiv(psi * np.exp(1j * phase), psi)
            self.assertTrue(result[0])
            self.assertAlmostEqual(np.mod(result[1], 2 * np.pi),
                                   np.mod(-phase, 2 * np.pi),
                                   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(np.mod(result[1], 2 * np.pi),
                                        np.mod(phase, 2 * np.pi),
                                        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(np.mod(result[1], 2 * np.pi),
                                        np.mod(phase, 2 * np.pi),
                                        atol=1e-5)

            result = util.oper_equiv(U * np.exp(1j * phase), U)
            self.assertTrue(np.all(result[0]))
            self.assertArrayAlmostEqual(np.mod(result[1], 2 * np.pi),
                                        np.mod(-phase, 2 * np.pi),
                                        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(np.mod(result[1], 2 * np.pi),
                                        np.mod(phase, 2 * np.pi))

            result = util.oper_equiv(U, U + 1)
            self.assertFalse(np.all(result[0]))