Example #1
0
    def test_plot_pulse_correlation_filter_function(self):
        omega = np.linspace(-1, 1, 50)
        concatenated_simple_pulse = ff.concatenate(
            (simple_pulse, simple_pulse),
            calc_pulse_correlation_FF=True,
            omega=omega)
        concatenated_complicated_pulse = ff.concatenate(
            (complicated_pulse, complicated_pulse),
            calc_pulse_correlation_FF=True,
            omega=omega)

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

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

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

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

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

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

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

        plt.close('all')
Example #2
0
def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int,
                                alpha: Sequence[float],
                                spectra: Dict[float, Sequence[float]],
                                omega: Sequence[float],
                                cliffords: Sequence[ff.PulseSequence]):
    infidelities = {a: np.empty((N_l, N_G), dtype=float) for a in alpha}
    lengths = np.round(np.linspace(min_l, max_l, N_l)).astype(int)
    delta_t = []
    t_now = [time.perf_counter()]
    print(f'Start simulation with {len(lengths)} sequence lengths')
    print('---------------------------------------------')
    for l, length in enumerate(lengths):
        t_now.append(time.perf_counter())
        delta_t.append(t_now[-1] - t_now[-2])
        print('Sequence length',
              length,
              f'Elapsed time: {t_now[-1] - t_now[0]:.2f} s',
              sep='\t')
        for j in range(N_G):
            randints = np.random.randint(0, len(cliffords), lengths[l])
            U = ff.concatenate(cliffords[randints])
            U_inv = find_inverse(U.total_propagator, cliffords)
            pulse_sequence = U @ U_inv
            for k, a in enumerate(alpha):
                infidelities[a][l,
                                j] = state_infidelity(pulse_sequence,
                                                      spectra[a], omega).sum()

    return infidelities, delta_t
Example #3
0
    def test_concatenation_periodic(self):
        """Test concatenation for periodic Hamiltonians"""
        X, Y, Z = util.paulis[1:]
        A = 0.01
        omega_0 = 1
        omega_d = omega_0
        tau = np.pi/A
        omega = np.logspace(np.log10(omega_0) - 3, np.log10(omega_0) + 3, 1001)

        t = np.linspace(0, tau, 1001)
        dt = np.diff(t)
        H_c = [[Z, [omega_0/2]*len(dt)],
               [X, A*np.cos(omega_d*t[1:])]]
        H_n = [[Z, np.ones_like(dt)],
               [X, np.ones_like(dt)]]

        NOT_LAB = ff.PulseSequence(H_c, H_n, dt)
        F_LAB = NOT_LAB.get_filter_function(omega)

        T = 2*np.pi/omega_d
        G = round(tau/T)
        t = np.linspace(0, T, int(T/NOT_LAB.dt[0])+1)
        dt = np.diff(t)

        H_c = [[Z, [omega_0/2]*len(dt)],
               [X, A*np.cos(omega_d*t[1:])]]
        H_n = [[Z, np.ones_like(dt)],
               [X, np.ones_like(dt)]]

        ATOMIC = ff.PulseSequence(H_c, H_n, dt)
        ATOMIC.cache_filter_function(omega)

        NOT_CC = ff.concatenate((ATOMIC for _ in range(G)))
        F_CC = NOT_CC.get_filter_function(omega)
        NOT_CC_PERIODIC = ff.concatenate_periodic(ATOMIC, G)
        F_CC_PERIODIC = NOT_CC_PERIODIC.get_filter_function(omega)

        # Have to do manual comparison due to floating point error. The high
        # rtol is due to 1e-21/1e-19 occurring once.
        attrs = ('dt', 'c_opers', 'c_coeffs', 'n_opers', 'n_coeffs')
        for attr in attrs:
            self.assertArrayAlmostEqual(getattr(NOT_LAB, attr),
                                        getattr(NOT_CC, attr),
                                        atol=1e-15, rtol=1e2)
            self.assertArrayAlmostEqual(getattr(NOT_LAB, attr),
                                        getattr(NOT_CC_PERIODIC, attr),
                                        atol=1e-15, rtol=1e2)

        # Check if stuff is cached
        self.assertIsNotNone(NOT_CC._total_phases)
        self.assertIsNotNone(NOT_CC._total_propagator)
        self.assertIsNotNone(NOT_CC._total_propagator_liouville)
        # concatenate_periodic does not cache phase factors
        self.assertIsNotNone(NOT_CC_PERIODIC._total_phases)
        self.assertIsNotNone(NOT_CC_PERIODIC._total_propagator)
        self.assertIsNotNone(NOT_CC_PERIODIC._total_propagator_liouville)

        self.assertArrayAlmostEqual(F_LAB, F_CC, atol=1e-13)
        self.assertArrayAlmostEqual(F_LAB, F_CC_PERIODIC, atol=1e-13)
