def test_multi_qubit_error_transfer_matrix(self): """Test the calculation of the multi-qubit transfer matrix""" n_cops = 4 n_nops = 2 for d, n_dt in zip(rng.randint(3, 9, 10), rng.randint(1, 11, 10)): f, n = np.modf(np.log2(d)) btype = 'Pauli' if f == 0.0 else 'GGM' pulse = testutil.rand_pulse_sequence(d, n_dt, n_cops, n_nops, btype) omega = util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() S = 1e-8 / omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = 1 - np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), atol=1e-4) S = np.outer(1e-7 * (np.arange(n_nops) + 1), 400 / (omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = 1 - np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), atol=1e-4) S = np.tile(1e-8 / abs(omega)**2, (n_nops, n_nops, 1)).astype(complex) S[np.triu_indices(n_nops, 1)].imag = 1e-10 * omega S[np.tril_indices(n_nops, -1)].imag = \ - S[np.triu_indices(n_nops, 1)].imag U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = 1 - np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), atol=1e-4)
def test_caching(self): """Make sure calculation works with or without cached intermediates.""" for d, n_dt in zip(testutil.rng.integers(2, 5, 5), testutil.rng.integers(2, 8, 5)): pulse = testutil.rand_pulse_sequence(d, n_dt) omega = ff.util.get_sample_frequencies(pulse, n_samples=27) spect = 1 / omega # Cache control matrix but not intermediates pulse.cache_control_matrix(omega, cache_intermediates=False) infid_nocache = ff.infidelity(pulse, spect, omega, cache_intermediates=False) infid_cache = ff.infidelity(pulse, spect, omega, cache_intermediates=True) self.assertArrayAlmostEqual(infid_nocache, infid_cache) cm_nocache = ff.gradient.calculate_derivative_of_control_matrix_from_scratch( omega, pulse.propagators, pulse.eigvals, pulse.eigvecs, pulse.basis, pulse.t, pulse.dt, pulse.n_opers, pulse.n_coeffs, pulse.c_opers, intermediates=dict()) pulse.cleanup('frequency dependent') pulse.cache_control_matrix(omega, cache_intermediates=True) cm_cache = ff.gradient.calculate_derivative_of_control_matrix_from_scratch( omega, pulse.propagators, pulse.eigvals, pulse.eigvecs, pulse.basis, pulse.t, pulse.dt, pulse.n_opers, pulse.n_coeffs, pulse.c_opers, intermediates=pulse._intermediates) self.assertArrayAlmostEqual(cm_nocache, cm_cache)
def test_multi_qubit_error_transfer_matrix(self): """Test the calculation of the multi-qubit transfer matrix""" n_cops = 4 n_nops = 2 for d, n_dt in zip(rng.randint(3, 9, 10), rng.randint(1, 11, 10)): f, n = np.modf(np.log2(d)) btype = 'Pauli' if f == 0.0 else 'GGM' pulse = testutil.rand_pulse_sequence(d, n_dt, n_cops, n_nops, btype) omega = ff.util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() S = 1e-2 / omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity) S = np.outer(1e-2 * (np.arange(n_nops) + 1), 400 / (omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity) S = np.einsum('i,j,o->ijo', 1e-2 * (np.arange(n_nops) + 1), 1e-2 * (np.arange(n_nops) + 1), 400 / (omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity)
def test_plot_infidelity_convergence(self): def spectrum(omega): return omega**0 n, infids = ff.infidelity(simple_pulse, spectrum, {}, test_convergence=True) fig, ax = plotting.plot_infidelity_convergence(n, infids)
def test_infidelity_convergence(self): omega = { 'omega_IR': 0, 'omega_UV': 2, 'spacing': 'linear', 'n_min': 10, 'n_max': 50, 'n_points': 4 } def spectrum(omega): return omega**0 simple_pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) complicated_pulse = testutil.rand_pulse_sequence(2, 100, 3, 3) with self.assertRaises(TypeError): n, infids = ff.infidelity(simple_pulse, spectrum, [], test_convergence=True) with self.assertRaises(TypeError): n, infids = ff.infidelity(simple_pulse, [1, 2, 3], dict(spacing='foobar'), test_convergence=True) with self.assertRaises(ValueError): n, infids = ff.infidelity(simple_pulse, spectrum, dict(spacing='foobar'), test_convergence=True) # Test with default args n, infids = ff.infidelity(simple_pulse, spectrum, {}, test_convergence=True) # Test with non-default args identifiers = rng.choice(complicated_pulse.n_oper_identifiers, rng.randint(1, 4)) n, infids = ff.infidelity(complicated_pulse, spectrum, omega, test_convergence=True, n_oper_identifiers=identifiers)
def test_filter_functions(self): # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)], skip_check=True, btype='Pauli') c_opers = ff_testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = ff_testutil.c_coeffs, ff_testutil.n_coeffs dt = ff_testutil.dt infid_MC = ff_testutil.cnot_infid_fast A = ff_testutil.A identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list( zip(c_opers[:3] + [c_opers[3] + 7 * c_opers[4] - c_opers[5]], c_coeffs[:3] + [c_coeffs[3]], identifiers[:4])) H_n = list(zip(n_opers[:3], n_coeffs[:3], identifiers[:3])) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) T = dt.sum() omega = np.logspace(np.log10(1 / T), 2, 125) S_t, omega_t = ff.util.symmetrize_spectrum(A[0] / omega**0.0, omega) infid, xi = ff.infidelity(cnot, S_t, omega_t, identifiers[:3], return_smallness=True) # infid scaled with d = 6, but we actually have d = 4 infid *= 1.5 self.assertLessEqual(np.abs(1 - (infid.sum() / infid_MC[0])), .4) self.assertLessEqual(infid.sum(), xi**2 / 4) time_slot_comp_closed = SchroedingerSolver( h_drift=[OPERATORS['h_drift']] * len(dt), h_ctrl=OPERATORS['h_ctrl'], initial_state=OPERATORS['initial_state'], tau=list(dt), calculate_propagator_derivatives=True, exponential_method='spectral', is_skew_hermitian=True, transfer_function=id_tf, amplitude_function=exp_amp_func, filter_function_h_n=H_n, filter_function_basis=qubit_subspace_basis) time_slot_comp_closed.set_optimization_parameters(eps.T) ff_infid = OperatorFilterFunctionInfidelity( solver=time_slot_comp_closed, noise_power_spec_density=S_t, omega=omega_t) print(ff_infid.grad()) np.testing.assert_array_almost_equal(infid, ff_infid.costs() * 1.5)
def test_infidelity_cnot(self): """Compare infidelity to monte carlo results""" c_opers = testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = testutil.c_coeffs, testutil.n_coeffs dt = testutil.dt infid_MC = testutil.cnot_infid_fast A = testutil.A # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)], skip_check=True, btype='Pauli') complete_basis = ff.Basis(qubit_subspace_basis, traceless=False, btype='Pauli') identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list(zip(c_opers, c_coeffs, identifiers)) H_n = list(zip(n_opers, n_coeffs, identifiers)) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) cnot_full = ff.PulseSequence(H_c, H_n, dt, basis=complete_basis) # Manually set dimension of pulse as the dimension of the computational # subspace cnot.d = 4 T = dt.sum() for f_min, A, alpha, MC, rtol in zip((1 / T, 1e-2 / T), A, (0.0, 0.7), infid_MC, (0.04, 0.02)): omega = np.geomspace(f_min, 1e2, 250) * 2 * np.pi S_t, omega_t = ff.util.symmetrize_spectrum(A / omega**alpha, omega) infid, xi = ff.infidelity(cnot, S_t, omega_t, identifiers[:3], return_smallness=True) U = ff.error_transfer_matrix(cnot_full, S_t, omega_t, identifiers[:3]) infid_P = np.trace(U[:, :16, :16], axis1=1, axis2=2).real / 4**2 print(np.abs(1 - (infid.sum() / MC))) print(np.abs(1 - (infid_P.sum() / MC))) self.assertLessEqual(np.abs(1 - (infid.sum() / MC)), rtol) self.assertLessEqual(np.abs(1 - (infid_P.sum() / MC)), rtol) self.assertLessEqual(infid.sum(), xi**2 / 4)
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_infidelity_cnot(self): """Compare infidelity to monte carlo results""" c_opers = testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = testutil.c_coeffs, testutil.n_coeffs dt = testutil.dt infid_MC = testutil.cnot_infid_fast A = testutil.A # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)[1:]], skip_check=True, btype='Pauli') complete_basis = ff.Basis(qubit_subspace_basis, traceless=False, btype='Pauli') identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list(zip(c_opers, c_coeffs, identifiers)) H_n = list(zip(n_opers, n_coeffs, identifiers)) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) cnot_full = ff.PulseSequence(H_c, H_n, dt, basis=complete_basis) # Manually set dimension of pulse as the dimension of the computational # subspace cnot.d = 4 f_min = 1 / cnot.tau omega = np.geomspace(f_min, 1e2, 250) for A, alpha, MC in zip(A, (0.0, 0.7), infid_MC): S = A / omega**alpha infid, xi = ff.infidelity(cnot, S, omega, identifiers[:3], return_smallness=True) K = numeric.calculate_cumulant_function(cnot_full, S, omega, identifiers[:3]) infid_P = -np.trace(K[:, :16, :16], axis1=1, axis2=2).real / 4**2 self.assertLessEqual(np.abs(1 - (infid.sum() / MC)), 0.10) self.assertLessEqual(np.abs(1 - (infid_P.sum() / MC)), 0.10) self.assertLessEqual(infid.sum(), xi**2 / 4)
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)
def test_infidelity(self): """Benchmark infidelity results against previous version's results""" rng = np.random.default_rng(seed=123456789) spectra = [ lambda S0, omega: S0 * abs(omega)**0, lambda S0, omega: S0 / abs(omega)**0.7, lambda S0, omega: S0 * np.exp(-abs(omega)), # different spectra for different n_opers lambda S0, omega: np.array( [S0 * abs(omega)**0, S0 / abs(omega)**0.7]), # cross-correlated spectra lambda S0, omega: np.array([[ S0 / abs(omega)**0.7, S0 / (1 + omega**2) + 1j * S0 * omega ], [S0 / (1 + omega**2) - 1j * S0 * omega, S0 / abs(omega)**0.7]]) ] ref_infids = ([[2.1571674053883583, 2.1235628100639845], [1.7951695420688032, 2.919850951578396], [0.4327173760925169, 0.817672660809546], [2.1571674053883583, 2.919850951578396], [[1.7951695420688032, -1.1595479985471822], [-1.1595479985471822, 2.919850951578396]], [0.8247284959004152, 2.495561429509174], [0.854760904366362, 3.781670732974073], [0.24181791977082442, 1.122626106375816], [0.8247284959004152, 3.781670732974073], [[0.854760904366362, -0.16574972846239408], [-0.16574972846239408, 3.781670732974073]], [2.9464977186365267, 0.8622319594213088], [2.8391133843027525, 0.678843575761492], [0.813728718501677, 0.16950739577216872], [2.9464977186365267, 0.678843575761492], [[2.8391133843027525, 0.2725782717379744], [0.2725782717379744, 0.678843575761492]]]) count = 0 for d in (2, 3, 4): pulse = testutil.rand_pulse_sequence(d, 10, 2, 3, local_rng=rng) pulse.n_oper_identifiers = np.array(['B_0', 'B_2']) omega = np.geomspace(0.1, 10, 51) S0 = np.abs(rng.standard_normal()) for spec in spectra: S = spec(S0, omega) infids = ff.infidelity(pulse, S, omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(infids, ref_infids[count], atol=1e-12) if S.ndim == 3: # Diagonal of the infidelity matrix should correspond to # uncorrelated terms uncorrelated_infids = ff.infidelity( pulse, S[range(2), range(2)], omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(np.diag(infids), uncorrelated_infids) # Infidelity matrix should be hermitian self.assertArrayEqual(infids, infids.conj().T) count += 1 # Check raises with self.assertRaises(TypeError): # spectrum not callable ff.infidelity(pulse, 2, omega, test_convergence=True) with self.assertRaises(TypeError): # omega not dict ff.infidelity(pulse, lambda x: x, 2, test_convergence=True) with self.assertRaises(ValueError): # omega['spacing'] not in ('linear', 'log') ff.infidelity(pulse, lambda x: x, {'spacing': 2}, test_convergence=True) with self.assertRaises(ValueError): # which not total or correlation ff.infidelity(pulse, spectra[0](S0, omega), omega, which=2) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[0](S0, omega)[:10], omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[3](S0, omega), omega, n_oper_identifiers=['B_0', 'B_1', 'B_2']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[4](S0, omega)[:, [0]], omega, n_oper_identifiers=['B_0']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, rng.standard_normal((2, 3, 4, len(omega))), omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(testutil.rand_pulse_sequence(2, 3, n_nops=2), rng.standard_normal((3, 2, 2, len(omega))), omega) with self.assertRaises(ValueError): # S cross-correlated but not hermitian ff.infidelity(testutil.rand_pulse_sequence(2, 3, n_nops=2), rng.standard_normal((2, 2, len(omega))), omega) with self.assertRaises(ValueError): # S 'cross-correlated' but not hermitian ff.infidelity(pulse, (1 + 1j) * rng.standard_normal( (1, 1, len(omega))), omega) with self.assertRaises(NotImplementedError): # smallness parameter for correlated noise source ff.infidelity(pulse, spectra[4](S0, omega), omega, n_oper_identifiers=['B_0', 'B_2'], return_smallness=True)
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)))
# %% Run simulation # We use the 1/f^0.7 spectrum from Dial et al (2013) and a white spectrum # leading to the same average clifford infidelity def spectrum(omega, alpha): eps0 = 2.7241e-4 return 4e-11 * (2 * np.pi * 1e-3 / omega)**alpha / eps0**2 alpha = (0.0, 0.7) # Scale noise such that average clifford infidelity is the same for all pulse types and alpha clifford_infids = { p: { a: np.array([ ff.infidelity(c, spectrum(omega, a), omega) for c in cliffords[p] ]) for a in alpha } for p in pulse_types } noise_scaling_factor = { p: { a: clifford_infids['optimized'][0.7].sum(1).mean(0) / clifford_infids[p][a].sum(1).mean(0) for a in alpha } for p in pulse_types }
def test_single_qubit_error_transfer_matrix(self): """Test the calculation of the single-qubit transfer matrix""" d = 2 for n_dt in rng.randint(1, 11, 10): pulse = testutil.rand_pulse_sequence(d, n_dt, 3, 2, btype='Pauli') 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 # Assert fidelity is same as computed by infidelity() S = 1e-8 / omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) # Calculate on foot (multi-qubit way) Gamma = numeric.calculate_decay_amplitudes(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(0)) U_from_K = ff.error_transfer_matrix(cumulant_function=K) 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(Up, U) self.assertArrayAlmostEqual(I_fidelity, I_decayamps) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) self.assertArrayAlmostEqual(U_from_K, U_onfoot) # Different spectra for each noise oper S = np.outer(1e-6 * np.arange(1, 3), 400 / (omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) Gamma = numeric.calculate_decay_amplitudes(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(0)) U_from_K = ff.error_transfer_matrix(cumulant_function=K) 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(Up, U) self.assertArrayAlmostEqual(I_fidelity, I_decayamps) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) self.assertArrayAlmostEqual(U_from_K, U_onfoot) # Cross-correlated spectra are complex, real part symmetric and # imaginary part antisymmetric S = 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)]]) U = ff.error_transfer_matrix(pulse, S, omega) Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) Gamma = numeric.calculate_decay_amplitudes(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((0, 1))) U_from_K = ff.error_transfer_matrix(cumulant_function=K) 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(Up, U) self.assertArrayAlmostEqual(I_fidelity, I_decayamps) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) self.assertArrayAlmostEqual(U_from_K, U_onfoot)
def test_infidelity(self): """Benchmark infidelity results against previous version's results""" rng.seed(123456789) spectra = [ lambda S0, omega: S0 * abs(omega)**0, lambda S0, omega: S0 / abs(omega)**0.7, lambda S0, omega: S0 * np.exp(-abs(omega)), # different spectra for different n_opers lambda S0, omega: np.array( [S0 * abs(omega)**0, S0 / abs(omega)**0.7]), # cross-correlated spectra lambda S0, omega: np.array([[ S0 / abs(omega)**0.7, S0 / (1 + omega**2) + 1j * S0 * omega ], [S0 / (1 + omega**2) - 1j * S0 * omega, S0 / abs(omega)**0.7]]) ] ref_infids = ([[0.415494970094, 0.89587362496], [0.493004378474, 0.812378971328], [0.133466914361, 0.197411969384], [0.415494970094, 0.812378971328], [[0.493004378474, 0.435140425045], [0.435140425045, 0.812378971328]], [3.62995021962, 2.938710386281], [2.302617869945, 2.6187737025], [0.506821680978, 0.695495602872], [3.62995021962, 2.6187737025], [[2.302617869945, 0.58515469294], [0.58515469294, 2.6187737025]], [2.822636459567, 1.205901937127], [1.63758822101, 1.236844976323], [0.324175447082, 0.329789052239], [2.822636459567, 1.236844976323], [[1.63758822101, 0.72007826813], [0.72007826813, 1.236844976323]]]) count = 0 for d in (2, 3, 4): pulse = testutil.rand_pulse_sequence(d, 10, 2, 3) pulse.n_oper_identifiers = np.array(['B_0', 'B_2']) omega = np.geomspace(0.1, 10, 51) S0 = np.abs(rng.standard_normal()) for spec in spectra: S = spec(S0, omega) infids = ff.infidelity(pulse, S, omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(infids, ref_infids[count], atol=1e-12) if S.ndim == 3: # Diagonal of the infidelity matrix should correspond to # uncorrelated terms uncorrelated_infids = ff.infidelity( pulse, S[range(2), range(2)], omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(np.diag(infids), uncorrelated_infids) # Infidelity matrix should be hermitian self.assertArrayEqual(infids, infids.conj().T) count += 1 # Check raises with self.assertRaises(TypeError): # spectrum not callable ff.infidelity(pulse, 2, omega, test_convergence=True) with self.assertRaises(TypeError): # omega not dict ff.infidelity(pulse, lambda x: x, 2, test_convergence=True) with self.assertRaises(ValueError): # omega['spacing'] not in ('linear', 'log') ff.infidelity(pulse, lambda x: x, {'spacing': 2}, test_convergence=True) with self.assertRaises(ValueError): # which not total or correlation ff.infidelity(pulse, spectra[0](S0, omega), omega, which=2) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[0](S0, omega)[:10], omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[3](S0, omega), omega, n_oper_identifiers=['B_0', 'B_1', 'B_2']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[4](S0, omega)[:, [0]], omega, n_oper_identifiers=['B_0']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, rng.standard_normal((2, 3, 4, len(omega))), omega) with self.assertRaises(NotImplementedError): # smallness parameter for correlated noise source ff.infidelity(pulse, spectra[4](S0, omega), omega, n_oper_identifiers=['B_0', 'B_2'], return_smallness=True)
alpha = (0.0, 0.7) # Scaling factor for the noise so that alpha = 0 and alpha = 0.7 give the same # average clifford fidelity noise_scaling_factor = {0.0: 0.4415924985735799, 0.7: 1} state_infidelities = {} clifford_infidelities = {} spectra = {} for i, a in enumerate(alpha): S0 = 4e-11 * (2 * np.pi * 1e-3)**a / eps0**2 * noise_scaling_factor[a] spectra[a] = S0 / omega**a # Need to calculate with two-sided spectra clifford_infidelities[a] = [ ff.infidelity(C, spectra[a], omega).sum() for C in cliffords ] state_infidelities, exec_times = run_randomized_benchmarking( N_G, N_l, m_min, m_max, alpha, spectra, omega) # %% Plot results fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(8, 3)) fidelities = {a: 1 - infid for a, infid in state_infidelities.items()} for i, a in enumerate(alpha): means = np.mean(fidelities[a], axis=1) stds = np.std(fidelities[a], axis=1) popt, pcov = optimize.curve_fit(fitfun,
def test_single_qubit_error_transfer_matrix(self): """Test the calculation of the single-qubit transfer matrix""" d = 2 for n_dt in rng.randint(1, 11, 10): pulse = testutil.rand_pulse_sequence(d, n_dt, 3, 2, btype='Pauli') omega = ff.util.get_sample_frequencies(pulse, n_samples=51) n_oper_identifiers = pulse.n_oper_identifiers traces = pulse.basis.four_element_traces.todense() # Single spectrum # Assert fidelity is same as computed by infidelity() S = 1e-2 / omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity) # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same u_kl = numeric.calculate_error_vector_correlation_functions( pulse, S, omega, n_oper_identifiers) U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces) / 2 + np.einsum('...kl,klji->...ij', u_kl, traces) / 2 - np.einsum('...kl,kilj->...ij', u_kl, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) # Different spectra for each noise oper S = np.outer(1e-2 * np.arange(1, 3), 400 / (omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity) # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same u_kl = numeric.calculate_error_vector_correlation_functions( pulse, S, omega, n_oper_identifiers) U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces) / 2 + np.einsum('...kl,klji->...ij', u_kl, traces) / 2 - np.einsum('...kl,kilj->...ij', u_kl, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) # Cross-correlated spectra S = np.einsum('i,j,o->ijo', 1e-2 * np.arange(1, 3), 1e-2 * np.arange(1, 3), 400 / (omega**2 + 400), dtype=complex) # Cross spectra are complex S[0, 1] *= 1 + 1j S[1, 0] *= 1 - 1j U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) I_transfer = np.einsum('...ii', U) / d**2 self.assertArrayAlmostEqual(I_transfer, I_fidelity) # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same u_kl = numeric.calculate_error_vector_correlation_functions( pulse, S, omega, n_oper_identifiers) U_multi = np.zeros_like(U) U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces) / 2 + np.einsum('...kl,klji->...ij', u_kl, traces) / 2 - np.einsum('...kl,kilj->...ij', u_kl, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-16)
def test_infidelity(self): """Benchmark infidelity results against previous version's results""" rng.seed(123456789) spectra = [ lambda S0, omega: S0 * omega**0, lambda S0, omega: S0 / omega**0.7, lambda S0, omega: S0 * np.exp(-omega), # different spectra for different n_opers lambda S0, omega: np.array([S0 * omega**0, S0 / omega**0.7]), # cross-correlated spectra lambda S0, omega: np.array([[ S0 / omega**0.7, (1 + 1j) * S0 * np.exp(-omega) ], [(1 - 1j) * S0 * np.exp(-omega), S0 / omega**0.7]]) ] ref_infids = ([0.448468950307, 0.941871479562], [0.65826575772, 1.042914346335], [0.163303005479, 0.239032549377], [0.448468950307, 1.042914346335], [[0.65826575772, 0.069510589685 + 0.069510589685j], [0.069510589685 - 0.069510589685j, 1.042914346335]], [3.687399348243, 3.034914820757], [2.590545568435, 3.10093804628], [0.55880380219, 0.782544974968 ], [3.687399348243, 3.10093804628], [[2.590545568435, -0.114514760108 - 0.114514760108j], [-0.114514760108 + 0.114514760108j, 3.10093804628]], [2.864567451344, 1.270260393902], [ 1.847740998731, 1.559401345443 ], [0.362116177417, 0.388022992097], [2.864567451344, 1.559401345443], [[1.847740998731, 0.088373663409 + 0.088373663409j], [0.088373663409 - 0.088373663409j, 1.559401345443]]) count = 0 for d in (2, 3, 4): pulse = testutil.rand_pulse_sequence(d, 10, 2, 3) pulse.n_oper_identifiers = np.array(['B_0', 'B_2']) omega = np.geomspace(0.1, 10, 51) S0 = np.abs(rng.standard_normal()) for spec in spectra: S, omega_t = ff.util.symmetrize_spectrum( spec(S0, omega), omega) infids = ff.infidelity(pulse, S, omega_t, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(infids, ref_infids[count], atol=1e-12) if S.ndim == 3: # Diagonal of the infidelity matrix should correspond to # uncorrelated terms uncorrelated_infids = ff.infidelity( pulse, S[range(2), range(2)], omega_t, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(np.diag(infids), uncorrelated_infids) # Infidelity matrix should be hermitian self.assertArrayEqual(infids, infids.conj().T) count += 1 # Check raises with self.assertRaises(TypeError): # spectrum not callable ff.infidelity(pulse, 2, omega_t, test_convergence=True) with self.assertRaises(TypeError): # omega not dict ff.infidelity(pulse, lambda x: x, 2, test_convergence=True) with self.assertRaises(ValueError): # omega['spacing'] not in ('linear', 'log') ff.infidelity(pulse, lambda x: x, {'spacing': 2}, test_convergence=True) with self.assertRaises(ValueError): # which not total or correlation ff.infidelity(pulse, spectra[0](S0, omega_t), omega, which=2) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[0](S0, omega_t)[:10], omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[3](S0, omega), omega, n_oper_identifiers=['B_0', 'B_1', 'B_2']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[4](S0, omega)[:, [0]], omega, n_oper_identifiers=['B_0']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, rng.standard_normal((2, 3, 4, len(omega))), omega) with self.assertRaises(NotImplementedError): # smallness parameter for correlated noise source ff.infidelity(pulse, spectra[4](S0, omega), omega, n_oper_identifiers=['B_0', 'B_2'], return_smallness=True)
eps0 = 2.7241e-4 # Scaling factor for the noise so that alpha = 0 and alpha = 0.7 have the same # power noise_scaling_factor = {0.0: 0.4415924985735799, 0.7: 1} state_infidelities = {} clifford_infidelities = {} for i, alpha in enumerate((0.0, 0.7)): S0 = 1e-13 * (2 * np.pi * 1e-3)**alpha / eps0**2 * noise_scaling_factor[alpha] S = S0 / omega**alpha # Need to calculate with two-sided spectra clifford_infidelities[alpha] = [ ff.infidelity(C, *util.symmetrize_spectrum(S, omega)).sum() for C in cliffords ] print('=============================================') print(f'\t\talpha = {alpha}') print('=============================================') state_infidelities[alpha], exec_times = run_randomized_benchmarking( N_G, N_l, m_min, m_max, omega) # %% Plot results fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(8, 3)) fidelities = {alpha: 1 - infid for alpha, infid in state_infidelities.items()} for i, alpha in enumerate((0.0, 0.7)):