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_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_properties(self): """Basis orthonormal and of correct dimensions""" d = rng.integers(2, 17) n = rng.integers(1, 5) ggm_basis = ff.Basis.ggm(d) pauli_basis = ff.Basis.pauli(n) from_partial_basis = ff.Basis.from_partial(testutil.rand_herm(d), traceless=False) custom_basis = ff.Basis(testutil.rand_herm_traceless(d)) btypes = ('Pauli', 'GGM', 'From partial', 'Custom') bases = (pauli_basis, ggm_basis, from_partial_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.integers(0, len(base))] in base) else: self.assertEqual(2**n, base.d) # Check if __contains__ works as expected self.assertTrue(base[rng.integers(0, len(base))] 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 == 'From partial': self.assertTrue(base.istraceless) else: self.assertFalse(base.istraceless) if not btype == 'Custom': 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 = util.paulis[1].view(ff.Basis) self.assertTrue(basis.isorthonorm) self.assertArrayEqual(basis.T, basis.view(np.ndarray).T)
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_different_n_opers(self): """Test behavior when concatenating with different n_opers.""" for d, n_dt in zip(rng.randint(2, 5, 20), rng.randint(1, 11, 20)): opers = testutil.rand_herm_traceless(d, 10) letters = np.array(sample(list(string.ascii_letters), 10)) n_idx = sample(range(10), rng.randint(2, 5)) c_idx = sample(range(10), rng.randint(2, 5)) n_opers = opers[n_idx] c_opers = opers[c_idx] n_coeffs = np.ones((n_opers.shape[0], n_dt)) n_coeffs *= np.abs(rng.standard_normal((n_opers.shape[0], 1))) c_coeffs = rng.standard_normal((c_opers.shape[0], n_dt)) dt = np.abs(rng.standard_normal(n_dt)) n_ids = np.array([''.join(l) for l in letters[n_idx]]) c_ids = np.array([''.join(l) for l in letters[c_idx]]) pulse_1 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)), list(zip(n_opers, n_coeffs, n_ids)), dt) permutation = rng.permutation(range(n_opers.shape[0])) pulse_2 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)), list(zip(n_opers[permutation], n_coeffs[permutation], n_ids[permutation])), dt) more_n_idx = sample(range(10), rng.randint(2, 5)) more_n_opers = opers[more_n_idx] more_n_coeffs = np.ones((more_n_opers.shape[0], n_dt)) more_n_coeffs *= np.abs(rng.standard_normal( (more_n_opers.shape[0], 1))) more_n_ids = np.array([''.join(l) for l in letters[more_n_idx]]) pulse_3 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)), list(zip(more_n_opers, more_n_coeffs, more_n_ids)), dt) nontrivial_n_coeffs = np.abs(rng.standard_normal( (n_opers.shape[0], n_dt))) pulse_4 = ff.PulseSequence(list(zip(c_opers, c_coeffs, c_ids)), list(zip(more_n_opers, nontrivial_n_coeffs, more_n_ids)), dt) omega = np.geomspace(.1, 10, 50) # Test caching with self.assertRaises(ValueError): ff.concatenate([pulse_1, pulse_3], calc_filter_function=True) with self.assertRaises(ValueError): ff.concatenate([pulse_1, pulse_3], calc_pulse_correlation_FF=True) pulse_1.cache_filter_function(omega) pulse_2.cache_filter_function(omega) pulse_3.cache_filter_function(omega) pulse_11 = ff.concatenate([pulse_1, pulse_1]) pulse_12 = ff.concatenate([pulse_1, pulse_2]) pulse_13_1 = ff.concatenate([pulse_1, pulse_3]) pulse_13_2 = ff.concatenate([pulse_1, pulse_3], calc_filter_function=True) # concatenate pulses with different n_opers and nontrivial sens. subset = (set.issubset(set(pulse_1.n_oper_identifiers), set(pulse_4.n_oper_identifiers)) or set.issubset(set(pulse_4.n_oper_identifiers), set(pulse_1.n_oper_identifiers))) if not subset and (len(pulse_1.dt) > 1 or len(pulse_4.dt) > 1): with self.assertRaises(ValueError): pulse_1 @ pulse_4 self.assertEqual(pulse_11, pulse_12) # Filter functions should be the same even though pulse_2 has # different n_oper ordering self.assertArrayAlmostEqual(pulse_11._control_matrix, pulse_12._control_matrix, atol=1e-12) self.assertArrayAlmostEqual(pulse_11._filter_function, pulse_12._filter_function, atol=1e-12) should_be_cached = False for i in n_idx: if i in more_n_idx: should_be_cached = True self.assertEqual(should_be_cached, pulse_13_1.is_cached('filter_function')) # Test forcibly caching self.assertTrue(pulse_13_2.is_cached('filter_function'))