Example #4
0
def QFT_pulse(N: int = 4, tau: float = 1):
    pulses = [T_I_pulse(N, tau)]
    for n in range(N - 1):
        pulses.append(H_k_pulse(n, N, tau))
        pulses.append(P_n_pulse(n + 1, N, tau))

    pulses.append(H_k_pulse(N - 1, N, tau))
    pulses.append(T_F_pulse(N, tau))

    return ff.concatenate(pulses, calc_pulse_correlation_FF=False)
Example #5
0
    def test_concatenate_base(self):
        """Basic functionality."""
        pulse_1, pulse_2 = [testutil.rand_pulse_sequence(2, 1, 2, 3)
                            for _ in range(2)]

        # Trivial case, copy
        c_pulse = ff.concatenate([pulse_1])
        self.assertEqual(pulse_1, c_pulse)
        self.assertFalse(pulse_1 is c_pulse)

        # Don't cache filter function, expect same result as with
        # concatenate_without_filter_function
        c_pulse_1 = ff.concatenate([pulse_1, pulse_2],
                                   calc_filter_function=False)
        c_pulse_2 = pulse_sequence.concatenate_without_filter_function(
            [pulse_1, pulse_2], return_identifier_mappings=False
        )
        self.assertEqual(c_pulse_1, c_pulse_2)

        # Try concatenation with different frequencies but FF calc. forced
        with self.assertRaises(ValueError):
            pulse_1.omega = [1, 2]
            pulse_2.omega = [3, 4]
            ff.concatenate([pulse_1, pulse_2], calc_filter_function=True)
