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_second_order_filter_function(self): for d, n_nops in zip(rng.integers(2, 7, 5), rng.integers(1, 5, 5)): pulse = testutil.rand_pulse_sequence(d, 3, 2, n_nops) omega = util.get_sample_frequencies(pulse, n_samples=42) # Make sure result is the same with or without intermediates pulse.cache_control_matrix(omega, cache_intermediates=True) F = pulse.get_filter_function(omega, order=1) F_1 = pulse.get_filter_function(omega, order=2) # Test caching F_2 = pulse.get_filter_function(omega, order=2) F_3 = numeric.calculate_second_order_filter_function( pulse.eigvals, pulse.eigvecs, pulse.propagators, omega, pulse.basis, pulse.n_opers, pulse.n_coeffs, pulse.dt, show_progressbar=False, intermediates=None) # Make sure first and second order are of same order of magnitude rel = np.linalg.norm(F) / np.linalg.norm(F_1) self.assertIs(F_1, F_2) self.assertArrayEqual(F_1, F_3) self.assertEqual(F_1.shape, (n_nops, n_nops, d**2, d**2, 42)) self.assertLessEqual(rel, 10) self.assertGreaterEqual(rel, 1 / 10)
def test_control_matrix(self): """Test control matrix for traceless and non-traceless bases""" n_opers = testutil.rand_herm(3, 4) n_opers_traceless = testutil.rand_herm_traceless(3, 4) basis = ff.Basis(testutil.rand_herm(3), traceless=False) basis_traceless = ff.Basis(testutil.rand_herm_traceless(3), traceless=True) base_pulse = testutil.rand_pulse_sequence(3, 10, 4, 4) omega = np.logspace(-1, 1, 51) for i, base in enumerate((basis, basis_traceless)): for j, n_ops in enumerate((n_opers, n_opers_traceless)): pulse = copy(base_pulse) pulse.n_opers = n_ops pulse.basis = base R = pulse.get_control_matrix(omega) if i == 0 and j == 0: # base not traceless, nopers not traceless self.assertTrue((R[:, 0] != 0).all()) elif i == 0 and j == 1: # base not traceless, nopers traceless self.assertTrue((R[:, 0] != 0).all()) elif i == 1 and j == 0: # base traceless, nopers not traceless self.assertTrue((R[:, 0] != 0).all()) elif i == 1 and j == 1: # base traceless, nopers traceless self.assertTrue(np.allclose(R[:, 0], 0))
def test_integration(self): """Compare integrals to numerical results.""" d = 3 pulse = testutil.rand_pulse_sequence(d, 5) # including zero E = util.get_sample_frequencies(pulse, 51, include_quasistatic=True) for i, (eigval, dt, t) in enumerate(zip(pulse.eigvals, pulse.dt, pulse.t)): integral, integral_numeric = _get_integrals_first_order( d, E, eigval, dt, t) self.assertArrayAlmostEqual(integral, integral_numeric, atol=1e-4) integral, integral_numeric = _get_integrals_second_order( d, E, eigval, dt, t) self.assertArrayAlmostEqual(integral, integral_numeric, atol=1e-4) # excluding (most likely) zero E = testutil.rng.standard_normal(51) for i, (eigval, dt, t) in enumerate(zip(pulse.eigvals, pulse.dt, pulse.t)): integral, integral_numeric = _get_integrals_first_order( d, E, eigval, dt, t) self.assertArrayAlmostEqual(integral, integral_numeric, atol=1e-4) integral, integral_numeric = _get_integrals_second_order( d, E, eigval, dt, t) self.assertArrayAlmostEqual(integral, integral_numeric, atol=1e-4)
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_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_calculate_decay_amplitudes(self): """Test raises of numeric.calculate_decay_amplitudes""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) omega = rng.standard_normal(43) # single spectrum spectrum = rng.standard_normal(78) for i in range(4): with self.assertRaises(ValueError): numeric.calculate_decay_amplitudes(pulse, np.tile(spectrum, [1]*i), omega)
def test_calculate_error_vector_correlation_functions(self): """Test raises of numeric.error_transfer_matrix""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) omega = rng.standard_normal(43) # single spectrum S = rng.standard_normal(78) for i in range(4): with self.assertRaises(ValueError): numeric.calculate_error_vector_correlation_functions( pulse, np.tile(S, [1] * i), omega)
def test_raises(self): pulse = testutil.rand_pulse_sequence(2, 3) omega = ff.util.get_sample_frequencies(pulse, n_samples=13) with self.assertRaises(ValueError): ff.infidelity_derivative(pulse, 1 / omega, omega, control_identifiers=['long string']) with self.assertRaises(ValueError): pulse.get_filter_function_derivative( omega, n_coeffs_deriv=testutil.rng.normal(size=(2, 5, 10)))
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_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_liouville_is_cCP(self): for d in rng.integers(2, 9, (15, )): # (anti-) Hermitian generator, should always be cCP H = 1j * testutil.rand_herm(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) H_sup = (np.einsum('iab,...bc,jca', basis, H, basis, optimize=['einsum_path', (0, 1), (0, 1)]) - np.einsum('iab,jbc,...ca', basis, basis, H, optimize=['einsum_path', (0, 2), (0, 1)])) cCP, (D, V) = superoperator.liouville_is_cCP(H_sup, basis, True) _cCP = superoperator.liouville_is_cCP(H_sup, basis, False) self.assertArrayEqual(cCP, _cCP) self.assertTrue(np.all(cCP)) if H_sup.ndim == 2: self.assertIsInstance(cCP, (bool, np.bool8)) else: self.assertEqual(cCP.shape[0], H_sup.shape[0]) self.assertArrayAlmostEqual(D, 0, atol=1e-14) self.assertTrue( ff.util.oper_equiv(V, np.eye(d**2)[None, :, :], normalized=True)) pulse = testutil.rand_pulse_sequence(d, 1) omega = ff.util.get_sample_frequencies(pulse) S = 1 / abs(omega)**2 K_sup = ff.numeric.calculate_cumulant_function(pulse, S, omega) cCP = superoperator.liouville_is_cCP(K_sup, pulse.basis, False, atol=1e-13) self.assertTrue(np.all(cCP)) if K_sup.ndim == 2: self.assertIsInstance(cCP, (bool, np.bool8)) else: self.assertEqual(cCP.shape[0], K_sup.shape[0])
def test_calculate_cumulant_function(self): """Test numeric.calculate_cumulant_function""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) omega = rng.standard_normal(43) # single spectrum spectrum = rng.standard_normal(43) Gamma = numeric.calculate_decay_amplitudes(pulse, spectrum, omega) K_1 = numeric.calculate_cumulant_function(pulse, spectrum, omega) K_2 = numeric.calculate_cumulant_function(pulse, decay_amplitudes=Gamma) self.assertArrayAlmostEqual(K_1, K_2) with self.assertRaises(ValueError): numeric.calculate_cumulant_function(pulse, None, None, None)
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_caching(self): pauli_pulse = testutil.rand_pulse_sequence(4, 1, 1, 4, 'Pauli') ggm_pulse = testutil.rand_pulse_sequence(4, 1, 1, 4, 'GGM') attrs = ('omega', 'eigvals', 'eigvecs', 'propagators', 'total_phases', 'total_propagator', 'filter_function', 'total_propagator_liouville', 'control_matrix') pauli_pulse.cleanup('all') remapped_pauli_pulse = ff.remap(pauli_pulse, (1, 0)) for attr in attrs: self.assertEqual(pauli_pulse.is_cached(attr), remapped_pauli_pulse.is_cached(attr)) omega = util.get_sample_frequencies(pauli_pulse, n_samples=50) pauli_pulse.cache_filter_function(omega) remapped_pauli_pulse = ff.remap(pauli_pulse, (1, 0)) for attr in attrs: self.assertEqual(pauli_pulse.is_cached(attr), remapped_pauli_pulse.is_cached(attr)) ggm_pulse.cleanup('all') remapped_ggm_pulse = ff.remap(ggm_pulse, (1, 0)) for attr in attrs: self.assertEqual(ggm_pulse.is_cached(attr), remapped_ggm_pulse.is_cached(attr)) omega = util.get_sample_frequencies(ggm_pulse, n_samples=50) ggm_pulse.cache_filter_function(omega) with self.assertWarns(UserWarning): remapped_ggm_pulse = ff.remap(ggm_pulse, (1, 0)) for attr in attrs[:-2]: self.assertEqual(ggm_pulse.is_cached(attr), remapped_ggm_pulse.is_cached(attr)) for attr in attrs[-2:]: self.assertFalse(remapped_ggm_pulse.is_cached(attr))
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_calculation_with_unitaries(self): for d, G in zip(rng.integers(2, 9, (11, )), rng.integers(2, 11, (11, ))): pulses = [ testutil.rand_pulse_sequence(d, rng.integers(1, 11), 2, 4) for g in range(G) ] for pulse in pulses: # All pulses should have same n_opers for xxx_from_atomic pulse.n_opers = pulses[0].n_opers pulse.n_oper_identifiers = pulses[0].n_oper_identifiers if rng.integers(0, 2): pulse._t = None omega = rng.random(17) B_atomic = np.array([ numeric.calculate_noise_operators_from_scratch( pulse.eigvals, pulse.eigvecs, pulse.propagators, omega, pulse.n_opers, pulse.n_coeffs, pulse.dt, pulse.t) for pulse in pulses ]) R_atomic = np.array([ numeric.calculate_control_matrix_from_scratch( pulse.eigvals, pulse.eigvecs, pulse.propagators, omega, pulse.basis, pulse.n_opers, pulse.n_coeffs, pulse.dt, pulse.t) for pulse in pulses ]) self.assertArrayAlmostEqual(ff.basis.expand(B_atomic, pulse.basis), R_atomic.transpose(0, 3, 1, 2), atol=1e-14) B = numeric.calculate_noise_operators_from_atomic( np.array([pulse.get_total_phases(omega) for pulse in pulses]), B_atomic, np.array([pulse.total_propagator for pulse in pulses])) R = numeric.calculate_control_matrix_from_atomic( np.array([pulse.get_total_phases(omega) for pulse in pulses]), R_atomic, np.array( [pulse.total_propagator_liouville for pulse in pulses])) self.assertArrayAlmostEqual(ff.basis.expand(B, pulse.basis), R.transpose(2, 0, 1), atol=1e-14)
def test_copy(self): pulse = testutil.rand_pulse_sequence(2, 2) old_copers = pulse.c_opers.copy() copied = copy.copy(pulse) deepcopied = copy.deepcopy(pulse) self.assertEqual(pulse, copied) self.assertEqual(pulse, deepcopied) pulse.c_opers[...] = rng.standard_normal(size=pulse.c_opers.shape) self.assertArrayEqual(pulse.c_opers, copied.c_opers) self.assertArrayEqual(old_copers, deepcopied.c_opers) self.assertEqual(pulse, copied) self.assertNotEqual(pulse, deepcopied)
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_cache_filter_function(self): omega = rng.random(32) pulse = testutil.rand_pulse_sequence(2, 3, n_nops=2) F_fidelity = numeric.calculate_filter_function( pulse.get_control_matrix(omega), 'fidelity') F_generalized = numeric.calculate_filter_function( pulse.get_control_matrix(omega), 'generalized') pulse.cache_filter_function(omega, filter_function=F_generalized, which='generalized') self.assertTrue(pulse.is_cached('filter function')) self.assertTrue(pulse.is_cached('generalized filter function')) self.assertArrayEqual( pulse.get_filter_function(omega, which='generalized'), F_generalized) self.assertArrayEqual( pulse.get_filter_function(omega, which='fidelity'), F_fidelity)
def test_cache_intermediates(self): """Test caching of intermediate elements""" pulse = testutil.rand_pulse_sequence(3, 4, 2, 3) omega = util.get_sample_frequencies(pulse, 33, spacing='linear') ctrlmat = pulse.get_control_matrix(omega, cache_intermediates=True) filtfun = pulse.get_filter_function(omega, cache_intermediates=True) self.assertIsNotNone(pulse._intermediates) self.assertArrayAlmostEqual(pulse._intermediates['control_matrix_step'].sum(0), ctrlmat) self.assertArrayAlmostEqual(numeric.calculate_filter_function(ctrlmat), filtfun) self.assertArrayAlmostEqual(pulse._intermediates['n_opers_transformed'], numeric._transform_noise_operators(pulse.n_coeffs, pulse.n_opers, pulse.eigvecs)) eigvecs_prop = numeric._propagate_eigenvectors(pulse.propagators[:-1], pulse.eigvecs) basis_transformed = np.einsum('gba,kbc,gcd->gkad', eigvecs_prop.conj(), pulse.basis, eigvecs_prop) self.assertArrayAlmostEqual(pulse._intermediates['basis_transformed'], basis_transformed, atol=1e-14)
def test_filter_functions(self): """Filter functions equal for different bases""" # Set up random Hamiltonian base_pulse = testutil.rand_pulse_sequence(4, 3, 1, 1) omega = ff.util.get_sample_frequencies(base_pulse, n_samples=200) pauli_basis = ff.Basis.pauli(2) ggm_basis = ff.Basis.ggm(4) from_random_basis = ff.Basis(base_pulse.n_opers) bases = (pauli_basis, ggm_basis, from_random_basis) # Get Pulses with different bases F = [] for b in bases: pulse = copy(base_pulse) pulse.basis = b F.append(pulse.get_filter_function(omega).sum(0)) for pair in product(F, F): self.assertArrayAlmostEqual(*pair)
def test_bloch_sphere_visualization_not_available(self): if matplotlib is not None: from filter_functions import plotting else: plotting = mock.Mock() with self.assertRaises(RuntimeError): plotting.get_bloch_vector(testutil.rng.standard_normal((10, 2))) with self.assertRaises(RuntimeError): plotting.init_bloch_sphere() with self.assertRaises(RuntimeError): plotting.plot_bloch_vector_evolution( testutil.rand_pulse_sequence(2, 1)) from filter_functions import types self.assertIs(types.State, ndarray) self.assertIs(types.Operator, ndarray)
def test_concatenate_base(self): """Basic functionality.""" pulse_1, pulse_2 = [testutil.rand_pulse_sequence(2, 1, 2, 3) for _ in range(2)] # Trivial case, copy c_pulse = ff.concatenate([pulse_1]) self.assertEqual(pulse_1, c_pulse) self.assertFalse(pulse_1 is c_pulse) # Don't cache filter function, expect same result as with # concatenate_without_filter_function c_pulse_1 = ff.concatenate([pulse_1, pulse_2], calc_filter_function=False) c_pulse_2 = pulse_sequence.concatenate_without_filter_function( [pulse_1, pulse_2], return_identifier_mappings=False ) self.assertEqual(c_pulse_1, c_pulse_2) # Try concatenation with different frequencies but FF calc. forced with self.assertRaises(ValueError): pulse_1.omega = [1, 2] pulse_2.omega = [3, 4] ff.concatenate([pulse_1, pulse_2], calc_filter_function=True)
def test_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_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_integrals_against_numeric(self): """Test the private function used to set up the integrand.""" pulses = [ testutil.rand_pulse_sequence(3, 1, 2, 3), testutil.rand_pulse_sequence(3, 1, 2, 3) ] pulses[1].n_opers = pulses[0].n_opers pulses[1].n_oper_identifiers = pulses[0].n_oper_identifiers omega = np.linspace(-1, 1, 50) spectra = [ 1e-6 / abs(omega), 1e-6 / np.power.outer(abs(omega), np.arange(2)).T, np.array([[ 1e-6 / abs(omega)**0.7, 1e-6 / (1 + omega**2) + 1j * 1e-6 * omega ], [ 1e-6 / (1 + omega**2) - 1j * 1e-6 * omega, 1e-6 / abs(omega)**0.7 ]]) ] pulse = ff.concatenate(pulses, omega=omega, calc_pulse_correlation_FF=True) idx = testutil.rng.choice(np.arange(2), testutil.rng.integers(1, 3), replace=False) R = pulse.get_control_matrix(omega) R_pc = pulse.get_pulse_correlation_control_matrix() F = pulse.get_filter_function(omega) F_kl = pulse.get_filter_function(omega, 'generalized') F_pc = pulse.get_pulse_correlation_filter_function() F_pc_kl = pulse.get_pulse_correlation_filter_function('generalized') for i, spectrum in enumerate(spectra): if i == 0: S = spectrum elif i == 1: S = spectrum[idx] elif i == 2: S = spectrum[idx[None, :], idx[:, None]] R_1 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='fidelity', control_matrix=R, filter_function=None) R_2 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='fidelity', control_matrix=[R, R], filter_function=None) F_1 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='fidelity', control_matrix=None, filter_function=F) self.assertArrayAlmostEqual(R_1, R_2) self.assertArrayAlmostEqual(R_1, F_1) R_1 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='fidelity', control_matrix=R_pc, filter_function=None) R_2 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='fidelity', control_matrix=[R_pc, R_pc], filter_function=None) F_1 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='fidelity', control_matrix=None, filter_function=F_pc) self.assertArrayAlmostEqual(R_1, R_2) self.assertArrayAlmostEqual(R_1, F_1) R_1 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='generalized', control_matrix=R, filter_function=None) R_2 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='generalized', control_matrix=[R, R], filter_function=None) F_1 = numeric._get_integrand(S, omega, idx, which_pulse='total', which_FF='generalized', control_matrix=None, filter_function=F_kl) self.assertArrayAlmostEqual(R_1, R_2) self.assertArrayAlmostEqual(R_1, F_1) R_1 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='generalized', control_matrix=R_pc, filter_function=None) R_2 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='generalized', control_matrix=[R_pc, R_pc], filter_function=None) F_1 = numeric._get_integrand(S, omega, idx, which_pulse='correlations', which_FF='generalized', control_matrix=None, filter_function=F_pc_kl) self.assertArrayAlmostEqual(R_1, R_2) self.assertArrayAlmostEqual(R_1, F_1)
def test_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))