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_oper_equiv(self): with self.assertRaises(ValueError): util.oper_equiv(rng.standard_normal((2, 2)), rng.standard_normal((3, 3))) for d in rng.randint(2, 10, (5, )): psi = rng.standard_normal((d, 1)) + 1j * rng.standard_normal( (d, 1)) # Also test broadcasting U = testutil.rand_herm(d, rng.randint(1, 11)).squeeze() phase = rng.standard_normal() result = util.oper_equiv(psi, psi * np.exp(1j * phase)) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], phase, places=5) result = util.oper_equiv(psi * np.exp(1j * phase), psi) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], -phase, places=5) psi /= np.linalg.norm(psi, ord=2) result = util.oper_equiv(psi, psi * np.exp(1j * phase), normalized=True, eps=1e-13) self.assertTrue(result[0]) self.assertArrayAlmostEqual(result[1], phase, atol=1e-5) result = util.oper_equiv(psi, psi + 1) self.assertFalse(result[0]) result = util.oper_equiv(U, U * np.exp(1j * phase)) self.assertTrue(np.all(result[0])) self.assertArrayAlmostEqual(result[1], phase, atol=1e-5) result = util.oper_equiv(U * np.exp(1j * phase), U) self.assertTrue(np.all(result[0])) self.assertArrayAlmostEqual(result[1], -phase, atol=1e-5) norm = np.sqrt(util.dot_HS(U, U)) norm = norm[:, None, None] if U.ndim == 3 else norm U /= norm # TIP: In numpy 1.18 we could just do: # U /= np.expand_dims(np.sqrt(util.dot_HS(U, U)), axis=(-1, -2)) result = util.oper_equiv(U, U * np.exp(1j * phase), normalized=True, eps=1e-10) self.assertTrue(np.all(result[0])) self.assertArrayAlmostEqual(result[1], phase) result = util.oper_equiv(U, U + 1) self.assertFalse(np.all(result[0]))
def test_integrate(self): f = rng.standard_normal(32) x = rng.random(32) self.assertEqual(util.integrate(f, x), np.trapz(f, x)) f = rng.standard_normal((2, 32)).astype(complex) x = rng.random(32) self.assertArrayEqual(util.integrate(f, x), np.trapz(f, x)) f = rng.standard_normal(32) x = np.linspace(0, 1, 32) dx = 1 / 31 self.assertAlmostEqual(util.integrate(f, x), util.integrate(f, dx=dx))
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_basis_expansion_and_normalization(self): """Correct expansion of operators and normalization of bases""" for _ in range(10): d = rng.randint(2, 16) ggm_basis = ff.Basis.ggm(d) basis = ff.Basis(np.einsum('i,ijk->ijk', rng.standard_normal(d**2), ggm_basis), skip_check=True) M = rng.standard_normal((d, d)) + 1j * rng.standard_normal((d, d)) M -= np.trace(M) / d coeffs = ff.basis.expand(M, basis, normalized=False) self.assertArrayAlmostEqual(M, np.einsum('i,ijk', coeffs, basis)) self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis), ff.basis.ggm_expand(M), atol=1e-14) self.assertArrayAlmostEqual(ff.basis.ggm_expand(M), ff.basis.ggm_expand(M, traceless=True), atol=1e-14) n = rng.randint(1, 50) M = (rng.standard_normal((n, d, d)) + 1j * rng.standard_normal( (n, d, d))) coeffs = ff.basis.expand(M, basis, normalized=False) self.assertArrayAlmostEqual( M, np.einsum('li,ijk->ljk', coeffs, basis)) self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis), ff.basis.ggm_expand(M), atol=1e-14) # Argument to ggm_expand not square in last two dimensions with self.assertRaises(ValueError): ff.basis.ggm_expand(basis[..., 0]) self.assertTrue(ff.basis.normalize(basis).isorthonorm) # Basis method and function should give the same normalized = ff.basis.normalize(basis) basis.normalize() self.assertEqual(normalized, basis) # normalize single element elem = basis[1] normalized = ff.basis.normalize(elem) elem.normalize() self.assertEqual(normalized, elem) # Not matrix or sequence of matrices with self.assertRaises(ValueError): ff.basis.normalize(basis[0, 0])
def test_oper_equiv(self): with self.assertRaises(ValueError): util.oper_equiv(*[np.ones((1, 2, 3))] * 2) for d in rng.randint(2, 10, (5, )): psi = rng.standard_normal((d, 1)) + 1j * rng.standard_normal( (d, 1)) U = testutil.rand_herm(d).squeeze() phase = rng.standard_normal() result = util.oper_equiv(psi, psi * np.exp(1j * phase)) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], phase, places=5) result = util.oper_equiv(psi * np.exp(1j * phase), psi) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], -phase, places=5) psi /= np.linalg.norm(psi, ord=2) result = util.oper_equiv(psi, psi * np.exp(1j * phase), normalized=True, eps=1e-13) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], phase, places=5) result = util.oper_equiv(psi, psi + 1) self.assertFalse(result[0]) result = util.oper_equiv(U, U * np.exp(1j * phase)) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], phase, places=5) result = util.oper_equiv(U * np.exp(1j * phase), U) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], -phase, places=5) U /= np.sqrt(util.dot_HS(U, U)) result = util.oper_equiv(U, U * np.exp(1j * phase), normalized=True, eps=1e-10) self.assertTrue(result[0]) self.assertAlmostEqual(result[1], phase) result = util.oper_equiv(U, U + 1) self.assertFalse(result[0])
def test_get_sample_frequencies(self): pulse = PulseSequence([[util.paulis[1], [np.pi / 2]]], [[util.paulis[1], [1]]], [abs(rng.standard_normal())]) # Default args omega = util.get_sample_frequencies(pulse) self.assertAlmostEqual(omega[0], 2e-2 * np.pi / pulse.tau) self.assertAlmostEqual(omega[-1], 2e2 * np.pi / pulse.tau) self.assertEqual(len(omega), 300) self.assertTrue((omega >= 0).all()) self.assertLessEqual(np.var(np.diff(np.log(omega[150:]))), 1e-16) # custom args omega = util.get_sample_frequencies(pulse, spacing='linear', n_samples=50, include_quasistatic=True) self.assertAlmostEqual(omega[0], 0) self.assertAlmostEqual(omega[-1], 2e2 * np.pi / pulse.tau) self.assertEqual(len(omega), 50) self.assertTrue((omega >= 0).all()) self.assertLessEqual(np.var(np.diff(omega[1:])), 1e-16) # Exceptions with self.assertRaises(ValueError): omega = util.get_sample_frequencies(pulse, spacing='foo')
def test_basis_constructor(self): """Test the constructor for several failure modes""" # Constructing from given elements should check for __getitem__ with self.assertRaises(TypeError): _ = ff.Basis(1) # All elements should be either sparse, Qobj, or ndarray elems = [ ff.util.paulis[1], COO.from_numpy(ff.util.paulis[3]), [[0, 1], [1, 0]] ] with self.assertRaises(TypeError): _ = ff.Basis(elems) # Too many elements with self.assertRaises(ValueError): _ = ff.Basis(rng.standard_normal((5, 2, 2))) # Properly normalized self.assertEqual(ff.Basis.pauli(1), ff.Basis(ff.util.paulis)) # Non traceless elems but traceless basis requested with self.assertRaises(ValueError): _ = ff.Basis(np.ones((2, 2)), traceless=True) # Calling with only the identity should work with traceless true or # false self.assertEqual(ff.Basis(np.eye(2), traceless=False), ff.Basis(np.eye(2), traceless=True)) # Constructing a basis from a basis should work _ = ff.Basis(ff.Basis.ggm(2)[1:])
def test_oper_equiv(self): self.assertFalse( util.oper_equiv(qutip.rand_ket(2), qutip.rand_dm(2))[0]) for d in rng.integers(2, 10, (5, )): psi = qutip.rand_ket(d) U = qutip.rand_dm(d) phase = rng.standard_normal() result = util.oper_equiv(psi, psi * np.exp(1j * phase)) self.assertTrue(result[0]) self.assertAlmostEqual(np.mod(result[1], 2 * np.pi), np.mod(phase, 2 * np.pi), places=5) result = util.oper_equiv(psi * np.exp(1j * phase), psi) self.assertTrue(result[0]) self.assertAlmostEqual(np.mod(result[1], 2 * np.pi), np.mod(-phase, 2 * np.pi), places=5) result = util.oper_equiv(U, U * np.exp(1j * phase)) self.assertTrue(result[0]) self.assertAlmostEqual(np.mod(result[1], 2 * np.pi), np.mod(phase, 2 * np.pi), places=5) result = util.oper_equiv(U * np.exp(1j * phase), U) self.assertTrue(result[0]) self.assertAlmostEqual(np.mod(result[1], 2 * np.pi), np.mod(-phase, 2 * np.pi), places=5)
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_tensor_transpose(self): # Test basic functionality paulis = np.array(util.paulis) I, X, Y, Z = paulis arr = util.tensor(I, X, Y, Z) arr_dims = [[2] * 4] * 2 order = np.arange(4) for _ in range(20): order = rng.permutation(order) r = util.tensor_transpose(arr, order, arr_dims) self.assertArrayAlmostEqual(r, util.tensor(*paulis[order])) # Check exceptions with self.assertRaises(ValueError): # wrong arr_dims (too few dims) r = util.tensor_transpose(arr, order, [[2] * 3] * 2) with self.assertRaises(ValueError): # wrong arr_dims (too few dims) r = util.tensor_transpose(arr, order, [[2] * 4] * 1) with self.assertRaises(ValueError): # wrong arr_dims (dims too large) r = util.tensor_transpose(arr, order, [[3] * 4] * 2) with self.assertRaises(ValueError): # wrong order (too few axes) r = util.tensor_transpose(arr, (0, 1, 2), arr_dims) with self.assertRaises(ValueError): # wrong order (index 4 too large) r = util.tensor_transpose(arr, (1, 2, 3, 4), arr_dims) with self.assertRaises(ValueError): # wrong order (not unique axes) r = util.tensor_transpose(arr, (1, 1, 1, 1), arr_dims) with self.assertRaises(TypeError): # wrong order (floats instead of ints) r = util.tensor_transpose(arr, (0., 1., 2., 3.), arr_dims) # random tests for rank, n_args, n_broadcast in zip(rng.randint(1, 4, 10), rng.randint(3, 6, 10), rng.randint(1, 11, 10)): arrs = rng.standard_normal((n_args, n_broadcast, *[2] * rank)) order = rng.permutation(n_args) arr_dims = [[2] * n_args] * rank r = util.tensor_transpose(util.tensor(*arrs, rank=rank), order=order, arr_dims=arr_dims, rank=rank) self.assertArrayAlmostEqual(r, util.tensor(*arrs[order], rank=rank))
def test_cexp(self): """Fast complex exponential.""" x = rng.standard_normal((50, 100)) a = util.cexp(x) b = np.exp(1j * x) self.assertArrayAlmostEqual(a, b) a = util.cexp(-x) b = np.exp(-1j * x) self.assertArrayAlmostEqual(a, b)
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_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_symmetrize_spectrum(self): pulse = PulseSequence( [[util.paulis[1], [np.pi/2]]], [[util.paulis[1], [1]]], [abs(rng.standard_normal())] ) asym_omega = util.get_sample_frequencies(pulse, symmetric=False, n_samples=100) sym_omega = util.get_sample_frequencies(pulse, symmetric=True, n_samples=200) S_symmetrized, omega_symmetrized = util.symmetrize_spectrum( 1/asym_omega**0.7, asym_omega) self.assertArrayEqual(omega_symmetrized, sym_omega) self.assertArrayEqual(S_symmetrized[99::-1], S_symmetrized[100:]) self.assertArrayEqual(S_symmetrized[100:]*2, 1/asym_omega**0.7) # zero frequency not doubled omega = np.arange(10) S_sym, omega_sym = util.symmetrize_spectrum(omega, omega) self.assertArrayEqual(S_sym, np.abs(np.arange(-9, 10)/2)) self.assertArrayEqual(omega_sym, np.arange(-9, 10))
def test_tensor(self): shapes = [(1, 2, 3, 4, 5), (5, 4, 3, 2, 1)] A = rng.standard_normal(shapes[0]) B = rng.standard_normal(shapes[1]) with self.assertRaises(ValueError): util.tensor(A, B) shapes = [(3, 2, 1), (3, 4, 2)] A = rng.standard_normal(shapes[0]) B = rng.standard_normal(shapes[1]) with self.assertRaises(ValueError): util.tensor(A, B, rank=1) self.assertEqual(util.tensor(A, B, rank=2).shape, (3, 8, 2)) self.assertEqual(util.tensor(A, B, rank=3).shape, (9, 8, 2)) shapes = [(10, 1, 3, 2), (10, 1, 2, 3)] A = rng.standard_normal(shapes[0]) B = rng.standard_normal(shapes[1]) self.assertEqual(util.tensor(A, B).shape, (10, 1, 6, 6)) shapes = [(3, 5, 4, 4), (3, 5, 4, 4)] A = rng.standard_normal(shapes[0]) B = rng.standard_normal(shapes[1]) self.assertEqual(util.tensor(A, B).shape, (3, 5, 16, 16)) d = rng.randint(2, 9) eye = np.eye(d) for i in range(d): for j in range(d): A, B = eye[i:i + 1, :], eye[:, j:j + 1] self.assertArrayEqual(util.tensor(A, B, rank=1), np.kron(A, B)) i, j = rng.randint(0, 4, (2, )) A, B = util.paulis[i], util.paulis[j] self.assertArrayEqual(util.tensor(A, B), np.kron(A, B)) args = [ rng.standard_normal((4, 1, 2)), rng.standard_normal((3, 2)), rng.standard_normal((4, 3, 5)) ] self.assertEqual(util.tensor(*args, rank=1).shape, (4, 3, 20)) self.assertEqual(util.tensor(*args, rank=2).shape, (4, 9, 20)) self.assertEqual(util.tensor(*args, rank=3).shape, (16, 9, 20)) args = [rng.standard_normal((2, 3, 4)), rng.standard_normal((4, 3))] with self.assertRaises(ValueError) as err: util.tensor(*args, rank=1) msg = ('Incompatible shapes (2, 3, 4) and (4, 3) for tensor ' + 'product of rank 1.') self.assertEqual(msg, str(err.exception))
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_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_tensor_insert(self): I, X, Y, Z = util.paulis arr = util.tensor(X, I) with self.assertRaises(ValueError): # Test exception for empty args util.tensor_insert(arr, np.array([[]]), pos=0, arr_dims=[]) r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=0) self.assertArrayAlmostEqual(r, util.tensor(Y, Z, X, I)) r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=1) self.assertArrayAlmostEqual(r, util.tensor(X, Y, Z, I)) r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=2) self.assertArrayAlmostEqual(r, util.tensor(X, I, Y, Z)) # Test pos being negative r = util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=-1) self.assertArrayAlmostEqual(r, util.tensor(X, Y, Z, I)) # Test pos exception with self.assertRaises(IndexError) as err: util.tensor_insert(arr, Y, Z, arr_dims=[[2, 2], [2, 2]], pos=3) msg = 'Invalid position 3 specified. Must be between -2 and 2.' self.assertEqual(msg, str(err.exception)) # Test broadcasting and rank != 2 A, B, C = (rng.standard_normal( (2, 3, 1, 2)), rng.standard_normal( (2, 3, 1, 2)), rng.standard_normal((3, 1, 3))) arr = util.tensor(A, C, rank=1) r = util.tensor_insert(arr, B, pos=1, rank=1, arr_dims=[[2, 3]]) self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=1)) # Test exceptions for wrong arr_dims format with self.assertRaises(ValueError): util.tensor_insert(arr, B, pos=1, rank=1, arr_dims=[[3, 3], [1, 2], [2, 1]]) with self.assertRaises(ValueError): util.tensor_insert(arr, B, pos=1, rank=1, arr_dims=[[2], [2, 1]]) A, B, C = (rng.standard_normal( (2, 3, 1, 2)), rng.standard_normal( (2, 3, 2, 2)), rng.standard_normal((3, 2, 1))) arr = util.tensor(A, C, rank=3) r = util.tensor_insert(arr, B, pos=1, rank=3, arr_dims=[[3, 3], [1, 2], [2, 1]]) self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=3)) # Test exceptions for wrong arr_dims format with self.assertRaises(ValueError): util.tensor_insert(arr, B, pos=1, rank=3, arr_dims=[[1, 2], [2, 1]]) with self.assertRaises(ValueError): util.tensor_insert(arr, B, pos=1, rank=2, arr_dims=[[3, 3, 1], [1, 2], [2]]) A, B, C = (rng.standard_normal((2, 1)), rng.standard_normal( (1, 2, 3)), rng.standard_normal(1)) arr = util.tensor(A, C, rank=1) r = util.tensor_insert(arr, B, pos=0, rank=1, arr_dims=[[1, 1]]) self.assertArrayAlmostEqual(r, util.tensor(A, B, C, rank=1)) arrs, args = [ rng.standard_normal((2, 2, 2)), rng.standard_normal((2, 2, 2)) ] arr_dims = [[2, 2], [2, 2]] r = util.tensor_insert(util.tensor(*arrs), *args, pos=(0, 1), arr_dims=arr_dims) self.assertArrayAlmostEqual( r, util.tensor(args[0], arrs[0], args[1], arrs[1])) r = util.tensor_insert(util.tensor(*arrs), *args, pos=(0, 0), arr_dims=arr_dims) self.assertArrayAlmostEqual(r, util.tensor(*args, *arrs)) r = util.tensor_insert(util.tensor(*arrs), *args, pos=(1, 2), arr_dims=arr_dims) self.assertArrayAlmostEqual( r, util.tensor(*np.insert(arrs, (1, 2), args, axis=0))) # Test exception for wrong pos argument with self.assertRaises(ValueError): util.tensor_insert(util.tensor(*arrs), *args, pos=(0, 1, 2), arr_dims=arr_dims) # Test exception for wrong shapes arrs, args = (rng.standard_normal( (2, 4, 3, 2)), rng.standard_normal((2, 2, 3, 4))) with self.assertRaises(ValueError) as err: util.tensor_insert(util.tensor(*arrs), *args, pos=(1, 2), arr_dims=[[3, 3], [2, 2]]) err_msg = ('Could not insert arg 0 with shape (4, 9, 4) into the ' + 'array with shape (2, 3, 4) at position 1.') cause_msg = ('Incompatible shapes (2, 3, 4) and (4, 9, 4) for ' + 'tensor product of rank 2.') self.assertEqual(err_msg, str(err.exception)) self.assertEqual(cause_msg, str(err.exception.__cause__)) # Do some random tests for rank, n_args, n_broadcast in zip(rng.randint(1, 4, 10), rng.randint(3, 6, 10), rng.randint(1, 11, 10)): arrs = rng.randn(n_args, n_broadcast, *[2] * rank) split_idx = rng.randint(1, n_args - 1) ins_idx = rng.randint(split_idx - n_args, n_args - split_idx) ins_arrs = arrs[:split_idx] arr = util.tensor(*arrs[split_idx:], rank=rank) sorted_arrs = np.insert(arrs[split_idx:], ins_idx, ins_arrs, axis=0) arr_dims = [[2] * (n_args - split_idx)] * rank r = util.tensor_insert(arr, *ins_arrs, pos=ins_idx, rank=rank, arr_dims=arr_dims) self.assertArrayAlmostEqual(r, util.tensor(*sorted_arrs, rank=rank)) pos = rng.randint(-split_idx + 1, split_idx, split_idx) r = util.tensor_insert(arr, *ins_arrs, pos=pos, rank=rank, arr_dims=arr_dims) sorted_arrs = np.insert(arrs[split_idx:], pos, ins_arrs, axis=0) self.assertArrayAlmostEqual(r, util.tensor(*sorted_arrs, rank=rank), atol=1e-10)
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_abs2(self): x = rng.standard_normal((20, 100)) + 1j * rng.standard_normal( (20, 100)) self.assertArrayAlmostEqual(np.abs(x)**2, util.abs2(x))
def test_tensor_merge(self): # Test basic functionality I, X, Y, Z = util.paulis arr = util.tensor(X, Y, Z) ins = util.tensor(I, I) r1 = util.tensor_merge(arr, ins, pos=[1, 2], arr_dims=[[2] * 3, [2] * 3], ins_dims=[[2] * 2, [2] * 2]) r2 = util.tensor_merge(ins, arr, pos=[0, 1, 2], arr_dims=[[2] * 2, [2] * 2], ins_dims=[[2] * 3, [2] * 3]) self.assertArrayAlmostEqual(r1, util.tensor(X, I, Y, I, Z)) self.assertArrayAlmostEqual(r1, r2) # Test if tensor_merge and tensor_insert produce same results arr = util.tensor(Y, Z) ins = util.tensor(I, X) r1 = util.tensor_merge(arr, ins, pos=[0, 0], arr_dims=[[2] * 2, [2] * 2], ins_dims=[[2] * 2, [2] * 2]) r2 = util.tensor_insert(arr, I, X, pos=[0, 0], arr_dims=[[2] * 2, [2] * 2]) self.assertArrayAlmostEqual(r1, r2) # Test pos being negative r = util.tensor_merge(arr, ins, arr_dims=[[2, 2], [2, 2]], ins_dims=[[2, 2], [2, 2]], pos=(-1, -2)) self.assertArrayAlmostEqual(r, util.tensor(X, Y, I, Z)) # Test exceptions # Wrong dims format with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 2], [2, 2], [2, 2]], ins_dims=[[2, 2], [2, 2]]) with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 2], [2, 2]], ins_dims=[[2, 2], [2, 2], [2, 2]]) with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 2], [2, 2, 2]], ins_dims=[[2, 2], [2, 2]]) with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 2], [2, 2]], ins_dims=[[2, 2], [2, 2, 2]]) # Wrong pos with self.assertRaises(IndexError): util.tensor_merge(arr, ins, pos=(1, 3), arr_dims=[[2, 2], [2, 2]], ins_dims=[[2, 2], [2, 2]]) # Wrong dimensions given with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 3], [2, 2]], ins_dims=[[2, 2], [2, 2]]) with self.assertRaises(ValueError): util.tensor_merge(arr, ins, pos=(1, 2), arr_dims=[[2, 2], [2, 2]], ins_dims=[[2, 3], [2, 2]]) # Incompatible shapes arrs, args = (rng.standard_normal( (2, 4, 3, 2)), rng.standard_normal((2, 2, 3, 4))) with self.assertRaises(ValueError) as err: util.tensor_merge(util.tensor(*arrs), util.tensor(*args), pos=(1, 2), arr_dims=[[3, 3], [2, 2]], ins_dims=[[3, 3], [4, 4]]) msg = ('Incompatible shapes (2, 9, 16) and (4, 9, 4) for tensor ' + 'product of rank 2.') self.assertEqual(msg, str(err.exception)) # Test rank 1 and broadcasting arr = rng.standard_normal((2, 10, 3, 4)) ins = rng.standard_normal((2, 10, 3, 2)) r = util.tensor_merge(util.tensor(*arr, rank=1), util.tensor(*ins, rank=1), pos=[0, 1], arr_dims=[[4, 4]], ins_dims=[[2, 2]], rank=1) self.assertArrayAlmostEqual( r, util.tensor(ins[0], arr[0], ins[1], arr[1], rank=1)) # Do some random tests for rank, n_args, n_broadcast in zip(rng.randint(1, 4, 10), rng.randint(3, 6, 10), rng.randint(1, 11, 10)): arrs = rng.standard_normal((n_args, n_broadcast, *[2] * rank)) split_idx = rng.randint(1, n_args - 1) arr = util.tensor(*arrs[split_idx:], rank=rank) ins = util.tensor(*arrs[:split_idx], rank=rank) pos = rng.randint(0, split_idx, split_idx) sorted_arrs = np.insert(arrs[split_idx:], pos, arrs[:split_idx], axis=0) arr_dims = [[2] * (n_args - split_idx)] * rank ins_dims = [[2] * split_idx] * rank r = util.tensor_merge(arr, ins, pos=pos, rank=rank, arr_dims=arr_dims, ins_dims=ins_dims) self.assertArrayAlmostEqual(r, util.tensor(*sorted_arrs, rank=rank))
def test_pulse_sequence_attributes(self): """Test attributes of single instance""" X, Y, Z = util.paulis[1:] n_dt = rng.randint(1, 10) # trivial case A = ff.PulseSequence([[X, rng.standard_normal(n_dt), 'X']], [[Z, rng.standard_normal(n_dt), 'Z']], np.abs(rng.standard_normal(n_dt))) self.assertFalse(A == 1) self.assertTrue(A != 1) # different number of time steps B = ff.PulseSequence([[X, rng.standard_normal(n_dt+1), 'X']], [[Z, rng.standard_normal(n_dt+1), 'Z']], np.abs(rng.standard_normal(n_dt+1))) self.assertFalse(A == B) self.assertTrue(A != B) # different time steps B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)), list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)), np.abs(rng.standard_normal(n_dt)) ) self.assertFalse(A == B) self.assertTrue(A != B) # different control opers B = ff.PulseSequence( list(zip([Y], A.c_coeffs, A.c_oper_identifiers)), list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different control coeffs B = ff.PulseSequence( list(zip(A.c_opers, [rng.standard_normal(n_dt)], A.c_oper_identifiers)), list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different noise opers B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)), list(zip([Y], A.n_coeffs, A.n_oper_identifiers)), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different noise coeffs B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)), list(zip(A.n_opers, [rng.standard_normal(n_dt)], A.n_oper_identifiers)), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different control oper identifiers B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, ['foobar'])), list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different noise oper identifiers B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)), list(zip(A.n_opers, A.n_coeffs, ['foobar'])), A.dt ) self.assertFalse(A == B) self.assertTrue(A != B) # different bases elem = testutil.rand_herm_traceless(2) B = ff.PulseSequence( list(zip(A.c_opers, A.c_coeffs, A.c_oper_identifiers)), list(zip(A.n_opers, A.n_coeffs, A.n_oper_identifiers)), A.dt, ff.Basis(elem) ) self.assertFalse(A == B) self.assertTrue(A != B) # Test sparse operators for whatever reason A = ff.PulseSequence([[util.paulis[1], [1]]], [[sparse.COO.from_numpy(util.paulis[2]), [2]]], [3]) B = ff.PulseSequence([[sparse.COO.from_numpy(util.paulis[1]), [1]]], [[util.paulis[2], [2]]], [3]) self.assertEqual(A, B) # Test for attributes for attr in A.__dict__.keys(): if not (attr.startswith('_') or '_' + attr in A.__dict__.keys()): # not a cached attribute with self.assertRaises(AttributeError): _ = A.is_cached(attr) else: # set mock attribute at random if rng.randint(0, 2): setattr(A, attr, 'foo') assertion = self.assertTrue else: setattr(A, attr, None) assertion = self.assertFalse assertion(A.is_cached(attr)) # Diagonalization attributes A.diagonalize() self.assertIsNotNone(A.eigvals) self.assertIsNotNone(A.eigvecs) self.assertIsNotNone(A.propagators) A.cleanup('conservative') self.assertIsNotNone(A.eigvals) A.cleanup('conservative') self.assertIsNotNone(A.eigvecs) A.cleanup('conservative') self.assertIsNotNone(A.propagators) aliases = {'eigenvalues': '_eigvals', 'eigenvectors': '_eigvecs', 'total propagator': '_total_propagator', 'total propagator liouville': '_total_propagator_liouville', 'frequencies': '_omega', 'total phases': '_total_phases', 'filter function': '_filter_function', 'fidelity filter function': '_filter_function', 'generalized filter function': '_filter_function_gen', 'pulse correlation filter function': '_filter_function_pc', 'fidelity pulse correlation filter function': '_filter_function_pc', 'generalized pulse correlation filter function': '_filter_function_pc_gen', 'control matrix': '_control_matrix', 'pulse correlation control matrix': '_control_matrix_pc'} for alias, attr in aliases.items(): # set mock attribute at random if rng.randint(0, 2): setattr(A, attr, 'foo') assertion = self.assertTrue else: setattr(A, attr, None) assertion = self.assertFalse assertion(A.is_cached(alias)) assertion(A.is_cached(alias.upper())) assertion(A.is_cached(alias.replace(' ', '_'))) A.cleanup('all') # Test cleanup C = ff.concatenate((A, A), calc_pulse_correlation_FF=True, which='generalized', omega=util.get_sample_frequencies(A)) C.diagonalize() attrs = ['_eigvals', '_eigvecs', '_propagators'] for attr in attrs: self.assertIsNotNone(getattr(C, attr)) C.cleanup() for attr in attrs: self.assertIsNone(getattr(C, attr)) C.diagonalize() C.cache_control_matrix(A.omega) attrs.extend(['_control_matrix', '_total_phases', '_total_propagator', '_total_propagator_liouville']) for attr in attrs: self.assertIsNotNone(getattr(C, attr)) C.cleanup('greedy') for attr in attrs: self.assertIsNone(getattr(C, attr)) C.cache_filter_function(A.omega, which='generalized') for attr in attrs + ['omega', '_filter_function_gen', '_filter_function_pc_gen']: self.assertIsNotNone(getattr(C, attr)) C = ff.concatenate((A, A), calc_pulse_correlation_FF=True, which='fidelity', omega=A.omega) C.diagonalize() C.cache_filter_function(A.omega, which='fidelity') attrs.extend(['omega', '_filter_function', '_filter_function_pc']) for attr in attrs: self.assertIsNotNone(getattr(C, attr)) C.cleanup('all') for attr in attrs + ['_filter_function_gen', '_filter_function_pc_gen']: self.assertIsNone(getattr(C, attr)) C.cache_filter_function(A.omega, which='fidelity') C.cleanup('frequency dependent') freq_attrs = {'omega', '_control_matrix', '_filter_function', '_filter_function_gen', '_filter_function_pc', '_filter_function_pc_gen', '_total_phases'} for attr in freq_attrs: self.assertIsNone(getattr(C, attr)) for attr in set(attrs).difference(freq_attrs): self.assertIsNotNone(getattr(C, attr))
def 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_infidelity(self): """Benchmark infidelity results against previous version's results""" rng.seed(123456789) spectra = [ lambda S0, omega: S0 * abs(omega)**0, lambda S0, omega: S0 / abs(omega)**0.7, lambda S0, omega: S0 * np.exp(-abs(omega)), # different spectra for different n_opers lambda S0, omega: np.array( [S0 * abs(omega)**0, S0 / abs(omega)**0.7]), # cross-correlated spectra lambda S0, omega: np.array([[ S0 / abs(omega)**0.7, S0 / (1 + omega**2) + 1j * S0 * omega ], [S0 / (1 + omega**2) - 1j * S0 * omega, S0 / abs(omega)**0.7]]) ] ref_infids = ([[0.415494970094, 0.89587362496], [0.493004378474, 0.812378971328], [0.133466914361, 0.197411969384], [0.415494970094, 0.812378971328], [[0.493004378474, 0.435140425045], [0.435140425045, 0.812378971328]], [3.62995021962, 2.938710386281], [2.302617869945, 2.6187737025], [0.506821680978, 0.695495602872], [3.62995021962, 2.6187737025], [[2.302617869945, 0.58515469294], [0.58515469294, 2.6187737025]], [2.822636459567, 1.205901937127], [1.63758822101, 1.236844976323], [0.324175447082, 0.329789052239], [2.822636459567, 1.236844976323], [[1.63758822101, 0.72007826813], [0.72007826813, 1.236844976323]]]) count = 0 for d in (2, 3, 4): pulse = testutil.rand_pulse_sequence(d, 10, 2, 3) pulse.n_oper_identifiers = np.array(['B_0', 'B_2']) omega = np.geomspace(0.1, 10, 51) S0 = np.abs(rng.standard_normal()) for spec in spectra: S = spec(S0, omega) infids = ff.infidelity(pulse, S, omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(infids, ref_infids[count], atol=1e-12) if S.ndim == 3: # Diagonal of the infidelity matrix should correspond to # uncorrelated terms uncorrelated_infids = ff.infidelity( pulse, S[range(2), range(2)], omega, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(np.diag(infids), uncorrelated_infids) # Infidelity matrix should be hermitian self.assertArrayEqual(infids, infids.conj().T) count += 1 # Check raises with self.assertRaises(TypeError): # spectrum not callable ff.infidelity(pulse, 2, omega, test_convergence=True) with self.assertRaises(TypeError): # omega not dict ff.infidelity(pulse, lambda x: x, 2, test_convergence=True) with self.assertRaises(ValueError): # omega['spacing'] not in ('linear', 'log') ff.infidelity(pulse, lambda x: x, {'spacing': 2}, test_convergence=True) with self.assertRaises(ValueError): # which not total or correlation ff.infidelity(pulse, spectra[0](S0, omega), omega, which=2) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[0](S0, omega)[:10], omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[3](S0, omega), omega, n_oper_identifiers=['B_0', 'B_1', 'B_2']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[4](S0, omega)[:, [0]], omega, n_oper_identifiers=['B_0']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, rng.standard_normal((2, 3, 4, len(omega))), omega) with self.assertRaises(NotImplementedError): # smallness parameter for correlated noise source ff.infidelity(pulse, spectra[4](S0, omega), omega, n_oper_identifiers=['B_0', 'B_2'], return_smallness=True)
def test_basis_constructor(self): """Test the constructor for several failure modes""" # Constructing from given elements should check for __getitem__ with self.assertRaises(TypeError): _ = ff.Basis(1) # All elements should be either sparse, Qobj, or ndarray elems = [ util.paulis[1], COO.from_numpy(util.paulis[3]), [[0, 1], [1, 0]] ] with self.assertRaises(TypeError): _ = ff.Basis(elems) # Too many elements with self.assertRaises(ValueError): _ = ff.Basis(rng.standard_normal((5, 2, 2))) # Non traceless elems but traceless basis requested with self.assertRaises(ValueError): _ = ff.Basis.from_partial(np.ones((2, 2)), traceless=True) # Incorrect number of labels for default constructor with self.assertRaises(ValueError): _ = ff.Basis(util.paulis, labels=['a', 'b', 'c']) # Incorrect number of labels for from_partial constructor with self.assertRaises(ValueError): _ = ff.Basis.from_partial(util.paulis[:2], labels=['a', 'b', 'c']) # from_partial constructor should move identity label to the front basis = ff.Basis.from_partial([util.paulis[1], util.paulis[0]], labels=['x', 'i']) self.assertEqual(basis.labels[:2], ['i', 'x']) self.assertEqual(basis.labels[2:], ['$C_{2}$', '$C_{3}$']) # from_partial constructor should copy labels if it can partial_basis = ff.Basis.pauli(1)[[1, 3]] partial_basis.labels = [ partial_basis.labels[1], partial_basis.labels[3] ] basis = ff.Basis.from_partial(partial_basis) self.assertEqual(basis.labels[:2], partial_basis.labels) self.assertEqual(basis.labels[2:], ['$C_{2}$', '$C_{3}$']) # Default constructor should return 3d array also for single 2d element basis = ff.Basis(rng.standard_normal((2, 2))) self.assertEqual(basis.shape, (1, 2, 2)) # from_partial constructor should return same basis for 2d or 3d input elems = testutil.rand_herm(3) basis1 = ff.Basis.from_partial(elems, labels=['weif']) basis2 = ff.Basis.from_partial(elems.squeeze(), labels=['weif']) self.assertEqual(basis1, basis2) # Calling with only the identity should work with traceless true or false self.assertEqual(ff.Basis(np.eye(2), traceless=False), ff.Basis(np.eye(2), traceless=True)) # Constructing a basis from a basis should work _ = ff.Basis(ff.Basis.ggm(2)[1:]) # Constructing should not change the elements elems = rng.standard_normal((6, 3, 3)) basis = ff.Basis(elems) self.assertArrayEqual(elems, basis)
def test_basis_expansion_and_normalization(self): """Correct expansion of operators and normalization of bases""" # dtype b = ff.Basis.ggm(3) r = ff.basis.expand(rng.standard_normal((3, 3)), b, hermitian=False) self.assertTrue(r.dtype == 'complex128') r = ff.basis.expand(testutil.rand_herm(3), b, hermitian=True) self.assertTrue(r.dtype == 'float64') b._isherm = False r = ff.basis.expand(testutil.rand_herm(3), b, hermitian=True) self.assertTrue(r.dtype == 'complex128') r = ff.basis.ggm_expand(testutil.rand_herm(3), hermitian=True) self.assertTrue(r.dtype == 'float64') r = ff.basis.ggm_expand(rng.standard_normal((3, 3)), hermitian=False) self.assertTrue(r.dtype == 'complex128') for _ in range(10): d = rng.integers(2, 16) ggm_basis = ff.Basis.ggm(d) basis = ff.Basis( np.einsum('i,ijk->ijk', rng.standard_normal(d**2), ggm_basis)) M = rng.standard_normal((d, d)) + 1j * rng.standard_normal((d, d)) M -= np.trace(M) / d coeffs = ff.basis.expand(M, basis, normalized=False) self.assertArrayAlmostEqual(M, np.einsum('i,ijk', coeffs, basis)) self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis), ff.basis.ggm_expand(M), atol=1e-14) self.assertArrayAlmostEqual(ff.basis.ggm_expand(M), ff.basis.ggm_expand(M, traceless=True), atol=1e-14) n = rng.integers(1, 50) M = rng.standard_normal((n, d, d)) + 1j * rng.standard_normal( (n, d, d)) coeffs = ff.basis.expand(M, basis, normalized=False) self.assertArrayAlmostEqual( M, np.einsum('li,ijk->ljk', coeffs, basis)) self.assertArrayAlmostEqual(ff.basis.expand(M, ggm_basis), ff.basis.ggm_expand(M), atol=1e-14) # Argument to ggm_expand not square in last two dimensions with self.assertRaises(ValueError): ff.basis.ggm_expand(basis[..., 0]) self.assertTrue(ff.basis.normalize(basis).isorthonorm) # Basis method and function should give the same normalized = ff.basis.normalize(basis) basis.normalize() self.assertEqual(normalized, basis) # normalize single element elem = basis[1] normalized = ff.basis.normalize(elem) elem.normalize() self.assertEqual(normalized, elem) # Not matrix or sequence of matrices with self.assertRaises(ValueError): ff.basis.normalize(basis[0, 0]) # Test copy arr = rng.standard_normal((3, 2, 2)) basis = ff.Basis(arr) normalized = basis.normalize(copy=True) self.assertIsNot(normalized, basis) self.assertFalse(np.array_equal(normalized, basis)) basis.normalize() self.assertArrayEqual(normalized, basis)
def test_mdot(self): arr = rng.standard_normal((3, 2, 4, 4)) self.assertArrayEqual(util.mdot(arr, 0), arr[0] @ arr[1] @ arr[2]) self.assertArrayEqual(util.mdot(arr, 1), arr[:, 0] @ arr[:, 1])
def test_infidelity(self): """Benchmark infidelity results against previous version's results""" rng.seed(123456789) spectra = [ lambda S0, omega: S0 * omega**0, lambda S0, omega: S0 / omega**0.7, lambda S0, omega: S0 * np.exp(-omega), # different spectra for different n_opers lambda S0, omega: np.array([S0 * omega**0, S0 / omega**0.7]), # cross-correlated spectra lambda S0, omega: np.array([[ S0 / omega**0.7, (1 + 1j) * S0 * np.exp(-omega) ], [(1 - 1j) * S0 * np.exp(-omega), S0 / omega**0.7]]) ] ref_infids = ([0.448468950307, 0.941871479562], [0.65826575772, 1.042914346335], [0.163303005479, 0.239032549377], [0.448468950307, 1.042914346335], [[0.65826575772, 0.069510589685 + 0.069510589685j], [0.069510589685 - 0.069510589685j, 1.042914346335]], [3.687399348243, 3.034914820757], [2.590545568435, 3.10093804628], [0.55880380219, 0.782544974968 ], [3.687399348243, 3.10093804628], [[2.590545568435, -0.114514760108 - 0.114514760108j], [-0.114514760108 + 0.114514760108j, 3.10093804628]], [2.864567451344, 1.270260393902], [ 1.847740998731, 1.559401345443 ], [0.362116177417, 0.388022992097], [2.864567451344, 1.559401345443], [[1.847740998731, 0.088373663409 + 0.088373663409j], [0.088373663409 - 0.088373663409j, 1.559401345443]]) count = 0 for d in (2, 3, 4): pulse = testutil.rand_pulse_sequence(d, 10, 2, 3) pulse.n_oper_identifiers = np.array(['B_0', 'B_2']) omega = np.geomspace(0.1, 10, 51) S0 = np.abs(rng.standard_normal()) for spec in spectra: S, omega_t = ff.util.symmetrize_spectrum( spec(S0, omega), omega) infids = ff.infidelity(pulse, S, omega_t, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(infids, ref_infids[count], atol=1e-12) if S.ndim == 3: # Diagonal of the infidelity matrix should correspond to # uncorrelated terms uncorrelated_infids = ff.infidelity( pulse, S[range(2), range(2)], omega_t, n_oper_identifiers=['B_0', 'B_2']) self.assertArrayAlmostEqual(np.diag(infids), uncorrelated_infids) # Infidelity matrix should be hermitian self.assertArrayEqual(infids, infids.conj().T) count += 1 # Check raises with self.assertRaises(TypeError): # spectrum not callable ff.infidelity(pulse, 2, omega_t, test_convergence=True) with self.assertRaises(TypeError): # omega not dict ff.infidelity(pulse, lambda x: x, 2, test_convergence=True) with self.assertRaises(ValueError): # omega['spacing'] not in ('linear', 'log') ff.infidelity(pulse, lambda x: x, {'spacing': 2}, test_convergence=True) with self.assertRaises(ValueError): # which not total or correlation ff.infidelity(pulse, spectra[0](S0, omega_t), omega, which=2) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[0](S0, omega_t)[:10], omega) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[3](S0, omega), omega, n_oper_identifiers=['B_0', 'B_1', 'B_2']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, spectra[4](S0, omega)[:, [0]], omega, n_oper_identifiers=['B_0']) with self.assertRaises(ValueError): # S wrong dimensions ff.infidelity(pulse, rng.standard_normal((2, 3, 4, len(omega))), omega) with self.assertRaises(NotImplementedError): # smallness parameter for correlated noise source ff.infidelity(pulse, spectra[4](S0, omega), omega, n_oper_identifiers=['B_0', 'B_2'], return_smallness=True)