Example #6
0
    def test_concatenate_4_spin_echos(self):
        """Concatenate four Spin Echos with a random one having a filter
        function
        """
        tau = 1
        tau_pi = 1e-4
        omega = np.logspace(-2, 1, 200)
        n = 1

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

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

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

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

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

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

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

        self.assertEqual(CPMG, CPMG_concat_1)
        self.assertEqual(CPMG, CPMG_concat_2)
        self.assertArrayAlmostEqual(CPMG_concat_1._filter_function,
                                    CPMG._filter_function, rtol=1e-10)
        self.assertArrayAlmostEqual(CPMG_concat_2._filter_function,
                                    CPMG._filter_function, rtol=1e-10)
    def test_integrals_against_numeric(self):
        """Test the private function used to set up the integrand."""
        pulses = [
            testutil.rand_pulse_sequence(3, 1, 2, 3),
            testutil.rand_pulse_sequence(3, 1, 2, 3)
        ]
        pulses[1].n_opers = pulses[0].n_opers
        pulses[1].n_oper_identifiers = pulses[0].n_oper_identifiers

        omega = np.linspace(-1, 1, 50)
        spectra = [
            1e-6 / abs(omega),
            1e-6 / np.power.outer(abs(omega), np.arange(2)).T,
            np.array([[
                1e-6 / abs(omega)**0.7,
                1e-6 / (1 + omega**2) + 1j * 1e-6 * omega
            ],
                      [
                          1e-6 / (1 + omega**2) - 1j * 1e-6 * omega,
                          1e-6 / abs(omega)**0.7
                      ]])
        ]

        pulse = ff.concatenate(pulses,
                               omega=omega,
                               calc_pulse_correlation_FF=True)

        idx = testutil.rng.choice(np.arange(2),
                                  testutil.rng.integers(1, 3),
                                  replace=False)

        R = pulse.get_control_matrix(omega)
        R_pc = pulse.get_pulse_correlation_control_matrix()
        F = pulse.get_filter_function(omega)
        F_kl = pulse.get_filter_function(omega, 'generalized')
        F_pc = pulse.get_pulse_correlation_filter_function()
        F_pc_kl = pulse.get_pulse_correlation_filter_function('generalized')

        for i, spectrum in enumerate(spectra):
            if i == 0:
                S = spectrum
            elif i == 1:
                S = spectrum[idx]
            elif i == 2:
                S = spectrum[idx[None, :], idx[:, None]]

            R_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='fidelity',
                                         control_matrix=R,
                                         filter_function=None)
            R_2 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='fidelity',
                                         control_matrix=[R, R],
                                         filter_function=None)
            F_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='fidelity',
                                         control_matrix=None,
                                         filter_function=F)

            self.assertArrayAlmostEqual(R_1, R_2)
            self.assertArrayAlmostEqual(R_1, F_1)

            R_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='fidelity',
                                         control_matrix=R_pc,
                                         filter_function=None)
            R_2 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='fidelity',
                                         control_matrix=[R_pc, R_pc],
                                         filter_function=None)
            F_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='fidelity',
                                         control_matrix=None,
                                         filter_function=F_pc)

            self.assertArrayAlmostEqual(R_1, R_2)
            self.assertArrayAlmostEqual(R_1, F_1)

            R_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='generalized',
                                         control_matrix=R,
                                         filter_function=None)
            R_2 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='generalized',
                                         control_matrix=[R, R],
                                         filter_function=None)
            F_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='total',
                                         which_FF='generalized',
                                         control_matrix=None,
                                         filter_function=F_kl)

            self.assertArrayAlmostEqual(R_1, R_2)
            self.assertArrayAlmostEqual(R_1, F_1)

            R_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='generalized',
                                         control_matrix=R_pc,
                                         filter_function=None)
            R_2 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='generalized',
                                         control_matrix=[R_pc, R_pc],
                                         filter_function=None)
            F_1 = numeric._get_integrand(S,
                                         omega,
                                         idx,
                                         which_pulse='correlations',
                                         which_FF='generalized',
                                         control_matrix=None,
                                         filter_function=F_pc_kl)

            self.assertArrayAlmostEqual(R_1, R_2)
            self.assertArrayAlmostEqual(R_1, F_1)
Example #8
0
    def test_pulse_correlation_filter_function(self):
        """
        Test calculation of pulse correlation filter function and control
        matrix.
        """
        X, Y, Z = util.paulis[1:]
        T = 1
        omega = np.linspace(-2e1, 2e1, 250)
        H_c, H_n, dt = dict(), dict(), dict()
        H_c['X'] = [[X, [np.pi/2/T]]]
        H_n['X'] = [[X, [1]],
                    [Y, [1]],
                    [Z, [1]]]
        dt['X'] = [T]
        H_c['Y'] = [[Y, [np.pi/4/T]]]
        H_n['Y'] = [[X, [1]],
                    [Y, [1]],
                    [Z, [1]]]
        dt['Y'] = [T]
        n_nops = 3

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.assertAlmostEqual(infid_1.sum(), infid_2.sum())
        self.assertArrayAlmostEqual(infid_1, infid_2.sum(axis=(0, 1)))
