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_liouville_to_choi(self): """Test converting Liouville superops to choi matrices.""" for d in rng.integers(2, 9, (15, )): # unitary channel U = testutil.rand_unit(d, rng.integers(1, 8)).squeeze() n = np.log2(d) if n % 1 == 0: basis = ff.Basis.pauli(int(n)) else: basis = ff.Basis.ggm(d) U_sup = superoperator.liouville_representation(U, basis) choi = superoperator.liouville_to_choi(U_sup, basis).view(ff.Basis) self.assertTrue(choi.isherm) self.assertArrayAlmostEqual(np.einsum('...ii', choi), d) pulse = testutil.rand_pulse_sequence(d, 1) omega = ff.util.get_sample_frequencies(pulse) S = 1 / abs(omega)**2 U_sup = ff.error_transfer_matrix(pulse, S, omega) choi = superoperator.liouville_to_choi(U_sup, basis).view(ff.Basis) self.assertTrue(choi.isherm) self.assertAlmostEqual(np.einsum('ii', choi), d)
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_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_error_transfer_matrix(self): """Test raises of numeric.error_transfer_matrix.""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) omega = testutil.rng.randn(43) spectrum = np.ones_like(omega) with self.assertRaises(ValueError): ff.error_transfer_matrix(pulse, spectrum) with self.assertRaises(TypeError): ff.error_transfer_matrix(cumulant_function=[1, 2, 3]) with self.assertRaises(ValueError): ff.error_transfer_matrix(cumulant_function=testutil.rng.randn(2, 3, 4))
def test_liouville_is_CP(self): def partial_transpose(A): d = A.shape[-1] sqd = int(np.sqrt(d)) return A.reshape(-1, sqd, sqd, sqd, sqd).swapaxes(-1, -3).reshape(A.shape) # Partial transpose map should be non-CP basis = ff.Basis.pauli(2) Phi = ff.basis.expand(partial_transpose(basis), basis).T CP = superoperator.liouville_is_CP(Phi, basis) self.assertFalse(CP) for d in rng.integers(2, 9, (15, )): # unitary channel U = testutil.rand_unit(d, rng.integers(1, 8)).squeeze() n = np.log2(d) if n % 1 == 0: basis = ff.Basis.pauli(int(n)) else: basis = ff.Basis.ggm(d) U_sup = superoperator.liouville_representation(U, basis) CP, (D, V) = superoperator.liouville_is_CP(U_sup, basis, True) _CP = superoperator.liouville_is_CP(U_sup, basis, False) self.assertArrayEqual(CP, _CP) self.assertTrue(np.all(CP)) if U_sup.ndim == 2: self.assertIsInstance(CP, (bool, np.bool8)) else: self.assertEqual(CP.shape[0], U_sup.shape[0]) # Only one nonzero eigenvalue self.assertArrayAlmostEqual(D[..., :-1], 0, atol=basis._atol) pulse = testutil.rand_pulse_sequence(d, 1) omega = ff.util.get_sample_frequencies(pulse) S = 1 / abs(omega)**2 U_sup = ff.error_transfer_matrix(pulse, S, omega) CP = superoperator.liouville_is_CP(U_sup, pulse.basis) self.assertTrue(np.all(CP)) self.assertIsInstance(CP, (bool, np.bool8))
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_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_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_plot_error_transfer_matrix(self): omega = ff.util.get_sample_frequencies(simple_pulse) S = 1e-4 * np.sin(omega) / omega # Test calling with pulse, spectrum, omega fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, colorscale='linear') fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, fig=fig) fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, grid=grid) # Test calling with precomputed transfer matrix U = ff.error_transfer_matrix(simple_pulse, S, omega) fig, grid = plotting.plot_error_transfer_matrix(U=U) # Test calling with precomputed transfer matrix and pulse U = ff.error_transfer_matrix(simple_pulse, S, omega) fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, U=U) # Test calling with precomputed transfer matrix of ndim == 2 U = ff.error_transfer_matrix(simple_pulse, S, omega) fig, grid = plotting.plot_error_transfer_matrix(U=U[0]) # Log colorscale fig, grid = plotting.plot_error_transfer_matrix(U=U, colorscale='log') # Non-default args n_oper_inds = sample(range(len(complicated_pulse.n_opers)), rng.randint(2, 4)) n_oper_identifiers = complicated_pulse.n_oper_identifiers[n_oper_inds] basis_labels = [] for i in range(4): basis_labels.append(string.ascii_uppercase[rng.randint(0, 26)]) omega = ff.util.get_sample_frequencies(complicated_pulse, n_samples=50, spacing='log') S = np.exp(-omega**2) U = ff.error_transfer_matrix(complicated_pulse, S, omega) fig, grid = plotting.plot_error_transfer_matrix( complicated_pulse, S=S, omega=omega, n_oper_identifiers=n_oper_identifiers, basis_labels=basis_labels, basis_labelsize=4, linthresh=1e-4, cmap=plt.cm.jet) fig, grid = plotting.plot_error_transfer_matrix( U=U[n_oper_inds], n_oper_identifiers=n_oper_identifiers, basis_labels=basis_labels, basis_labelsize=4, linthresh=1e-4, cmap=plt.cm.jet) # neither U nor all of pulse, S, omega given with self.assertRaises(ValueError): plotting.plot_error_transfer_matrix(complicated_pulse, S) # invalid identifiers with self.assertRaises(ValueError): plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, n_oper_identifiers=['foo']) # number of basis_labels not correct with self.assertRaises(ValueError): plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, basis_labels=basis_labels[:2]) # grid too small with self.assertRaises(ValueError): plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, grid=grid[:1]) # Test various keyword args for matplotlib for the two-qubit pulse S = np.tile(S, (6, 6, 1)) grid_kw = {'axes_pad': 0.1} imshow_kw = {'interpolation': 'bilinear'} figure_kw = {'num': 1} fig, ax = plotting.plot_error_transfer_matrix(two_qubit_pulse, S, omega, imshow_kw=imshow_kw, grid_kw=grid_kw, **figure_kw) plt.close('all')
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)