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_basis_generation_from_partial_pauli(self): """"Generate complete basis from partial elements of a Pauli basis""" # Do 100 test runs with random elements from a Pauli basis in (2 ... 8) # dimensions for _ in range(50): n = rng.randint(1, 4) d = 2**n b = ff.Basis.pauli(n) inds = [i for i in range(d**2)] tup = tuple( inds.pop(rng.randint(0, len(inds))) for _ in range(rng.randint(1, d**2))) elems = b[tup, ...] basis = ff.Basis(elems) self.assertTrue(basis.isorthonorm) self.assertTrue(basis.isherm) self.assertTrue(basis.istraceless) self.assertTrue(basis.iscomplete) self.assertTrue(all(elem in basis for elem in elems)) with self.assertWarns(UserWarning): b = [basis[0], 1j * basis[1]] ff.Basis(b) with self.assertRaises(ValueError): b = [basis[0], basis[0] + basis[1]] ff.Basis(b)
def test_constructor(self): """Test if can create basis from qutip objects.""" elems_qutip = [qutip.sigmay(), qutip.qeye(2).data] elems_np = [qutip.sigmay().full(), qutip.qeye(2).full()] basis_qutip = ff.Basis(elems_qutip) basis_np = ff.Basis(elems_np) self.assertArrayEqual(basis_qutip, basis_np)
def test_basis_generation_from_partial_random(self): """"Generate complete basis from partial elements of a random basis""" # Do 25 test runs with random elements from a random basis in # (2 ... 8) dimensions for _ in range(25): d = rng.randint(2, 7) # Get a random traceless hermitian operator oper = testutil.rand_herm_traceless(d) # ... and build a basis from it b = ff.Basis(oper) self.assertTrue(b.isorthonorm) self.assertTrue(b.isherm) self.assertTrue(b.istraceless) self.assertTrue(b.iscomplete) # Choose random elements from that basis and generate a new basis # from it inds = [i for i in range(d**2)] tup = tuple( inds.pop(rng.randint(0, len(inds))) for _ in range(rng.randint(1, d**2))) elems = b[tup, ...] basis = ff.Basis(elems) self.assertTrue(basis.isorthonorm) self.assertTrue(basis.isherm) self.assertTrue(basis.istraceless) self.assertTrue(basis.iscomplete) self.assertTrue(all(elem in basis for elem in elems)) # Test runs with non-traceless opers for _ in range(25): d = rng.randint(2, 7) # Get a random hermitian operator oper = testutil.rand_herm(d) # ... and build a basis from it b = ff.Basis(oper) self.assertTrue(b.isorthonorm) self.assertTrue(b.isherm) self.assertFalse(b.istraceless) self.assertTrue(b.iscomplete) # Choose random elements from that basis and generate a new basis # from it inds = [i for i in range(d**2)] tup = tuple( inds.pop(rng.randint(0, len(inds))) for _ in range(rng.randint(1, d**2))) elems = b[tup, ...] basis = ff.Basis(elems) self.assertTrue(basis.isorthonorm) self.assertTrue(basis.isherm) self.assertFalse(basis.istraceless) self.assertTrue(basis.iscomplete) self.assertTrue(all(elem in basis for elem in elems))
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_infidelity_cnot(self): """Compare infidelity to monte carlo results""" c_opers = testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = testutil.c_coeffs, testutil.n_coeffs dt = testutil.dt infid_MC = testutil.cnot_infid_fast A = testutil.A # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)], skip_check=True, btype='Pauli') complete_basis = ff.Basis(qubit_subspace_basis, traceless=False, btype='Pauli') identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list(zip(c_opers, c_coeffs, identifiers)) H_n = list(zip(n_opers, n_coeffs, identifiers)) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) cnot_full = ff.PulseSequence(H_c, H_n, dt, basis=complete_basis) # Manually set dimension of pulse as the dimension of the computational # subspace cnot.d = 4 T = dt.sum() for f_min, A, alpha, MC, rtol in zip((1 / T, 1e-2 / T), A, (0.0, 0.7), infid_MC, (0.04, 0.02)): omega = np.geomspace(f_min, 1e2, 250) * 2 * np.pi S_t, omega_t = ff.util.symmetrize_spectrum(A / omega**alpha, omega) infid, xi = ff.infidelity(cnot, S_t, omega_t, identifiers[:3], return_smallness=True) U = ff.error_transfer_matrix(cnot_full, S_t, omega_t, identifiers[:3]) infid_P = np.trace(U[:, :16, :16], axis1=1, axis2=2).real / 4**2 print(np.abs(1 - (infid.sum() / MC))) print(np.abs(1 - (infid_P.sum() / MC))) self.assertLessEqual(np.abs(1 - (infid.sum() / MC)), rtol) self.assertLessEqual(np.abs(1 - (infid_P.sum() / MC)), rtol) self.assertLessEqual(infid.sum(), xi**2 / 4)
def test_infidelity_cnot(self): """Compare infidelity to monte carlo results""" c_opers = testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = testutil.c_coeffs, testutil.n_coeffs dt = testutil.dt infid_MC = testutil.cnot_infid_fast A = testutil.A # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)[1:]], skip_check=True, btype='Pauli') complete_basis = ff.Basis(qubit_subspace_basis, traceless=False, btype='Pauli') identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list(zip(c_opers, c_coeffs, identifiers)) H_n = list(zip(n_opers, n_coeffs, identifiers)) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) cnot_full = ff.PulseSequence(H_c, H_n, dt, basis=complete_basis) # Manually set dimension of pulse as the dimension of the computational # subspace cnot.d = 4 f_min = 1 / cnot.tau omega = np.geomspace(f_min, 1e2, 250) for A, alpha, MC in zip(A, (0.0, 0.7), infid_MC): S = A / omega**alpha infid, xi = ff.infidelity(cnot, S, omega, identifiers[:3], return_smallness=True) K = numeric.calculate_cumulant_function(cnot_full, S, omega, identifiers[:3]) infid_P = -np.trace(K[:, :16, :16], axis1=1, axis2=2).real / 4**2 self.assertLessEqual(np.abs(1 - (infid.sum() / MC)), 0.10) self.assertLessEqual(np.abs(1 - (infid_P.sum() / MC)), 0.10) self.assertLessEqual(infid.sum(), xi**2 / 4)
def test_basis_properties(self): """Basis orthonormal and of correct dimensions""" d = rng.randint(2, 17) n = rng.randint(1, 5) ggm_basis = ff.Basis.ggm(d) pauli_basis = ff.Basis.pauli(n) custom_basis = ff.Basis(testutil.rand_herm(d), traceless=False) btypes = ('Pauli', 'GGM', 'Custom') bases = (pauli_basis, ggm_basis, custom_basis) for btype, base in zip(btypes, bases): base.tidyup(eps_scale=0) self.assertTrue(base == base) self.assertFalse(base == ff.Basis.ggm(d + 1)) self.assertEqual(btype, base.btype) if not btype == 'Pauli': self.assertEqual(d, base.d) # Check if __contains__ works as expected self.assertTrue(base[rng.randint(0, d**2)] in base) else: self.assertEqual(2**n, base.d) # Check if __contains__ works as expected self.assertTrue(base[rng.randint(0, (2**n)**2)] in base) # Check if all elements of each basis are orthonormal and hermitian self.assertArrayEqual(base.T, base.view(np.ndarray).swapaxes(-1, -2)) self.assertTrue(base.isorthonorm) self.assertTrue(base.isherm) # Check if basis spans the whole space and all elems are traceless if not btype == 'Custom': self.assertTrue(base.istraceless) else: self.assertFalse(base.istraceless) self.assertTrue(base.iscomplete) # Check sparse representation self.assertArrayEqual(base.sparse.todense(), base) # Test sparse cache self.assertArrayEqual(base.sparse.todense(), base) if base.d < 8: # Test very resource intense ref = np.einsum('iab,jbc,kcd,lda', *(base, ) * 4) self.assertArrayAlmostEqual(base.four_element_traces.todense(), ref, atol=1e-16) # Test setter base._four_element_traces = None base.four_element_traces = ref self.assertArrayEqual(base.four_element_traces, ref) base._print_checks() basis = ff.util.paulis[1].view(ff.Basis) self.assertTrue(basis.isorthonorm) self.assertArrayEqual(basis.T, basis.view(np.ndarray).T)
def test_filter_functions(self): # Basis for qubit subspace qubit_subspace_basis = ff.Basis( [np.pad(b, 1, 'constant') for b in ff.Basis.pauli(2)], skip_check=True, btype='Pauli') c_opers = ff_testutil.subspace_opers n_opers = c_opers c_coeffs, n_coeffs = ff_testutil.c_coeffs, ff_testutil.n_coeffs dt = ff_testutil.dt infid_MC = ff_testutil.cnot_infid_fast A = ff_testutil.A identifiers = ['eps_12', 'eps_23', 'eps_34', 'b_12', 'b_23', 'b_34'] H_c = list( zip(c_opers[:3] + [c_opers[3] + 7 * c_opers[4] - c_opers[5]], c_coeffs[:3] + [c_coeffs[3]], identifiers[:4])) H_n = list(zip(n_opers[:3], n_coeffs[:3], identifiers[:3])) cnot = ff.PulseSequence(H_c, H_n, dt, basis=qubit_subspace_basis) T = dt.sum() omega = np.logspace(np.log10(1 / T), 2, 125) S_t, omega_t = ff.util.symmetrize_spectrum(A[0] / omega**0.0, omega) infid, xi = ff.infidelity(cnot, S_t, omega_t, identifiers[:3], return_smallness=True) # infid scaled with d = 6, but we actually have d = 4 infid *= 1.5 self.assertLessEqual(np.abs(1 - (infid.sum() / infid_MC[0])), .4) self.assertLessEqual(infid.sum(), xi**2 / 4) time_slot_comp_closed = SchroedingerSolver( h_drift=[OPERATORS['h_drift']] * len(dt), h_ctrl=OPERATORS['h_ctrl'], initial_state=OPERATORS['initial_state'], tau=list(dt), calculate_propagator_derivatives=True, exponential_method='spectral', is_skew_hermitian=True, transfer_function=id_tf, amplitude_function=exp_amp_func, filter_function_h_n=H_n, filter_function_basis=qubit_subspace_basis) time_slot_comp_closed.set_optimization_parameters(eps.T) ff_infid = OperatorFilterFunctionInfidelity( solver=time_slot_comp_closed, noise_power_spec_density=S_t, omega=omega_t) print(ff_infid.grad()) np.testing.assert_array_almost_equal(infid, ff_infid.costs() * 1.5)
def test_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_basis_generation_from_partial_ggm(self): """"Generate complete basis from partial elements of a GGM basis""" # Do 100 test runs with random elements from a GGM basis in (2 ... 8) # dimensions for _ in range(50): d = rng.randint(2, 9) b = ff.Basis.ggm(d) inds = [i for i in range(d**2)] tup = tuple( inds.pop(rng.randint(0, len(inds))) for _ in range(rng.randint(1, d**2))) elems = b[tup, ...] basis = ff.Basis(elems) self.assertTrue(basis.isorthonorm) self.assertTrue(basis.isherm) self.assertTrue(basis.istraceless) self.assertTrue(basis.iscomplete) self.assertTrue(all(elem in basis for elem in elems))
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_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_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)