Example #9
0
    def test_pulse_sequence_attributes_concat(self):
        """Test attributes of concatenated sequence."""
        X, Y, Z = util.paulis[1:]
        n_dt_1 = rng.randint(5, 11)
        x_coeff_1 = rng.standard_normal(n_dt_1)
        z_coeff_1 = rng.standard_normal(n_dt_1)
        dt_1 = np.abs(rng.standard_normal(n_dt_1))
        n_dt_2 = rng.randint(5, 11)
        y_coeff_2 = rng.standard_normal(n_dt_2)
        z_coeff_2 = rng.standard_normal(n_dt_2)
        dt_2 = np.abs(rng.standard_normal(n_dt_2))
        pulse_1 = ff.PulseSequence([[X, x_coeff_1]],
                                   [[Z, z_coeff_1]],
                                   dt_1)
        pulse_2 = ff.PulseSequence([[Y, y_coeff_2]],
                                   [[Z, z_coeff_2]],
                                   dt_2)
        pulse_3 = ff.PulseSequence([[Y, rng.standard_normal(2)],
                                    [X, rng.standard_normal(2)]],
                                   [[Z, np.abs(rng.standard_normal(2))]],
                                   [1, 1])

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

        pulse_12 = pulse_1 @ pulse_2
        pulse_21 = pulse_2 @ pulse_1

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

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

        # Test nbytes property
        _ = pulse_1.nbytes

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

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

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

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

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

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

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

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

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

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

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

        self.assertArrayEqual(pulse.c_oper_identifiers, sorted(ids))
        self.assertArrayEqual(pulse.n_oper_identifiers, sorted(ids))
Example #10
0
    def test_pulse_sequence_attributes(self):
        """Test attributes of single instance"""
        X, Y, Z = util.paulis[1:]
        n_dt = rng.randint(1, 10)

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

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

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

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

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

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

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

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

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

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

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

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

                assertion(A.is_cached(attr))

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

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

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

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

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

        A.cleanup('all')

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

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

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

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

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

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

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

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

        for attr in set(attrs).difference(freq_attrs):
            self.assertIsNotNone(getattr(C, attr))
Example #11
0
def H_k_pulse(k, N: int = 4, tau: float = 1):
    return ff.concatenate([
        R_k_pulse(k, np.pi, 0, N, tau),
        R_k_pulse(k, np.pi / 2, -np.pi / 2, N, tau)
    ])
Example #12
0
    def test_concatenate_without_filter_function(self):
        """Concatenate two Spin Echos without filter functions."""
        tau = 10
        tau_pi = 1e-4
        n = 1

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

        n_oper = util.paulis[3]
        H_n_SE = [[n_oper, np.ones_like(dt_SE)]]
        SE_1 = ff.PulseSequence(H_c_SE, H_n_SE, dt_SE)
        SE_2 = ff.PulseSequence(H_c_SE, H_n_SE, dt_SE)

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

        H_n_CPMG = [[n_oper, np.ones_like(dt_CPMG)]]
        CPMG = ff.PulseSequence(H_c_CPMG, H_n_CPMG, dt_CPMG)
        CPMG_concat = SE_1 @ SE_2

        self.assertEqual(CPMG_concat, CPMG)
        # Should still be None
        self.assertEqual(CPMG_concat._filter_function, CPMG._filter_function)

        # Test if calculation of composite filter function can be enforced with
        # omega != None
        omega = util.get_sample_frequencies(SE_1)
        CPMG_concat = ff.concatenate((SE_1, SE_2), omega=omega)
        self.assertIsNotNone(CPMG_concat._filter_function)

        pulse = testutil.rand_pulse_sequence(2, 1, 2, 3)
        # Concatenate pulses without filter functions
        with self.assertRaises(TypeError):
            # Not all pulse sequence
            pulse_sequence.concatenate_without_filter_function([pulse, 2])

        with self.assertRaises(TypeError):
            # Not iterable
            pulse_sequence.concatenate_without_filter_function(pulse)

        with self.assertRaises(ValueError):
            # Incompatible Hamiltonian shapes
            pulse_sequence.concatenate_without_filter_function(
                [testutil.rand_pulse_sequence(2, 1),
                 testutil.rand_pulse_sequence(3, 1)]
            )

        with self.assertRaises(ValueError):
            # Incompatible bases
            pulse = testutil.rand_pulse_sequence(4, 1, btype='GGM')
            cpulse = copy(pulse)
            cpulse.basis = ff.Basis.pauli(2)
            pulse_sequence.concatenate_without_filter_function([pulse, cpulse])

        pulse = pulse_sequence.concatenate_without_filter_function(
            [pulse, pulse], return_identifier_mappings=False
        )
        self.assertFalse(pulse.is_cached('filter function'))
