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