def test_diagonalization_cnot(self): """CNOT""" cnot_mat = np.block([[util.paulis[0], np.zeros((2, 2))], [np.zeros((2, 2)), util.paulis[1]]]) subspace_c_opers = testutil.subspace_opers subspace_n_opers = subspace_c_opers c_opers = testutil.opers n_opers = c_opers c_coeffs, n_coeffs = testutil.c_coeffs, testutil.n_coeffs dt = testutil.dt subspace = testutil.subspace cnot_subspace = ff.PulseSequence(list(zip(subspace_c_opers, c_coeffs)), list(zip(subspace_n_opers, n_coeffs)), dt) cnot = ff.PulseSequence(list(zip(c_opers, c_coeffs)), list(zip(n_opers, n_coeffs)), dt) cnot.diagonalize() cnot_subspace.diagonalize() phase_eq = ff.util.oper_equiv(cnot_subspace.total_propagator[1:5, 1:5], cnot_mat, eps=1e-9) self.assertTrue(phase_eq[0]) phase_eq = ff.util.oper_equiv( cnot.total_propagator[np.ix_(*subspace)][1:5, 1:5], cnot_mat, eps=1e-9) self.assertTrue(phase_eq[0])
def test_SE(self): """Spin echo""" tau = np.pi tau_pi = 1e-8 n = 1 H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cpmg') H_n = [[util.paulis[3] / 2, np.ones_like(dt)]] SE_pulse = ff.PulseSequence(H_c, H_n, dt) omega = util.get_sample_frequencies(SE_pulse, 100, spacing='linear') # Comparison to filter function defined with omega**2 F = SE_pulse.get_filter_function(omega)[0, 0] * omega**2 self.assertArrayAlmostEqual(F, analytic.SE(omega * tau), atol=1e-10) # Test again with a factor of one between the noise operators and # coefficients r = rng.standard_normal() H_n = [[util.paulis[3] / 2 * r, np.ones_like(dt) / r]] SE_pulse = ff.PulseSequence(H_c, H_n, dt) # Comparison to filter function defined with omega**2 F = SE_pulse.get_filter_function(omega)[0, 0] * omega**2 self.assertArrayAlmostEqual(F, analytic.SE(omega * tau), atol=1e-10)
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 test_pulse_sequence_constructor(self): X, Y, Z = qutip.sigmax(), qutip.sigmay(), qutip.sigmaz() pulse_1 = ff.PulseSequence( [[X, [1, 2, 3], 'X'], [util.paulis[2], [3, 4, 5], 'Y'], [Z, [5, 6, 7], 'Z']], [[util.paulis[3], [1, 2, 3], 'Z'], [Y, [3, 4, 5], 'Y'], [util.paulis[1], [5, 6, 7], 'X']], [1, 3, 5]) pulse_2 = ff.PulseSequence( [[Y, [3, 4, 5], 'Y'], [util.paulis[3], [5, 6, 7], 'Z'], [util.paulis[1], [1, 2, 3], 'X']], [[X, [5, 6, 7], 'X'], [Z, [1, 2, 3], 'Z'], [util.paulis[2], [3, 4, 5], 'Y']], [1, 3, 5]) self.assertEqual(pulse_1, pulse_2)
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_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_concatenate_with_filter_function_SE12(self): """Concatenate two Spin Echos with both having a filter function.""" tau = 10 tau_pi = 1e-4 omega = np.logspace(-1, 2, 500) 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_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 = [[util.paulis[3], np.ones_like(dt_CPMG)]] CPMG = ff.PulseSequence(H_c_CPMG, H_n_CPMG, dt_CPMG) SE_1.cache_filter_function(omega) SE_2.cache_filter_function(omega) CPMG.cache_filter_function(omega) CPMG_concat = SE_1 @ SE_2 self.assertIsNotNone(SE_1._total_phases) self.assertIsNotNone(SE_1._total_Q) self.assertIsNotNone(SE_1._total_Q_liouville) self.assertIsNotNone(SE_2._total_phases) self.assertIsNotNone(SE_2._total_Q) self.assertIsNotNone(SE_2._total_Q_liouville) self.assertIsNotNone(CPMG._total_phases) self.assertIsNotNone(CPMG._total_Q) self.assertIsNotNone(CPMG._total_Q_liouville) self.assertIsNotNone(CPMG_concat._total_phases) self.assertIsNotNone(CPMG_concat._total_Q) self.assertIsNotNone(CPMG_concat._total_Q_liouville) self.assertEqual(CPMG_concat, CPMG) self.assertArrayAlmostEqual(CPMG_concat._F, CPMG._F, rtol=1e-11)
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 create_sing_trip_pulse_seq(eps, dbz, *args): H_c = [[sigma_z, exchange_interaction(eps[0]), 'control1'], [sigma_x, dbz * np.ones(eps.shape[1]), 'drift']] H_n = [[sigma_z, deriv_exchange_interaction(eps[0])]] dt = time_step * np.ones(n_time_steps) pulse = ff.PulseSequence(H_c, H_n, dt) return pulse
def test_FID(self): """FID""" tau = abs(rng.standard_normal()) FID_pulse = ff.PulseSequence([[util.paulis[1] / 2, [0]]], [[util.paulis[3] / 2, [1]]], [tau]) omega = util.get_sample_frequencies(FID_pulse, 50, spacing='linear') # Comparison to filter function defined with omega**2 F = FID_pulse.get_filter_function(omega).squeeze() * omega**2 self.assertArrayAlmostEqual(F, analytic.FID(omega * tau), atol=1e-10)
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 P_n_pulse(n, N: int = 4, tau: float = 1): Id = qt.qeye(2) H_c = [] H_n = [] for l in range(n+1, N+1): Z = [Id]*(N - 2) Z.insert(n-1, qt.sigmaz()) Z.insert(l-1, qt.sigmaz()) Z = qt.tensor(Z) identifier = ('I'*(n-1) + 'Z' + 'I'*(l - n - 1) + 'Z' + 'I'*(N - l)) H_c.append([Z, [-np.pi/4*2**(n - l)/tau], identifier]) H_n.append([Z/np.sqrt(Z.shape[0]), [1], identifier]) return ff.PulseSequence(H_c, H_n, [tau])
def create_pulse_sequence(u_ctrl, u_drift, *args): # hacky hacky! if len(args): d = args[0] basis = ff.Basis.ggm(d) H_c = (list( zip(basis[1:len(u_ctrl) + 1], u_ctrl, [f'c{i}' for i in range(len(u_ctrl) + 1)])) + list( zip(basis[len(u_ctrl) + 1:], u_drift, [f'd{i}' for i in range(d**2 - len(u_ctrl) + 1)]))) H_n = (list(zip(basis[1:], np.ones((d**2 - 1, u_drift.shape[1]))))) dt = np.full(u_ctrl.shape[-1], fill_value=0.32) return ff.PulseSequence(H_c, H_n, dt, basis=basis)
def T_F_pulse(N: int = 4, tau: float = 1): Id = qt.qeye(2) if N == 1: T = Id H_c = [[Id, [0], 'I']] H_n = [[T/np.sqrt(T.shape[0]), [1], 'I']] else: H_c = [] H_n = [] T = [Id]*(N - 1) T.insert(0, qt.sigmaz()) for k in range(1, N+1): H_c.append([qt.tensor(T[-k+1:] + T[:-k+1]), [np.pi/4*(1 - 2**(k - N))/tau], 'I'*(k - 1) + 'Z' + 'I'*(N - k)]) H_n.append([qt.tensor(T[-k+1:] + T[:-k+1])/np.sqrt(2**len(T)), [1], 'I'*(k - 1) + 'Z' + 'I'*(N - k)]) return ff.PulseSequence(H_c, H_n, [tau])
def test_6_pulse_CPMG(self): """6-pulse CPMG""" tau = np.pi tau_pi = 1e-9 n = 6 H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cpmg') H_n = [[util.paulis[3] / 2, np.ones_like(dt)]] CPMG_pulse = ff.PulseSequence(H_c, H_n, dt) omega = util.get_sample_frequencies(CPMG_pulse, 100, spacing='log') # Comparison to filter function defined with omega**2 F = CPMG_pulse.get_filter_function(omega)[0, 0] * omega**2 self.assertArrayAlmostEqual(F, analytic.CPMG(omega * tau, n), atol=1e-10)
def R_k_pulse(k, theta, phi, N: int = 4, tau: float = 1): Id = qt.qeye(2) if N == 1: X = qt.sigmax() Y = qt.sigmay() else: X = [Id]*(N - 1) Y = [Id]*(N - 1) X.insert(k, qt.sigmax()) Y.insert(k, qt.sigmay()) X = qt.tensor(X) Y = qt.tensor(Y) H_c = [[X, [theta/2/tau*np.cos(phi)], 'I'*k + 'X' + 'I'*(N - k - 1)], [Y, [theta/2/tau*np.sin(phi)], 'I'*k + 'Y' + 'I'*(N - k - 1)]] H_n = [[X/np.sqrt(X.shape[0]), [1], 'I'*k + 'X' + 'I'*(N - k - 1)], [Y/np.sqrt(Y.shape[0]), [1], 'I'*k + 'Y' + 'I'*(N - k - 1)]] dt = [tau] return ff.PulseSequence(H_c, H_n, dt)
def test_5_pulse_CDD(self): """5-pulse CDD""" tau = np.pi tau_pi = 1e-9 omega = np.logspace(0, 3, 100) omega = np.concatenate([-omega[::-1], omega]) n = 3 H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cdd') H_n = [[util.paulis[3] / 2, np.ones_like(dt)]] CDD_pulse = ff.PulseSequence(H_c, H_n, dt) # Comparison to filter function defined with omega**2 F = CDD_pulse.get_filter_function(omega)[0, 0] * omega**2 self.assertArrayAlmostEqual(F, analytic.CDD(omega * tau, n), atol=1e-10)
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_filter_function(self): """Test the filter function calculation and related methods""" for d, n_dt in zip(rng.randint(2, 10, (3,)), rng.randint(10, 200, (3,))): total_pulse = testutil.rand_pulse_sequence(d, n_dt, 4, 6) c_opers, c_coeffs = total_pulse.c_opers, total_pulse.c_coeffs n_opers, n_coeffs = total_pulse.n_opers, total_pulse.n_coeffs dt = total_pulse.dt total_eigvals, total_eigvecs, _ = numeric.diagonalize( np.einsum('il,ijk->ljk', c_coeffs, c_opers), total_pulse.dt ) omega = util.get_sample_frequencies(total_pulse, n_samples=100) # Try the progress bar control_matrix = total_pulse.get_control_matrix( omega, show_progressbar=True) # Check that some attributes are cached self.assertIsNotNone(total_pulse._total_phases) self.assertIsNotNone(total_pulse._total_propagator) self.assertIsNotNone(total_pulse._total_propagator_liouville) # Calculate everything 'on foot' pulses = [ff.PulseSequence(list(zip(c_opers, c_coeffs[:, i:i+1])), list(zip(n_opers, n_coeffs[:, i:i+1])), dt[i:i+1]) for i in range(n_dt)] phases = np.empty((n_dt, len(omega)), dtype=complex) L = np.empty((n_dt, d**2, d**2)) control_matrix_g = np.empty((n_dt, 6, d**2, len(omega)), dtype=complex) for g, pulse in enumerate(pulses): phases[g] = np.exp(1j*total_pulse.t[g]*omega) L[g] = ff.superoperator.liouville_representation(total_pulse.propagators[g], total_pulse.basis) control_matrix_g[g] = pulse.get_control_matrix(omega) # Check that both methods of calculating the control are the same control_matrix_from_atomic = numeric.calculate_control_matrix_from_atomic( phases, control_matrix_g, L ) control_matrix_from_scratch = numeric.calculate_control_matrix_from_scratch( eigvals=total_eigvals, eigvecs=total_eigvecs, propagators=total_pulse.propagators, omega=omega, basis=total_pulse.basis, n_opers=n_opers, n_coeffs=n_coeffs, dt=total_pulse.dt ) self.assertArrayAlmostEqual(control_matrix, control_matrix_from_scratch) # first column (identity element) always zero but susceptible to # floating point error, increase atol self.assertArrayAlmostEqual(control_matrix_from_scratch, control_matrix_from_atomic, atol=1e-13) # Check if the filter functions for autocorrelated noise are real filter_function = total_pulse.get_filter_function(omega) self.assertTrue(np.isreal( filter_function[np.eye(len(n_opers), dtype=bool)] ).all()) # Check switch between fidelity and generalized filter function F_generalized = total_pulse.get_filter_function( omega, which='generalized') F_fidelity = total_pulse.get_filter_function( omega, which='fidelity') # Check that F_fidelity is correctly reduced from F_generalized self.assertArrayAlmostEqual(F_fidelity, F_generalized.trace(axis1=2, axis2=3)) # Hit getters again to check caching functionality F_generalized = total_pulse.get_filter_function( omega, which='generalized') F_fidelity = total_pulse.get_filter_function( omega, which='fidelity') # Check that F_fidelity is correctly reduced from F_generalized self.assertArrayAlmostEqual(F_fidelity, F_generalized.trace(axis1=2, axis2=3)) # Different set of frequencies than cached F_generalized = total_pulse.get_filter_function( omega + 1, which='generalized') F_fidelity = total_pulse.get_filter_function( omega + 1, which='fidelity') # Check that F_fidelity is correctly reduced from F_generalized self.assertArrayAlmostEqual(F_fidelity, F_generalized.trace(axis1=2, axis2=3))
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_extend_with_identity(self): """Test extending a pulse to more qubits""" ID, X, Y, Z = util.paulis n_dt = 10 coeffs = rng.standard_normal((3, n_dt)) ids = ['X', 'Y', 'Z'] pulse = ff.PulseSequence( list(zip((X, Y, Z), coeffs, ids)), list(zip((X, Y, Z), np.ones((3, n_dt)), ids)), np.ones(n_dt), basis=ff.Basis.pauli(1) ) omega = util.get_sample_frequencies(pulse, spacing='log', n_samples=50) for N in rng.randint(2, 5, 4): for target in rng.randint(0, N-1, 2): pulse.cleanup('all') ext_opers = util.tensor(*np.insert(np.tile(ID, (N-1, 3, 1, 1)), target, (X, Y, Z), axis=0)) # By default, extend should add the target qubit as suffix to # identifiers ext_ids = [i + f'_{target}' for i in ids] ext_pulse = ff.PulseSequence( list(zip(ext_opers, coeffs, ext_ids)), list(zip(ext_opers, np.ones((3, n_dt)), ext_ids)), np.ones(n_dt), basis=ff.Basis.pauli(N) ) # Use custom mapping for identifiers and or labels letters = rng.choice(list(string.ascii_letters), size=(3, 5)) mapped_ids = np.array([''.join(l) for l in letters]) mapping = {i: new_id for i, new_id in zip(ids, mapped_ids)} ext_pulse_mapped_identifiers = ff.PulseSequence( list(zip(ext_opers, coeffs, mapped_ids, ext_ids)), list(zip(ext_opers, np.ones((3, n_dt)), mapped_ids, ext_ids)), np.ones(n_dt), basis=ff.Basis.pauli(N) ) ext_pulse_mapped_labels = ff.PulseSequence( list(zip(ext_opers, coeffs, ext_ids, mapped_ids)), list(zip(ext_opers, np.ones((3, n_dt)), ext_ids, mapped_ids)), np.ones(n_dt), basis=ff.Basis.pauli(N) ) ext_pulse_mapped_identifiers_labels = ff.PulseSequence( list(zip(ext_opers, coeffs, mapped_ids)), list(zip(ext_opers, np.ones((3, n_dt)), mapped_ids)), np.ones(n_dt), basis=ff.Basis.pauli(N) ) calc_filter_functionF = rng.randint(0, 2) if calc_filter_functionF: # Expect things to be cached in extended pulse if original # also was cached pulse.cache_filter_function(omega) ext_pulse.cache_filter_function(omega) test_ext_pulse = ff.extend([(pulse, target)], N, d_per_qubit=2) test_ext_pulse_mapped_identifiers = ff.extend( [(pulse, target, mapping)], N, d_per_qubit=2 ) test_ext_pulse_mapped_labels = ff.extend( [(pulse, target, None, mapping)], N, d_per_qubit=2 ) test_ext_pulse_mapped_identifiers_labels = ff.extend( [(pulse, target, mapping, mapping)], N, d_per_qubit=2 ) self.assertEqual(ext_pulse, test_ext_pulse) self.assertEqual(ext_pulse_mapped_identifiers, test_ext_pulse_mapped_identifiers) self.assertEqual(ext_pulse_mapped_labels, test_ext_pulse_mapped_labels) self.assertEqual(ext_pulse_mapped_identifiers_labels, test_ext_pulse_mapped_identifiers_labels) if calc_filter_functionF: self.assertCorrectDiagonalization(test_ext_pulse, atol=1e-14) self.assertArrayAlmostEqual(test_ext_pulse._propagators, ext_pulse._propagators, atol=1e-14) self.assertArrayAlmostEqual( test_ext_pulse._total_propagator_liouville, ext_pulse._total_propagator_liouville, atol=1e-14 ) self.assertArrayAlmostEqual( test_ext_pulse._total_propagator, ext_pulse._total_propagator, atol=1e-14 ) self.assertArrayAlmostEqual(test_ext_pulse._total_phases, ext_pulse._total_phases) self.assertArrayAlmostEqual(test_ext_pulse._control_matrix, ext_pulse._control_matrix, atol=1e-12) self.assertArrayAlmostEqual( test_ext_pulse._filter_function, ext_pulse._filter_function, atol=1e-12 ) else: self.assertIsNone(test_ext_pulse._eigvals) self.assertIsNone(test_ext_pulse._eigvecs) self.assertIsNone(test_ext_pulse._propagators) self.assertIsNone( test_ext_pulse._total_propagator_liouville) self.assertIsNone(test_ext_pulse._total_propagator) self.assertIsNone(test_ext_pulse._total_phases) self.assertIsNone(test_ext_pulse._control_matrix) self.assertIsNone(test_ext_pulse._filter_function) pulse.cleanup('all') ext_pulse.cleanup('all')
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))
c_coeffs = { gate: [J[gate][0], B[gate][0] * np.ones(n_dt[gate])] for gate in gates } n_coeffs = {gate: np.ones((3, n_dt[gate])) for gate in gates} # Add identity gate, choosing the X/2 operation on the right qubit for gate in gates: H_c['optimized'][gate] = list( zip(ff.util.paulis[[1, 3]] / 2, c_coeffs[gate], ('X', 'Z'))) H_n['optimized'][gate] = list( zip(ff.util.paulis[1:2] / 2, n_coeffs[gate], ('X', ))) # %% Set up PulseSequences pulses = { p: {g: ff.PulseSequence(H_c[p][g], H_n[p][g], dt[p][g]) for g in gates} for p in pulse_types } # %% Define some parameters m_min = 1 m_max = 151 # sequence lengths N_l = 21 lengths = np.round(np.linspace(m_min, m_max, N_l)).astype(int) # no. of random sequences per length N_G = 50 omega = np.geomspace(1e-2 / (7 * m_max * T), 1e2 / T, 301) * 2 * np.pi
def test_accuracy(self): paulis = np.array(util.paulis) I, X, Y, Z = paulis amps = rng.standard_normal(rng.randint(1, 11)) pulse = ff.PulseSequence( [[util.tensor(X, Y, Z), amps]], [[util.tensor(X, I, I), np.ones_like(amps), 'XII'], [util.tensor(I, X, I), np.ones_like(amps), 'IXI'], [util.tensor(I, I, X), np.ones_like(amps), 'IIX']], np.ones_like(amps), ff.Basis.pauli(3) ) omega = util.get_sample_frequencies(pulse, 50) pulse.cache_filter_function(omega) for _ in range(100): order = rng.permutation(range(3)) reordered_pulse = ff.PulseSequence( [[util.tensor(*paulis[1:][order]), amps]], [[util.tensor(*paulis[[1, 0, 0]][order]), np.ones_like(amps), (''.join(['XII'[o] for o in order]))], [util.tensor(*paulis[[0, 1, 0]][order]), np.ones_like(amps), (''.join(['IXI'[o] for o in order]))], [util.tensor(*paulis[[0, 0, 1]][order]), np.ones_like(amps), (''.join(['IIX'[o] for o in order]))]], np.ones_like(amps), ff.Basis.pauli(3) ) reordered_pulse.cache_filter_function(omega) remapped_pulse = ff.remap( pulse, order, oper_identifier_mapping={ 'A_0': 'A_0', 'XII': ''.join(['XII'[o] for o in order]), 'IXI': ''.join(['IXI'[o] for o in order]), 'IIX': ''.join(['IIX'[o] for o in order]) } ) self.assertEqual(reordered_pulse, remapped_pulse) self.assertArrayAlmostEqual(reordered_pulse.t, remapped_pulse.t) self.assertEqual(reordered_pulse.d, remapped_pulse.d) self.assertEqual(reordered_pulse.basis, remapped_pulse.basis) self.assertArrayAlmostEqual(reordered_pulse._omega, remapped_pulse.omega) self.assertArrayAlmostEqual(reordered_pulse._propagators, remapped_pulse._propagators, atol=1e-14) self.assertArrayAlmostEqual(reordered_pulse._total_propagator, remapped_pulse._total_propagator, atol=1e-14) self.assertArrayAlmostEqual( reordered_pulse._total_propagator_liouville, remapped_pulse._total_propagator_liouville, atol=1e-14 ) self.assertArrayAlmostEqual(reordered_pulse._total_phases, remapped_pulse._total_phases) self.assertArrayAlmostEqual(reordered_pulse._control_matrix, remapped_pulse._control_matrix, atol=1e-12) self.assertArrayAlmostEqual(reordered_pulse._filter_function, remapped_pulse._filter_function, atol=1e-12) # Test the eigenvalues and -vectors by the characteristic equation self.assertCorrectDiagonalization(remapped_pulse, atol=1e-14)
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)
T = 20 H_n = {} H_c = {} dt = {} H_c['Id'] = [[qt.sigmax().full(), [0], 'X']] H_n['Id'] = [[qt.sigmax().full(), [1], 'X'], [qt.sigmay().full(), [1], 'Y']] dt['Id'] = [T] H_c['X2'] = [[qt.sigmax().full(), [np.pi / 4 / T], 'X']] H_n['X2'] = [[qt.sigmax().full(), [1], 'X'], [qt.sigmay().full(), [1], 'Y']] dt['X2'] = [T] H_c['Y2'] = [[qt.sigmay().full(), [np.pi / 4 / T], 'Y']] H_n['Y2'] = [[qt.sigmax().full(), [1], 'X'], [qt.sigmay().full(), [1], 'Y']] dt['Y2'] = [T] # %% Set up PulseSequences Id = ff.PulseSequence(H_c['Id'], H_n['Id'], dt['Id']) X2 = ff.PulseSequence(H_c['X2'], H_n['X2'], dt['X2']) Y2 = ff.PulseSequence(H_c['Y2'], H_n['Y2'], dt['Y2']) # %% Define some parameters m_min = 1 m_max = 151 # sequence lengths N_l = 21 lengths = np.round(np.linspace(m_min, m_max, N_l)).astype(int) # no. of random sequences per length N_G = 50 omega = np.geomspace(1e-2 / (7 * m_max * T), 1e2 / T, 301) * 2 * np.pi # %% Cache filter functions for primitive gates
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_pulse_sequence_constructor(self): """Test constructing a PulseSequence.""" base_pulse = testutil.rand_pulse_sequence(2, 5, 3, 3) H_c = list(zip(base_pulse.c_opers, base_pulse.c_coeffs, base_pulse.c_oper_identifiers)) H_n = list(zip(base_pulse.n_opers, base_pulse.n_coeffs, base_pulse.n_oper_identifiers)) dt = base_pulse.dt for i in range(3): H_c[i] = list(H_c[i]) H_n[i] = list(H_n[i]) with self.assertRaises(TypeError): # Not enough positional arguments ff.PulseSequence(H_c, H_n) with self.assertRaises(TypeError): # dt not a sequence ff.PulseSequence(H_c, H_n, dt[0]) idx = rng.randint(0, 5) with self.assertRaises(ValueError): # negative dt dt[idx] *= -1 ff.PulseSequence(H_c, H_n, dt) dt[idx] *= -1 with self.assertRaises(ValueError): # imaginary dt dt = dt.astype(complex) dt[idx] += 1j ff.PulseSequence(H_c, H_n, dt) dt = dt.real basis = ff.Basis.pauli(1) with self.assertRaises(ValueError): # basis not Basis instance ff.PulseSequence(H_c, H_n, dt, basis.view(np.ndarray)) with self.assertRaises(ValueError): # basis not square ff.PulseSequence(H_c, H_n, dt, basis.reshape(4, 1, 4)) with self.assertRaises(TypeError): # Control Hamiltonian not list or tuple ff.PulseSequence(np.array(H_c, dtype=object), H_n, dt) with self.assertRaises(TypeError): # Noise Hamiltonian not list or tuple ff.PulseSequence(H_c, np.array(H_n, dtype=object), dt) with self.assertRaises(TypeError): # Element of control Hamiltonian not list or tuple ff.PulseSequence([np.array(H_c[0], dtype=object)], H_n, dt) with self.assertRaises(TypeError): # Element of noise Hamiltonian not list or tuple ff.PulseSequence(H_c, [np.array(H_n[0], dtype=object)], dt) idx = rng.randint(0, 3) with self.assertRaises(TypeError): # Control Hamiltonian element not list or tuple H_c[idx] = dict(H_c[idx]) ff.PulseSequence(H_c, H_n, dt) H_c[idx] = list(H_c[idx]) with self.assertRaises(TypeError): # Noise Hamiltonian element not list or tuple H_n[idx] = dict(H_n[idx]) ff.PulseSequence(H_c, H_n, dt) H_n[idx] = list(H_n[idx]) with self.assertRaises(TypeError): # Control operators wrong type oper = H_c[idx][0].copy() H_c[idx][0] = dict(H_c[idx][0]) ff.PulseSequence(H_c, H_n, dt) H_c[idx][0] = oper with self.assertRaises(TypeError): # Noise operators wrong type oper = H_n[idx][0].copy() H_n[idx][0] = dict(H_n[idx][0]) ff.PulseSequence(H_c, H_n, dt) H_n[idx][0] = oper with self.assertRaises(TypeError): # Control coefficients wrong type coeff = H_c[idx][1].copy() H_c[idx][1] = H_c[idx][1][0] ff.PulseSequence(H_c, H_n, dt) H_c[idx][1] = coeff with self.assertRaises(TypeError): # Noise coefficients wrong type coeff = H_n[idx][1].copy() H_n[idx][1] = H_n[idx][1][0] ff.PulseSequence(H_c, H_n, dt) H_n[idx][1] = coeff with self.assertRaises(ValueError): # Control operators not 2d for hc in H_c: hc[0] = np.tile(hc[0], (rng.randint(2, 11), 1, 1)) ff.PulseSequence(H_c, H_n, dt) for hc in H_c: hc[0] = hc[0][0] with self.assertRaises(ValueError): # Noise operators not 2d for hn in H_n: hn[0] = np.tile(hn[0], (rng.randint(2, 11), 1, 1)) ff.PulseSequence(H_c, H_n, dt) for hn in H_n: hn[0] = hn[0][0] with self.assertRaises(ValueError): # Control operators not square for hc in H_c: hc[0] = np.tile(hc[0].reshape(1, 4), (2, 1)) ff.PulseSequence(H_c, H_n, dt) for hc in H_c: hc[0] = hc[0][0].reshape(2, 2) with self.assertRaises(ValueError): # Noise operators not square for hn in H_n: hn[0] = np.tile(hn[0].reshape(1, 4), (2, 1)) ff.PulseSequence(H_c, H_n, dt) for hn in H_n: hn[0] = hn[0][0].reshape(2, 2) with self.assertRaises(ValueError): # Control and noise operators not same dimension for hn in H_n: hn[0] = np.block([[hn[0], hn[0]], [hn[0], hn[0]]]) ff.PulseSequence(H_c, H_n, dt) for hn in H_n: hn[0] = hn[0][:2, :2] with self.assertRaises(ValueError): # Control identifiers not unique identifier = H_c[idx][2] H_c[idx][2] = H_c[idx-1][2] ff.PulseSequence(H_c, H_n, dt) H_c[idx][2] = identifier with self.assertRaises(ValueError): # Noise identifiers not unique identifier = H_n[idx][2] H_n[idx][2] = H_n[idx-1][2] ff.PulseSequence(H_c, H_n, dt) H_n[idx][2] = identifier coeffs = [] with self.assertRaises(ValueError): # Control coefficients not same length as dt for hc in H_c: coeffs.append(hc[1][-2:]) hc[1] = hc[1][:-2] ff.PulseSequence(H_c, H_n, dt) for i, c in enumerate(coeffs): H_c[i][1] = np.concatenate((H_c[i][1], c)) with self.assertRaises(ValueError): # Noise coefficients not same length as dt for hn in H_n: hn[1] = hn[1][:-2] ff.PulseSequence(H_c, H_n, dt) for i, c in enumerate(coeffs): H_n[i][1] = np.concatenate((H_n[i][1], c)) pulse = ff.PulseSequence(H_c, H_n, dt) # Hit __str__ and __repr__ methods pulse print(pulse) # Hit __copy__ method _ = copy(pulse) # Fewer identifiers than opers pulse_2 = ff.PulseSequence( [[util.paulis[1], [1], 'X'], [util.paulis[2], [1]]], [[util.paulis[1], [1]], [util.paulis[2], [1], 'Y']], [1] ) self.assertArrayEqual(pulse_2.c_oper_identifiers, ('A_1', 'X')) self.assertArrayEqual(pulse_2.n_oper_identifiers, ('B_0', 'Y'))
def test_accuracy(self): ID, X, Y, Z = util.paulis XI = util.tensor(X, ID) IX = util.tensor(ID, X) XII = util.tensor(X, ID, ID) IXI = util.tensor(ID, X, ID) IIX = util.tensor(ID, ID, X) XIII = util.tensor(X, ID, ID, ID) IXII = util.tensor(ID, X, ID, ID) IIXI = util.tensor(ID, ID, X, ID) IIIX = util.tensor(ID, ID, ID, X) YI = util.tensor(Y, ID) IY = util.tensor(ID, Y) YII = util.tensor(Y, ID, ID) IYI = util.tensor(ID, Y, ID) IIY = util.tensor(ID, ID, Y) YIII = util.tensor(Y, ID, ID, ID) IYII = util.tensor(ID, Y, ID, ID) IIYI = util.tensor(ID, ID, Y, ID) IIIY = util.tensor(ID, ID, ID, Y) ZI = util.tensor(Z, ID) IZ = util.tensor(ID, Z) ZII = util.tensor(Z, ID, ID) IZI = util.tensor(ID, Z, ID) ZIII = util.tensor(Z, ID, ID, ID) IZII = util.tensor(ID, Z, ID, ID) IIZI = util.tensor(ID, ID, Z, ID) IIIZ = util.tensor(ID, ID, ID, Z) IIZ = util.tensor(ID, ID, Z) XXX = util.tensor(X, X, X) n_dt = 10 coeffs = rng.standard_normal((3, n_dt)) X_pulse = ff.PulseSequence( [[X, coeffs[0], 'X']], list(zip((X, Y, Z), np.ones((3, n_dt)), ('X', 'Y', 'Z'))), np.ones(n_dt), basis=ff.Basis.pauli(1) ) Y_pulse = ff.PulseSequence( [[Y, coeffs[1], 'Y']], list(zip((X, Y, Z), np.ones((3, n_dt)), ('X', 'Y', 'Z'))), np.ones(n_dt), basis=ff.Basis.pauli(1) ) Z_pulse = ff.PulseSequence( [[Z, coeffs[2], 'Z']], list(zip((X, Y, Z), np.ones((3, n_dt)), ('X', 'Y', 'Z'))), np.ones(n_dt), basis=ff.Basis.pauli(1) ) XZ_pulse = ff.PulseSequence( [[XI, coeffs[0], 'XI'], [IZ, coeffs[2], 'IZ']], list(zip((XI, YI, ZI, IX, IY, IZ), np.ones((6, n_dt)), ('XI', 'YI', 'ZI', 'IX', 'IY', 'IZ'))), np.ones(n_dt), basis=ff.Basis.pauli(2) ) XYZ_pulse = ff.PulseSequence( [[XII, coeffs[0], 'XII'], [IYI, coeffs[1], 'IYI'], [IIZ, coeffs[2], 'IIZ']], list(zip((XII, YII, ZII, IIX, IIY, IIZ, IXI, IYI, IZI, XXX), np.ones((10, n_dt)), ('XII', 'YII', 'ZII', 'IIX', 'IIY', 'IIZ', 'IXI', 'IYI', 'IZI', 'XXX'))), np.ones(n_dt), basis=ff.Basis.pauli(3) ) ZYX_pulse = ff.PulseSequence( [[IIX, coeffs[0], 'IIX'], [IYI, coeffs[1], 'IYI'], [ZII, coeffs[2], 'ZII']], list(zip((IIX, IIY, IIZ, XII, YII, ZII, IXI, IYI, IZI), np.ones((9, n_dt)), ('IIX', 'IIY', 'IIZ', 'XII', 'YII', 'ZII', 'IXI', 'IYI', 'IZI'))), np.ones(n_dt), basis=ff.Basis.pauli(3) ) XZXZ_pulse = ff.PulseSequence( [[XIII, coeffs[0], 'XIII'], [IZII, coeffs[2], 'IZII'], [IIXI, coeffs[0], 'IIXI'], [IIIZ, coeffs[2], 'IIIZ']], list(zip((XIII, YIII, ZIII, IXII, IYII, IZII, IIXI, IIYI, IIZI, IIIX, IIIY, IIIZ), np.ones((12, n_dt)), ('XIII', 'YIII', 'ZIII', 'IXII', 'IYII', 'IZII', 'IIXI', 'IIYI', 'IIZI', 'IIIX', 'IIIY', 'IIIZ'))), np.ones(n_dt), basis=ff.Basis.pauli(4) ) XXZZ_pulse = ff.PulseSequence( [[XIII, coeffs[0], 'XIII'], [IIZI, coeffs[2], 'IIZI'], [IXII, coeffs[0], 'IXII'], [IIIZ, coeffs[2], 'IIIZ']], list(zip((XIII, YIII, ZIII, IIXI, IIYI, IIZI, IXII, IYII, IZII, IIIX, IIIY, IIIZ), np.ones((12, n_dt)), ('XIII', 'YIII', 'ZIII', 'IIXI', 'IIYI', 'IIZI', 'IXII', 'IYII', 'IZII', 'IIIX', 'IIIY', 'IIIZ'))), np.ones(n_dt), basis=ff.Basis.pauli(4) ) # Cache omega = ff.util.get_sample_frequencies(XYZ_pulse, n_samples=50) X_pulse.cache_filter_function(omega) Y_pulse.cache_filter_function(omega) Z_pulse.cache_filter_function(omega) XZ_pulse.cache_filter_function(omega) XYZ_pulse.cache_filter_function(omega) ZYX_pulse.cache_filter_function(omega) XZXZ_pulse.cache_filter_function(omega) XXZZ_pulse.cache_filter_function(omega) # Test that mapping a pulse to itself returns the pulse itself and # issues a warning with self.assertWarns(UserWarning): pulse = ff.extend([(X_pulse, 0)]) self.assertIs(pulse, X_pulse) with self.assertWarns(UserWarning): pulse = ff.extend([(XZ_pulse, (0, 1))]) self.assertIs(pulse, XZ_pulse) # Test mapping two single-qubit pulses to a two-qubit pulse XZ_pulse_ext = ff.extend([ (X_pulse, 0, {'X': 'XI', 'Y': 'YI', 'Z': 'ZI'}), (Z_pulse, 1, {'X': 'IX', 'Y': 'IY', 'Z': 'IZ'}) ]) self.assertEqual(XZ_pulse, XZ_pulse_ext) self.assertCorrectDiagonalization(XZ_pulse_ext, atol=1e-14) self.assertArrayAlmostEqual(XZ_pulse._propagators, XZ_pulse_ext._propagators, atol=1e-10) self.assertArrayAlmostEqual(XZ_pulse._control_matrix, XZ_pulse_ext._control_matrix, atol=1e-9) self.assertArrayAlmostEqual(XZ_pulse._filter_function, XZ_pulse_ext._filter_function, atol=1e-9) # Test additional noise Hamiltonian add_H_n = list(zip((XXX,), np.ones((1, n_dt)), ['XXX'])) XYZ_pulse_ext = ff.extend( [(XZ_pulse, (0, 2), {i: i[0] + 'I' + i[1] for i in XZ_pulse.n_oper_identifiers}), (Y_pulse, 1, {i: 'I' + i[0] + 'I' for i in Y_pulse.n_oper_identifiers})], additional_noise_Hamiltonian=add_H_n ) self.assertEqual(XYZ_pulse, XYZ_pulse_ext) self.assertCorrectDiagonalization(XYZ_pulse_ext, atol=1e-14) self.assertArrayAlmostEqual(XYZ_pulse._propagators, XYZ_pulse_ext._propagators, atol=1e-10) self.assertArrayAlmostEqual(XYZ_pulse._control_matrix, XYZ_pulse_ext._control_matrix, atol=1e-9) self.assertArrayAlmostEqual(XYZ_pulse._filter_function, XYZ_pulse_ext._filter_function, atol=1e-9) # Test remapping a two-qubit pulse ZYX_pulse_ext = ff.extend( [(XZ_pulse, (2, 0), {i: i[1] + 'I' + i[0] for i in XZ_pulse.n_oper_identifiers}), (Y_pulse, 1, {i: 'I' + i[0] + 'I' for i in Y_pulse.n_oper_identifiers})], ) self.assertEqual(ZYX_pulse, ZYX_pulse_ext) self.assertCorrectDiagonalization(ZYX_pulse_ext, atol=1e-14) self.assertArrayAlmostEqual(ZYX_pulse._propagators, ZYX_pulse_ext._propagators, atol=1e-10) self.assertArrayAlmostEqual(ZYX_pulse._control_matrix, ZYX_pulse_ext._control_matrix, atol=1e-9) self.assertArrayAlmostEqual(ZYX_pulse._filter_function, ZYX_pulse_ext._filter_function, atol=1e-9) XZXZ_pulse_ext = ff.extend([ (XZ_pulse, (0, 1), {i: i + 'II' for i in XZ_pulse.n_oper_identifiers}), (XZ_pulse, (2, 3), {i: 'II' + i for i in XZ_pulse.n_oper_identifiers}) ], cache_diagonalization=True) self.assertEqual(XZXZ_pulse, XZXZ_pulse_ext) self.assertCorrectDiagonalization(XZXZ_pulse_ext, atol=1e-14) self.assertArrayAlmostEqual(XZXZ_pulse._propagators, XZXZ_pulse_ext._propagators, atol=1e-10) self.assertArrayAlmostEqual(XZXZ_pulse._control_matrix, XZXZ_pulse_ext._control_matrix, atol=1e-9) self.assertArrayAlmostEqual(XZXZ_pulse._filter_function, XZXZ_pulse_ext._filter_function, atol=1e-8) XZXZ_pulse_ext = ff.extend([ (XZ_pulse, (0, 1), {i: i + 'II' for i in XZ_pulse.n_oper_identifiers}), (XZ_pulse, (2, 3), {i: 'II' + i for i in XZ_pulse.n_oper_identifiers}) ], cache_diagonalization=False) self.assertEqual(XZXZ_pulse, XZXZ_pulse_ext) self.assertArrayAlmostEqual(XZXZ_pulse._total_propagator, XZXZ_pulse_ext._total_propagator, atol=1e-10) # Test merging with overlapping qubit ranges XXZZ_pulse_ext = ff.extend([ (XZ_pulse, (0, 2), {i: i[0] + 'I' + i[1] + 'I' for i in XZ_pulse.n_oper_identifiers}), (XZ_pulse, (1, 3), {i: 'I' + i[0] + 'I' + i[1] for i in XZ_pulse.n_oper_identifiers}), ]) self.assertEqual(XXZZ_pulse, XXZZ_pulse_ext) self.assertCorrectDiagonalization(XXZZ_pulse_ext, atol=1e-14) self.assertArrayAlmostEqual(XXZZ_pulse._propagators, XXZZ_pulse_ext._propagators, atol=1e-10) self.assertArrayAlmostEqual(XXZZ_pulse._control_matrix, XXZZ_pulse_ext._control_matrix, atol=1e-10) self.assertArrayAlmostEqual(XXZZ_pulse._filter_function, XXZZ_pulse_ext._filter_function, atol=1e-8)
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'))