Example #13
0
    def test_different_n_opers(self):
        """Test behavior when concatenating with different n_opers."""
        for d, n_dt in zip(rng.randint(2, 5, 20),
                           rng.randint(1, 11, 20)):
            opers = testutil.rand_herm_traceless(d, 10)
            letters = np.array(sample(list(string.ascii_letters), 10))
            n_idx = sample(range(10), rng.randint(2, 5))
            c_idx = sample(range(10), rng.randint(2, 5))
            n_opers = opers[n_idx]
            c_opers = opers[c_idx]
            n_coeffs = np.ones((n_opers.shape[0], n_dt))
            n_coeffs *= np.abs(rng.standard_normal((n_opers.shape[0], 1)))
            c_coeffs = rng.standard_normal((c_opers.shape[0], n_dt))
            dt = np.abs(rng.standard_normal(n_dt))
            n_ids = np.array([''.join(l) for l in letters[n_idx]])
            c_ids = np.array([''.join(l) for l in letters[c_idx]])

            pulse_1 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)),
                                       list(zip(n_opers, n_coeffs, n_ids)),
                                       dt)
            permutation = rng.permutation(range(n_opers.shape[0]))
            pulse_2 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)),
                                       list(zip(n_opers[permutation],
                                                n_coeffs[permutation],
                                                n_ids[permutation])),
                                       dt)
            more_n_idx = sample(range(10), rng.randint(2, 5))
            more_n_opers = opers[more_n_idx]
            more_n_coeffs = np.ones((more_n_opers.shape[0], n_dt))
            more_n_coeffs *= np.abs(rng.standard_normal(
                (more_n_opers.shape[0], 1)))
            more_n_ids = np.array([''.join(l) for l in letters[more_n_idx]])
            pulse_3 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)),
                                       list(zip(more_n_opers, more_n_coeffs,
                                                more_n_ids)),
                                       dt)

            nontrivial_n_coeffs = np.abs(rng.standard_normal(
                (n_opers.shape[0], n_dt)))
            pulse_4 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)),
                                       list(zip(more_n_opers,
                                                nontrivial_n_coeffs,
                                                more_n_ids)),
                                       dt)

            omega = np.geomspace(.1, 10, 50)

            # Test caching
            with self.assertRaises(ValueError):
                ff.concatenate([pulse_1, pulse_3],
                               calc_filter_function=True)

            with self.assertRaises(ValueError):
                ff.concatenate([pulse_1, pulse_3],
                               calc_pulse_correlation_FF=True)

            pulse_1.cache_filter_function(omega)
            pulse_2.cache_filter_function(omega)
            pulse_3.cache_filter_function(omega)
            pulse_11 = ff.concatenate([pulse_1, pulse_1])
            pulse_12 = ff.concatenate([pulse_1, pulse_2])
            pulse_13_1 = ff.concatenate([pulse_1, pulse_3])
            pulse_13_2 = ff.concatenate([pulse_1, pulse_3],
                                        calc_filter_function=True)

            # concatenate pulses with different n_opers and nontrivial sens.
            subset = (set.issubset(set(pulse_1.n_oper_identifiers),
                                   set(pulse_4.n_oper_identifiers)) or
                      set.issubset(set(pulse_4.n_oper_identifiers),
                                   set(pulse_1.n_oper_identifiers)))

            if not subset and (len(pulse_1.dt) > 1 or len(pulse_4.dt) > 1):
                with self.assertRaises(ValueError):
                    pulse_1 @ pulse_4

            self.assertEqual(pulse_11, pulse_12)
            # Filter functions should be the same even though pulse_2 has
            # different n_oper ordering
            self.assertArrayAlmostEqual(pulse_11._control_matrix,
                                        pulse_12._control_matrix, atol=1e-12)
            self.assertArrayAlmostEqual(pulse_11._filter_function,
                                        pulse_12._filter_function, atol=1e-12)

            should_be_cached = False
            for i in n_idx:
                if i in more_n_idx:
                    should_be_cached = True
            self.assertEqual(should_be_cached,
                             pulse_13_1.is_cached('filter_function'))

            # Test forcibly caching
            self.assertTrue(pulse_13_2.is_cached('filter_function'))
Example #14
0
    def test_concatenate_split_cnot(self):
        """Split up cnot and concatenate the parts."""
        c_opers, c_coeffs, dt = (testutil.subspace_opers, testutil.c_coeffs,
                                 testutil.dt)
        n_opers = c_opers
        n_coeffs = testutil.n_coeffs

        H_c = list(zip(c_opers, c_coeffs))
        H_n = list(zip(n_opers, n_coeffs))
        omega = np.logspace(-2, 1, 200)

        cnot_whole = ff.PulseSequence(H_c, H_n, dt)

        cnot_sliced = [
            ff.PulseSequence(list(zip(c_opers, [c[:10] for c in c_coeffs])),
                             list(zip(n_opers, [n[:10] for n in n_coeffs])),
                             dt[:10]),
            ff.PulseSequence(list(zip(c_opers, [c[10:15] for c in c_coeffs])),
                             list(zip(n_opers, [n[10:15] for n in n_coeffs])),
                             dt[10:15]),
            ff.PulseSequence(list(zip(c_opers, [c[15:100] for c in c_coeffs])),
                             list(zip(n_opers, [n[15:100] for n in n_coeffs])),
                             dt[15:100]),
            ff.PulseSequence(list(zip(c_opers,
                                      [c[100:245] for c in c_coeffs])),
                             list(zip(n_opers,
                                      [n[100:245] for n in n_coeffs])),
                             dt[100:245]),
            ff.PulseSequence(list(zip(c_opers, [c[245:] for c in c_coeffs])),
                             list(zip(n_opers, [n[245:] for n in n_coeffs])),
                             dt[245:])
        ]

        cnot_concatenated = ff.concatenate(cnot_sliced)

        self.assertEqual(cnot_whole, cnot_concatenated)
        self.assertEqual(cnot_whole._filter_function,
                         cnot_concatenated._filter_function)

        cnot_concatenated.cache_filter_function(omega)
        cnot_whole.cache_filter_function(omega)

        self.assertEqual(cnot_whole, cnot_concatenated)
        self.assertArrayAlmostEqual(cnot_whole._filter_function,
                                    cnot_concatenated._filter_function)

        # Test concatenation if different child sequences have a filter
        # function already calculated
        cnot_sliced = [
            ff.PulseSequence(list(zip(c_opers, [c[:100] for c in c_coeffs])),
                             list(zip(n_opers, [n[:100] for n in n_coeffs])),
                             dt[:100]),
            ff.PulseSequence(list(zip(c_opers,
                                      [c[100:150] for c in c_coeffs])),
                             list(zip(n_opers,
                                      [n[100:150] for n in n_coeffs])),
                             dt[100:150]),
            ff.PulseSequence(list(zip(c_opers, [c[150:] for c in c_coeffs])),
                             list(zip(n_opers, [n[150:] for n in n_coeffs])),
                             dt[150:])
        ]

        atol = 1e-12
        rtol = 1e-10

        for slice_1, slice_2 in product(cnot_sliced, cnot_sliced):

            for cnot_slice in cnot_sliced:
                cnot_slice.cleanup('all')

            slice_1.cache_filter_function(omega)
            slice_2.cache_filter_function(omega)

            cnot_concatenated = ff.concatenate(cnot_sliced)

            self.assertArrayEqual(cnot_whole.omega, cnot_concatenated.omega)
            self.assertArrayAlmostEqual(cnot_whole._filter_function,
                                        cnot_concatenated._filter_function,
                                        rtol, atol)
Example #15
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()])