From 674ed30334deef17ade7003d0f93a6e32ddd8578 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 8 Jun 2020 10:33:24 +0200 Subject: [PATCH 01/22] Rename error vector correlation functions --> decay amplitudes --- filter_functions/numeric.py | 95 ++++++++++++++++--------------------- tests/test_core.py | 7 ++- tests/test_precision.py | 33 ++++++------- 3 files changed, 59 insertions(+), 76 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index ec8f967..d2f6cac 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -30,9 +30,9 @@ Calculate the control matrix from scratch :func:`calculate_control_matrix_periodic` Calculate the control matrix for a periodic Hamiltonian -:func:`calculate_error_vector_correlation_functions` - Calculate the correlation functions of the 1st order Magnus expansion - coefficients +:func:`calculate_decay_amplitudes` + Calculate the decay amplitudes, corresponding to first order terms of the + Magnus expansion :func:`calculate_filter_function` Calculate the filter function from the control matrix :func:`calculate_pulse_correlation_filter_function` @@ -331,7 +331,7 @@ def calculate_control_matrix_periodic(phases: ndarray, R: ndarray, return R_tot -def calculate_error_vector_correlation_functions( +def calculate_decay_amplitudes( pulse: 'PulseSequence', S: ndarray, omega: Coefficients, @@ -339,24 +339,22 @@ def calculate_error_vector_correlation_functions( show_progressbar: Optional[bool] = False, memory_parsimonious: Optional[bool] = False) -> ndarray: r""" - Get the error vector correlation functions - :math:`\langle u_{1,k} u_{1, l}\rangle_{\alpha\beta}` for noise sources + Get the decay amplitudes :math:`\Gamma_{\alpha\beta, kl}` for noise sources :math:`\alpha,\beta` and basis elements :math:`k,l`. - Parameters ---------- pulse: PulseSequence - The ``PulseSequence`` instance for which to compute the error vector - correlation functions. + The ``PulseSequence`` instance for which to compute the decay + amplitudes. S: array_like, shape (..., n_omega) The two-sided noise power spectral density. omega: array_like, The frequencies. Note that the frequencies are assumed to be symmetric about zero. n_oper_identifiers: array_like, optional - The identifiers of the noise operators for which to calculate the error - vector correlation functions. The default is all. + The identifiers of the noise operators for which to calculate the decay + amplitudes. The default is all. show_progressbar: bool, optional Show a progress bar for the calculation. memory_parsimonious: bool, optional @@ -379,16 +377,16 @@ def calculate_error_vector_correlation_functions( Returns ------- - u_kl: ndarray, shape (..., d**2, d**2) - The error vector correlation functions. + Gamma: ndarray, shape (..., d**2, d**2) + The decay amplitudes. Notes ----- - The correlation functions are given by + The decay amplitudes are given by .. math:: - \langle u_{1,k} u_{1, l}\rangle_{\alpha\beta} = \int + \Gamma_{\alpha\beta, kl} = \int \frac{\mathrm{d}\omega}{2\pi}\mathcal{R}^\ast_{\alpha k}(\omega) S_{\alpha\beta}(\omega)\mathcal{R}_{\beta l}(\omega). @@ -400,24 +398,24 @@ def calculate_error_vector_correlation_functions( if not memory_parsimonious: integrand = _get_integrand(S, omega, idx, R=R) - u_kl = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) - return u_kl + Gamma = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) + return Gamma # Conserve memory by looping. Let _get_integrand determine the shape integrand = _get_integrand(S, omega, idx, R=[R[:, 0:1], R]) n_kl = R.shape[1] - u_kl = np.zeros(integrand.shape[:-3] + (n_kl,)*2, - dtype=integrand.dtype) - u_kl[..., 0:1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) + Gamma = np.zeros(integrand.shape[:-3] + (n_kl,)*2, + dtype=integrand.dtype) + Gamma[..., 0:1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) for k in util.progressbar_range(1, n_kl, show_progressbar=show_progressbar, desc='Integrating'): integrand = _get_integrand(S, omega, idx, R=[R[:, k:k+1], R]) - u_kl[..., k:k+1, :] = integrate.trapz(integrand, omega, - axis=-1)/(2*np.pi) + Gamma[..., k:k+1, :] = integrate.trapz(integrand, omega, + axis=-1)/(2*np.pi) - return u_kl + return Gamma @util.parse_which_FF_parameter @@ -617,8 +615,7 @@ def error_transfer_matrix( Show a progress bar for the calculation of the control matrix. memory_parsimonious: bool, optional Trade memory footprint for performance. See - :func:`~numeric.calculate_error_vector_correlation_functions`. The - default is ``False``. + :func:`~numeric.calculate_decay_amplitudes`. The default is ``False``. Returns ------- @@ -709,47 +706,37 @@ def error_transfer_matrix( See Also -------- - calculate_error_vector_correlation_functions + calculate_decay_amplitudes infidelity: Calculate only infidelity of a pulse. """ N, d = pulse.basis.shape[:2] - u_kl = calculate_error_vector_correlation_functions(pulse, S, omega, - n_oper_identifiers, - show_progressbar, - memory_parsimonious) + Gamma = calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers, + show_progressbar, memory_parsimonious) if d == 2 and pulse.basis.btype in ('Pauli', 'GGM'): # Single qubit case. Can use simplified expression - U = np.zeros_like(u_kl) + U = np.zeros_like(Gamma) diag_mask = np.eye(N, dtype=bool) # Offdiagonal terms U[..., ~diag_mask] = -( - u_kl[..., ~diag_mask] + u_kl.swapaxes(-1, -2)[..., ~diag_mask] + Gamma[..., ~diag_mask] + Gamma.swapaxes(-1, -2)[..., ~diag_mask] )/2 - # Diagonal terms U_ii given by sum over diagonal of u_kl excluding u_ii - # Since the Pauli basis is traceless, U_00 is zero, therefore start at - # U_11 + # Diagonal terms U_ii given by sum over diagonal of Gamma excluding + # Gamma_ii. Since the Pauli basis is traceless, U_00 is zero, therefore + # start at U_11. diag_items = deque((True, False, True, True)) for i in range(1, N): - U[..., i, i] = u_kl[..., diag_items, diag_items].sum(axis=-1) + U[..., i, i] = Gamma[..., diag_items, diag_items].sum(axis=-1) # shift the item not summed over by one diag_items.rotate() - - if S.ndim == 3: - # Cross-correlated noise induces non-unitality, thus U[..., 0] != 0 - k, l, i = np.indices((3, 3, 3)) - eps_kli = (l - k)*(i - l)*(i - k)/2 - - U[..., 1:, 0] = 1j*np.einsum('...kl,kli', - u_kl[..., 1:, 1:], eps_kli) else: # Multi qubit case. Use general expression. T = pulse.basis.four_element_traces - U = (oe.contract('...kl,klij->...ij', u_kl, T, backend='sparse')/2 + - oe.contract('...kl,klji->...ij', u_kl, T, backend='sparse')/2 - - oe.contract('...kl,kilj->...ij', u_kl, T, backend='sparse')) + U = (oe.contract('...kl,klij->...ij', Gamma, T, backend='sparse')/2 + + oe.contract('...kl,klji->...ij', Gamma, T, backend='sparse')/2 - + oe.contract('...kl,kilj->...ij', Gamma, T, backend='sparse')) return U @@ -948,9 +935,9 @@ def infidelity(pulse: 'PulseSequence', if which == 'total': if not pulse.basis.istraceless: - # Fidelity not simply sum of all error vector auto-correlation - # funcs but trace tensor plays a role, cf eq. (29). For - # traceless bases, the trace tensor term reduces to delta_ij. + # Fidelity not simply sum of diagonal of decay amplitudes Gamma_kk + # but trace tensor plays a role, cf eq. (39). For traceless bases, + # the trace tensor term reduces to delta_ij. T = pulse.basis.four_element_traces Tp = (sparse.diagonal(T, axis1=2, axis2=3).sum(-1) - sparse.diagonal(T, axis1=1, axis2=3).sum(-1)).todense() @@ -1048,7 +1035,7 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, F: Optional[ndarray] = None) -> ndarray: """ Private function to generate the integrand for either :func:`infidelity` or - :func:`calculate_error_vector_correlation_functions`. + :func:`calculate_decay_amplitudes`. Parameters ---------- @@ -1061,9 +1048,9 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, Noise operator indices to consider. R: ndarray, optional Control matrix. If given, returns the integrand for - :func:`calculate_error_vector_correlation_functions`. If given as a - list or tuple, taken to be the left and right control matrices in the - integrand (allows for slicing up the integrand). + :func:`calculate_decay_amplitudes`. If given as a list or tuple, taken + to be the left and right control matrices in the integrand (allows for + slicing up the integrand). F: ndarray, optional Filter function. If given, returns the integrand for :func:`infidelity`. diff --git a/tests/test_core.py b/tests/test_core.py index 19b1176..fb59e5e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -783,7 +783,7 @@ def test_pulse_correlation_filter_function(self): self.assertAlmostEqual(infid_1.sum(), infid_2.sum()) self.assertArrayAlmostEqual(infid_1, infid_2.sum(axis=(0, 1))) - def test_calculate_error_vector_correlation_functions(self): + def test_calculate_decay_amplitudes(self): """Test raises of numeric.error_transfer_matrix""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) @@ -792,9 +792,8 @@ def test_calculate_error_vector_correlation_functions(self): S = testutil.rng.randn(78) for i in range(4): with self.assertRaises(ValueError): - numeric.calculate_error_vector_correlation_functions( - pulse, np.tile(S, [1]*i), omega - ) + numeric.calculate_decay_amplitudes(pulse, np.tile(S, [1]*i), + omega) def test_infidelity_convergence(self): import matplotlib diff --git a/tests/test_precision.py b/tests/test_precision.py index 65a1291..afe3cee 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -402,12 +402,11 @@ def test_single_qubit_error_transfer_matrix(self): # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same - u_kl = numeric.calculate_error_vector_correlation_functions( - pulse, S, omega, n_oper_identifiers - ) - U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces)/2 + - np.einsum('...kl,klji->...ij', u_kl, traces)/2 - - np.einsum('...kl,kilj->...ij', u_kl, traces)) + Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, + n_oper_identifiers) + U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + + np.einsum('...kl,klji->...ij', Gamma, traces)/2 - + np.einsum('...kl,kilj->...ij', Gamma, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) # Different spectra for each noise oper @@ -423,12 +422,11 @@ def test_single_qubit_error_transfer_matrix(self): # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same - u_kl = numeric.calculate_error_vector_correlation_functions( - pulse, S, omega, n_oper_identifiers - ) - U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces)/2 + - np.einsum('...kl,klji->...ij', u_kl, traces)/2 - - np.einsum('...kl,kilj->...ij', u_kl, traces)) + Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, + n_oper_identifiers) + U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + + np.einsum('...kl,klji->...ij', Gamma, traces)/2 - + np.einsum('...kl,kilj->...ij', Gamma, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) # Cross-correlated spectra @@ -449,13 +447,12 @@ def test_single_qubit_error_transfer_matrix(self): # Check that _single_qubit_error_transfer_matrix and # _multi_qubit_... # give the same - u_kl = numeric.calculate_error_vector_correlation_functions( - pulse, S, omega, n_oper_identifiers - ) + Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, + n_oper_identifiers) U_multi = np.zeros_like(U) - U_multi = (np.einsum('...kl,klij->...ij', u_kl, traces)/2 + - np.einsum('...kl,klji->...ij', u_kl, traces)/2 - - np.einsum('...kl,kilj->...ij', u_kl, traces)) + U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + + np.einsum('...kl,klji->...ij', Gamma, traces)/2 - + np.einsum('...kl,kilj->...ij', Gamma, traces)) self.assertArrayAlmostEqual(U, U_multi, atol=1e-16) def test_multi_qubit_error_transfer_matrix(self): From 67c734ee76c597c2c8760eb4d4a081525d5cfec8 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Wed, 10 Jun 2020 20:34:41 +0200 Subject: [PATCH 02/22] Add calculate_cumulant_function as intermediate New flow: error_transfer_matrix -> calculate_cumulant_function -> calculate_decay_amplitudes --- filter_functions/__init__.py | 4 +- filter_functions/numeric.py | 256 ++++++++++++++++++++--------------- filter_functions/plotting.py | 86 ++++++------ tests/test_plotting.py | 60 ++++---- 4 files changed, 225 insertions(+), 181 deletions(-) diff --git a/filter_functions/__init__.py b/filter_functions/__init__.py index e535416..6d6d4b1 100644 --- a/filter_functions/__init__.py +++ b/filter_functions/__init__.py @@ -25,7 +25,7 @@ from .numeric import (error_transfer_matrix, infidelity, liouville_representation) from .plotting import ( - plot_bloch_vector_evolution, plot_error_transfer_matrix, + plot_bloch_vector_evolution, plot_cumulant_function, plot_filter_function, plot_pulse_correlation_filter_function, plot_pulse_train) from .pulse_sequence import (PulseSequence, concatenate, concatenate_periodic, @@ -34,7 +34,7 @@ __all__ = ['Basis', 'PulseSequence', 'analytic', 'basis', 'concatenate', 'concatenate_periodic', 'error_transfer_matrix', 'extend', 'infidelity', 'liouville_representation', 'numeric', - 'plot_bloch_vector_evolution', 'plot_error_transfer_matrix', + 'plot_bloch_vector_evolution', 'plot_cumulant_function', 'plot_filter_function', 'plot_pulse_correlation_filter_function', 'plot_pulse_train', 'plotting', 'pulse_sequence', 'remap', 'util'] diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index d2f6cac..409100d 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -30,6 +30,8 @@ Calculate the control matrix from scratch :func:`calculate_control_matrix_periodic` Calculate the control matrix for a periodic Hamiltonian +:func:`calculate_cumulant_function` + Calculate the cumulant function for a given ``PulseSequence`` object. :func:`calculate_decay_amplitudes` Calculate the decay amplitudes, corresponding to first order terms of the Magnus expansion @@ -40,8 +42,7 @@ :func:`diagonalize` Diagonalize a Hamiltonian :func:`error_transfer_matrix` - Calculate the error transfer matrix of a pulse up to a unitary - rotation and second order in noise + Calculate the error transfer matrix of a pulse up to a unitary rotation :func:`infidelity` Function to compute the infidelity of a pulse defined by a ``PulseSequence`` instance for a given noise spectral density and @@ -59,6 +60,7 @@ import sparse from numpy import linalg as nla from numpy import ndarray +from scipy import linalg as sla from scipy import integrate from . import util @@ -68,6 +70,8 @@ __all__ = ['calculate_control_matrix_from_atomic', 'calculate_control_matrix_from_scratch', + 'calculate_cumulant_function', + 'calculate_decay_amplitudes', 'calculate_filter_function', 'calculate_pulse_correlation_filter_function', 'diagonalize', 'error_transfer_matrix', 'infidelity', 'liouville_representation'] @@ -331,6 +335,124 @@ def calculate_control_matrix_periodic(phases: ndarray, R: ndarray, return R_tot +def calculate_cumulant_function( + pulse: 'PulseSequence', + S: ndarray, + omega: Coefficients, + n_oper_identifiers: Optional[Sequence[str]] = None, + show_progressbar: Optional[bool] = False, + memory_parsimonious: Optional[bool] = False) -> ndarray: + r"""Calculate the cumulant function :math:`K(\tau)`. + + The error transfer matrix is obtained from the cumulant function by + exponentiation, :math:`\tilde{\mathcal{U}} = \exp K(\tau)`. + + Parameters + ---------- + pulse: PulseSequence + The ``PulseSequence`` instance for which to compute the cumulant + function. + S: array_like, shape (..., n_omega) + The two-sided noise power spectral density in units of inverse + frequencies as an array of shape (n_omega,), (n_nops, n_omega), or + (n_nops, n_nops, n_omega). In the first case, the same spectrum is + taken for all noise operators, in the second, it is assumed that there + are no correlations between different noise sources and thus there is + one spectrum for each noise operator. In the third and most general + case, there may be a spectrum for each pair of noise operators + corresponding to the correlations between them. n_nops is the number of + noise operators considered and should be equal to + ``len(n_oper_identifiers)``. + omega: array_like, + The frequencies. Note that the frequencies are assumed to be symmetric + about zero. + n_oper_identifiers: array_like, optional + The identifiers of the noise operators for which to evaluate the + cumulant function. The default is all. + show_progressbar: bool, optional + Show a progress bar for the calculation of the control matrix. + memory_parsimonious: bool, optional + Trade memory footprint for performance. See + :func:`~numeric.calculate_decay_amplitudes`. The default is ``False``. + + Returns + ------- + K: ndarray, shape ([n_nops,] n_nops, d**2, d**2) + The cumulant function. The individual noise operator contributions + chosen by ``n_oper_identifiers`` are on the first axis / axes, + depending on whether the noise is cross-correlated or not. + + Notes + ----- + The cumulant function is given by + + .. math:: + + K_{ij}(\tau) = -\frac{1}{2}\sum_{\alpha\beta} & \sum_{kl}\biggl( + \Delta_{\alpha\beta,kl}\left( + T_{klji} - T_{lkji} - T_{klij} + T_{lkij} + \right) + \Gamma_{\alpha\beta,kl}\left( + T_{klji} - T_{kjli} - T_{kilj} + T_{kijl} + \right) + \biggr) + + Here, :math:`T_{ijkl} = \mathrm{tr}(C_i C_j C_k C_l)` is a trivial function + of the basis elements :math:`C_i`, and :math:`\Gamma_{\alpha\beta,kl}` and + :math:`\Delta_{\alpha\beta,kl}` are the decay amplitudes and frequency + shifts which correspond to first and second order in the Magnus expansion, + respectively. Since the latter induce coherent errors, we can approximately + neglect them if we assume that the pulse has been experimentally + calibrated. + + For a single qubit and represented in the Pauli basis, the above reduces to + + .. math:: + + K_{ij}(\tau) = \begin{cases} + - \sum_{k\neq i}\Gamma_{kk} &\qif* i = j, \\ + - \Delta_{ij} + \Delta_{ji} + \Gamma_{ij} &\qif* i\neq j, + \end{cases} + + for :math:`i\in\{1,2,3\}` and :math:`K_{0j} = K_{i0} = 0`. + + See Also + -------- + calculate_decay_amplitudes: Calculate the :math:`\Gamma_{\alpha\beta,kl}` + error_transfer_matrix: Calculate the error transfer matrix :math:`\exp K`. + infidelity: Calculate only infidelity of a pulse. + + """ + N, d = pulse.basis.shape[:2] + Gamma = calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers, + show_progressbar, memory_parsimonious) + + if d == 2 and pulse.basis.btype in ('Pauli', 'GGM'): + # Single qubit case. Can use simplified expression + K = np.zeros_like(Gamma) + diag_mask = np.eye(N, dtype=bool) + + # Offdiagonal terms + K[..., ~diag_mask] = Gamma[..., ~diag_mask] + + # Diagonal terms K_ii given by sum over diagonal of Gamma excluding + # Gamma_ii. Since the Pauli basis is traceless, K_00 is zero, therefore + # start at K_11. + diag_items = deque((True, False, True, True)) + for i in range(1, N): + K[..., i, i] = -Gamma[..., diag_items, diag_items].sum(axis=-1) + # shift the item not summed over by one + diag_items.rotate() + else: + # Multi qubit case. Use general expression. + T = pulse.basis.four_element_traces + K = (oe.contract('...kl,lkji->...ij', Gamma, T, backend='sparse') + + oe.contract('...kl,klij->...ij', Gamma, T, backend='sparse') - + oe.contract('...kl,klji->...ij', Gamma, T, backend='sparse') - + oe.contract('...kl,lkij->...ij', Gamma, T, backend='sparse'))/2 + + return K + + def calculate_decay_amplitudes( pulse: 'PulseSequence', S: ndarray, @@ -432,7 +554,7 @@ def calculate_filter_function(R: ndarray, which: str = 'fidelity') -> ndarray: Returns ------- - F: ndarray, shape (n_nops, n_nops, [d**2, d**2], n_omega) + F: ndarray, shape (n_nops, n_nops, [d**2, d**2,] n_omega) The filter functions for each noise operator correlation. The diagonal corresponds to the filter functions for uncorrelated noise sources. @@ -479,7 +601,7 @@ def calculate_pulse_correlation_filter_function( Returns ------- - F_pc: ndarray, shape (n_pulses, n_pulses, n_nops, n_nops, [d**2, d**2], n_omega) # noqa + F_pc: ndarray, shape (n_pls, n_pls, n_nops, n_nops, [d**2, d**2,] n_omega) The pulse correlation filter functions for each pulse and noise operator correlations. The first two axes hold the pulse correlations, the second two the noise correlations. @@ -585,9 +707,7 @@ def error_transfer_matrix( n_oper_identifiers: Optional[Sequence[str]] = None, show_progressbar: Optional[bool] = False, memory_parsimonious: Optional[bool] = False) -> ndarray: - r""" - Compute the first correction to the error transfer matrix up to unitary - rotations and second order in noise. + r"""Compute the error transfer matrix up to unitary rotations. Parameters ---------- @@ -619,125 +739,47 @@ def error_transfer_matrix( Returns ------- - U: ndarray, shape (..., d**2, d**2) - The first correction to the error transfer matrix. The individual noise - operator contributions chosen by ``n_oper_identifiers`` are on the - first axis / axes, depending on whether the noise is cross-correlated - or not. + U: ndarray, shape (d**2, d**2) + The error transfer matrix. The individual noise operator contributions + are summed up before exponentiating as they might not commute. Notes ----- - The error transfer matrix is up to second order in noise :math:`\xi` given - by - - .. math:: - - \mathcal{\tilde{U}}_{ij} &= \mathrm{tr}\bigl(C_i\tilde{U} C_j - \tilde{U}^\dagger\bigr) \\ - &= \mathrm{tr}(C_i C_j) - -\frac{1}{2}\left\langle\mathrm{tr} - \bigl( - (\vec{u}_1\vec{C})^2 - \lbrace C_i, C_j\rbrace - \bigr) - \right\rangle + \left\langle\mathrm{tr} - \bigl( - \vec{u}_1\vec{C} C_i - \vec{u}_1\vec{C} C_j - \bigr) - \right\rangle - i\left\langle\mathrm{tr} - \bigl( - \vec{u}_2\vec{C}[C_i, C_j] - \bigr) - \right\rangle + \mathcal{O}(\xi^4). - - We can thus write the error transfer matrix as the identity matrix minus a - correction term, + The error transfer matrix is given by .. math:: - \mathcal{\tilde{U}}\approx\mathbb{I} - \mathcal{\tilde{U}}^{(1)}. - - Note additionally that the above expression includes a second-order term - from the Magnus Expansion (:math:`\propto\vec{u}_2`). Since this term can - be compensated by a unitary rotation and thus calibrated out, it is not - included in the calculation. + \tilde{\mathcal{U}} = \exp K(\tau) - For the general case of :math:`n` qubits, the correction term is calculated - as - - .. math:: + with :math:`K(\tau)` the cumulant function (see + :func:`calculate_cumulant_function`). For Gaussian noise this expression is + exact when taking into account the decay amplitudes :math:`\Gamma` and + frequency shifts :math:`\Delta`. As the latter effects coherent errors it + can be neglected if we assume that the experimenter has calibrated their + pulse. - \mathcal{\tilde{U}}_{ij}^{(1)} = \sum_{k,l=0}^{d^2-1} - \left\langle u_{1,k}u_{1,l}\right\rangle\left[ - \frac{1}{2}T_{k l i j} + - \frac{1}{2}T_{k l j i} - - T_{k i l j} - \right], - - where :math:`T_{ijkl} = \mathrm{tr}(C_i C_j C_k C_l)`. For a single - qubit and represented in the Pauli basis, this reduces to - - .. math:: + For non-Gaussian noise the expression above is perturbative and includes + noise up to order :math:`\xi^2` and hence + :math:`\tilde{\mathcal{U}} = \mathbb{1} + K(\tau) + \mathcal{O}(\xi^2)` + (although it is evaluated as a matrix exponential in any case). - \mathcal{\tilde{U}}_{ij}^{(1)} = \begin{cases} - \sum_{k\neq i}\bigl\langle u_{1,k}^2\bigr\rangle - &\mathrm{if\;} i = j, \\ - -\frac{1}{2}\left(\bigl\langle u_{1, i} u_{1, j}\bigr\rangle - \bigl\langle u_{1, j} u_{1, i}\bigr\rangle\right) - &\mathrm{if\;} i\neq j, \\ - \sum_{kl} i\epsilon_{kli}\bigl\langle u_{1, k} u_{1, l}\bigr\rangle - &\mathrm{if\;} j = 0, \\ - 0 &\mathrm{else.} - \end{cases} - - for :math:`i\in\{1,2,3\}` and :math:`\mathcal{\tilde{U}}_{0j}^{(1)} = 0`. - For purely auto-correlated noise where - (:math:`S_{\alpha\beta}=S_{\alpha\alpha}\delta_{\alpha\beta}`) we - additionally have :math:`\mathcal{\tilde{U}}_{i0}^{(1)} = 0` and - :math:`\langle u_{1, i} u_{1, j}\rangle=\langle u_{1, j} u_{1, i}\rangle`. Given the above expression of the error transfer matrix, the entanglement - infidelity is given by + fidelity is given by .. math:: - \mathcal{I}_\mathrm{e} = \frac{1}{d^2}\mathrm{tr} - \bigl(\mathcal{\tilde{U}}^{(1)}\bigr). + \mathcal{F}_\mathrm{e} = \frac{1}{d^2}\mathrm{tr}\,\tilde{\mathcal{U}}. See Also -------- - calculate_decay_amplitudes + calculate_cumulant_function: Calculate the cumulant function :math:`K` + calculate_decay_amplitudes: Calculate the :math:`\Gamma_{\alpha\beta,kl}` infidelity: Calculate only infidelity of a pulse. """ - N, d = pulse.basis.shape[:2] - Gamma = calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers, - show_progressbar, memory_parsimonious) - - if d == 2 and pulse.basis.btype in ('Pauli', 'GGM'): - # Single qubit case. Can use simplified expression - U = np.zeros_like(Gamma) - diag_mask = np.eye(N, dtype=bool) - - # Offdiagonal terms - U[..., ~diag_mask] = -( - Gamma[..., ~diag_mask] + Gamma.swapaxes(-1, -2)[..., ~diag_mask] - )/2 - - # Diagonal terms U_ii given by sum over diagonal of Gamma excluding - # Gamma_ii. Since the Pauli basis is traceless, U_00 is zero, therefore - # start at U_11. - diag_items = deque((True, False, True, True)) - for i in range(1, N): - U[..., i, i] = Gamma[..., diag_items, diag_items].sum(axis=-1) - # shift the item not summed over by one - diag_items.rotate() - else: - # Multi qubit case. Use general expression. - T = pulse.basis.four_element_traces - U = (oe.contract('...kl,klij->...ij', Gamma, T, backend='sparse')/2 + - oe.contract('...kl,klji->...ij', Gamma, T, backend='sparse')/2 - - oe.contract('...kl,kilj->...ij', Gamma, T, backend='sparse')) + K = calculate_cumulant_function(pulse, S, omega, n_oper_identifiers, + show_progressbar, memory_parsimonious) + U = sla.expm(K.sum(axis=list(range(K.ndim - 2)))) return U diff --git a/filter_functions/plotting.py b/filter_functions/plotting.py index 1ace148..2097548 100644 --- a/filter_functions/plotting.py +++ b/filter_functions/plotting.py @@ -35,8 +35,8 @@ Plot the pulse correlation filter function of a given ``PulseSequence`` :func:`plot_pulse_train` Plot the pulse train of a given ``PulseSequence`` -:func:`plot_error_transfer_matrix` - Plot the error transfer matrix of a ``PulseSequence`` for a given spectrum +:func:`plot_cumulant_function` + Plot the cumulant function of a ``PulseSequence`` for a given spectrum as an image. """ @@ -54,7 +54,7 @@ from .types import (Axes, Coefficients, Colormap, Figure, FigureAxes, FigureAxesLegend, FigureGrid, Grid, Operator, State) -__all__ = ['plot_bloch_vector_evolution', 'plot_error_transfer_matrix', +__all__ = ['plot_bloch_vector_evolution', 'plot_cumulant_function', 'plot_filter_function', 'plot_infidelity_convergence', 'plot_pulse_correlation_filter_function', 'plot_pulse_train'] @@ -577,16 +577,16 @@ def plot_infidelity_convergence(n_samples: Sequence[int], return fig, ax -def plot_error_transfer_matrix( +def plot_cumulant_function( pulse: Optional['PulseSequence'] = None, S: Optional[ndarray] = None, omega: Optional[Coefficients] = None, - U: Optional[ndarray] = None, + K: Optional[ndarray] = None, n_oper_identifiers: Optional[Sequence[int]] = None, basis_labels: Optional[Sequence[str]] = None, colorscale: Optional[str] = 'linear', linthresh: Optional[float] = None, - cbar_label: Optional[str] = 'Error transfer matrix', + cbar_label: Optional[str] = 'Cumulant Function', basis_labelsize: Optional[int] = None, fig: Optional[Figure] = None, grid: Optional[Grid] = None, @@ -594,12 +594,15 @@ def plot_error_transfer_matrix( grid_kw: Optional[dict] = None, imshow_kw: Optional[dict] = None, **figure_kw) -> FigureGrid: - """ - Plot the error transfer matrix for a given noise spectrum as an image. + r"""Plot the cumulant function for a given noise spectrum as an image. + + The cumulant function generates the error transfer matrix + :math:`\tilde{\mathcal{U}}` exactly for Gaussian noise and to second order + for non-Gaussian noise. The function may be called with either a ``PulseSequence``, a spectrum, and - a list of frequencies in which case the error transfer matrix is calculated - for those parameters, or with a precomputed error transfer matrix. + a list of frequencies in which case the cumulant function is calculated + for those parameters, or with a precomputed cumulant function. As of now, only auto-correlated spectra are implemented. @@ -610,18 +613,18 @@ def plot_error_transfer_matrix( S: ndarray The two-sided noise spectrum. omega: array_like - The frequencies for which to evaluate the error transfer matrix. Note - that they should be symmetric around zero, that is, include negative + The frequencies for which to evaluate the cumulant function. Note that + they should be symmetric around zero, that is, include negative frequencies. - U: ndarray, shape - A precomputed error transfer matrix. If given, *pulse*, *S*, *omega* + K: ndarray, shape + A precomputed cumulant function. If given, *pulse*, *S*, *omega* are not required. n_oper_identifiers: array_like, optional - The identifiers of the noise operators for which the error transfer - matrix should be plotted. All identifiers can be accessed via + The identifiers of the noise operators for which the cumulant function + should be plotted. All identifiers can be accessed via ``pulse.n_oper_identifiers``. Defaults to all. basis_labels: array_like (str), optional - Labels for the elements of the error transfer matrix (the basis + Labels for the elements of the cumulant function (the basis elements). colorscale: str, optional The scale of the color code ('linear' or 'log' (default)) @@ -629,7 +632,7 @@ def plot_error_transfer_matrix( The threshold below which the colorscale will be linear (only for 'log') colorscale cbar_label: str, optional - The label for the colorbar. Default: 'Error transfer matrix'. + The label for the colorbar. Default: 'Cumulant Function'. basis_labelsize: int, optional The size in points for the basis labels. fig: matplotlib figure, optional @@ -654,13 +657,13 @@ def plot_error_transfer_matrix( grid: matplotlib ImageGrid The ImageGrid instance used for plotting. """ - if U is not None: - if U.ndim == 2: - U = np.array([U]) + if K is not None: + if K.ndim == 2: + K = np.array([K]) - n_oper_inds = np.arange(len(U)) + n_oper_inds = np.arange(len(K)) if n_oper_identifiers is None: - if pulse is not None and len(pulse.n_oper_identifiers) == len(U): + if pulse is not None and len(pulse.n_oper_identifiers) == len(K): n_oper_identifiers = pulse.n_oper_identifiers else: n_oper_identifiers = ['$B_{}$'.format(i) @@ -668,33 +671,34 @@ def plot_error_transfer_matrix( else: if pulse is None or S is None or omega is None: - raise ValueError('Require either precomputed error transfer ' + - 'matrix or pulse, S, and omega as arguments.') + raise ValueError('Require either precomputed cumulant function ' + + 'or pulse, S, and omega as arguments.') n_oper_inds = util.get_indices_from_identifiers(pulse, n_oper_identifiers, 'noise') n_oper_identifiers = pulse.n_oper_identifiers[n_oper_inds] - # Get the error transfer matrix - U = numeric.error_transfer_matrix(pulse, S, omega, n_oper_identifiers) - if U.ndim == 4: + # Get the cumulant function + K = numeric.calculate_cumulant_function(pulse, S, omega, + n_oper_identifiers) + if K.ndim == 4: # Only autocorrelated noise supported - U = U[range(len(n_oper_inds)), range(len(n_oper_inds))] + K = K[range(len(n_oper_inds)), range(len(n_oper_inds))] # Only autocorrelated noise implemented for now, ie U is real - U = U.real + K = K.real if basis_labels is None: btype = pulse.basis.btype if pulse is not None else '' if btype == 'Pauli': - n_qubits = int(np.log(U.shape[-1])/np.log(4)) + n_qubits = int(np.log(K.shape[-1])/np.log(4)) basis_labels = [''.join(tup) for tup in product(['I', 'X', 'Y', 'Z'], repeat=n_qubits)] else: basis_labels = ['$C_{{{}}}$'.format(i) - for i in range(U.shape[-1])] + for i in range(K.shape[-1])] else: - if len(basis_labels) != U.shape[-1]: + if len(basis_labels) != K.shape[-1]: raise ValueError('Invalid number of basis_labels given') if grid is None: @@ -729,13 +733,13 @@ def plot_error_transfer_matrix( else: cmap = plt.get_cmap('RdBu') - Umax = U.max() - Umin = -Umax + Kmax = np.abs(K).max() + Kmin = -Kmax if colorscale == 'log': - linthresh = np.abs(U).mean()/10 if linthresh is None else linthresh - norm = colors.SymLogNorm(linthresh=linthresh, vmin=Umin, vmax=Umax) + linthresh = np.abs(K).mean()/10 if linthresh is None else linthresh + norm = colors.SymLogNorm(linthresh=linthresh, vmin=Kmin, vmax=Kmax) elif colorscale == 'linear': - norm = colors.Normalize(vmin=Umin, vmax=Umax) + norm = colors.Normalize(vmin=Kmin, vmax=Kmax) imshow_kw = {} if imshow_kw is None else imshow_kw imshow_kw.setdefault('origin', 'upper') @@ -748,10 +752,10 @@ def plot_error_transfer_matrix( # Draw the images for i, n_oper_identifier in enumerate(n_oper_identifiers): ax = grid[i] - im = ax.imshow(U[i], **imshow_kw) + im = ax.imshow(K[i], **imshow_kw) ax.set_title(n_oper_identifier) - ax.set_xticks(np.arange(U.shape[-1])) - ax.set_yticks(np.arange(U.shape[-1])) + ax.set_xticks(np.arange(K.shape[-1])) + ax.set_yticks(np.arange(K.shape[-1])) ax.set_xticklabels(basis_labels, rotation='vertical', fontsize=basis_labelsize) ax.set_yticklabels(basis_labels, fontsize=basis_labelsize) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index ec1f382..a954354 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -248,32 +248,32 @@ def test_plot_pulse_correlation_filter_function(self): # Ignore deprecation warning caused by qutip @pytest.mark.filterwarnings('ignore::PendingDeprecationWarning') - def test_plot_error_transfer_matrix(self): + def test_plot_cumulant_function(self): omega = ff.util.get_sample_frequencies(simple_pulse) S = 1e-4*np.sin(omega)/omega # Test calling with pulse, spectrum, omega - fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, - colorscale='linear') - fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, - fig=fig) - fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, S, omega, - grid=grid) + fig, grid = plotting.plot_cumulant_function(simple_pulse, S, omega, + colorscale='linear') + fig, grid = plotting.plot_cumulant_function(simple_pulse, S, omega, + fig=fig) + fig, grid = plotting.plot_cumulant_function(simple_pulse, S, omega, + grid=grid) # Test calling with precomputed transfer matrix - U = ff.error_transfer_matrix(simple_pulse, S, omega) - fig, grid = plotting.plot_error_transfer_matrix(U=U) + K = ff.calculate_cumulant_function(simple_pulse, S, omega) + fig, grid = plotting.plot_cumulant_function(K=K) # Test calling with precomputed transfer matrix and pulse - U = ff.error_transfer_matrix(simple_pulse, S, omega) - fig, grid = plotting.plot_error_transfer_matrix(simple_pulse, U=U) + K = ff.calculate_cumulant_function(simple_pulse, S, omega) + fig, grid = plotting.plot_cumulant_function(simple_pulse, K=K) # Test calling with precomputed transfer matrix of ndim == 2 - U = ff.error_transfer_matrix(simple_pulse, S, omega) - fig, grid = plotting.plot_error_transfer_matrix(U=U[0]) + K = ff.calculate_cumulant_function(simple_pulse, S, omega) + fig, grid = plotting.plot_cumulant_function(K=K[0]) # Log colorscale - fig, grid = plotting.plot_error_transfer_matrix(U=U, colorscale='log') + fig, grid = plotting.plot_cumulant_function(K=K, colorscale='log') # Non-default args n_oper_inds = sample(range(len(complicated_pulse.n_opers)), @@ -288,46 +288,44 @@ def test_plot_error_transfer_matrix(self): omega = ff.util.get_sample_frequencies(complicated_pulse, n_samples=50, spacing='log') S = np.exp(-omega**2) - U = ff.error_transfer_matrix(complicated_pulse, S, omega) - fig, grid = plotting.plot_error_transfer_matrix( + K = ff.calculate_cumulant_function(complicated_pulse, S, omega) + fig, grid = plotting.plot_cumulant_function( complicated_pulse, S=S, omega=omega, n_oper_identifiers=n_oper_identifiers, basis_labels=basis_labels, basis_labelsize=4, linthresh=1e-4, cmap=matplotlib.cm.jet ) - fig, grid = plotting.plot_error_transfer_matrix( - U=U[n_oper_inds], n_oper_identifiers=n_oper_identifiers, + fig, grid = plotting.plot_cumulant_function( + K=K[n_oper_inds], n_oper_identifiers=n_oper_identifiers, basis_labels=basis_labels, basis_labelsize=4, linthresh=1e-4, cmap=matplotlib.cm.jet ) - # neither U nor all of pulse, S, omega given + # neither K nor all of pulse, S, omega given with self.assertRaises(ValueError): - plotting.plot_error_transfer_matrix(complicated_pulse, S) + plotting.plot_cumulant_function(complicated_pulse, S) # invalid identifiers with self.assertRaises(ValueError): - plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, - n_oper_identifiers=['foo']) + plotting.plot_cumulant_function(complicated_pulse, S, omega, + n_oper_identifiers=['foo']) # number of basis_labels not correct with self.assertRaises(ValueError): - plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, - basis_labels=basis_labels[:2]) + plotting.plot_cumulant_function(complicated_pulse, S, omega, + basis_labels=basis_labels[:2]) # grid too small with self.assertRaises(ValueError): - plotting.plot_error_transfer_matrix(complicated_pulse, S, omega, - grid=grid[:1]) + plotting.plot_cumulant_function(complicated_pulse, S, omega, + grid=grid[:1]) # Test various keyword args for matplotlib for the two-qubit pulse S = np.tile(S, (6, 6, 1)) grid_kw = {'axes_pad': 0.1} imshow_kw = {'interpolation': 'bilinear'} figure_kw = {'num': 1} - fig, ax = plotting.plot_error_transfer_matrix(two_qubit_pulse, S, - omega, - imshow_kw=imshow_kw, - grid_kw=grid_kw, - **figure_kw) + fig, ax = plotting.plot_cumulant_function(two_qubit_pulse, S, omega, + imshow_kw=imshow_kw, + grid_kw=grid_kw, **figure_kw) plt.close('all') From a19ae06703bf9590ae4d843f7fbc663acb40aa09 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Wed, 10 Jun 2020 20:38:48 +0200 Subject: [PATCH 03/22] Add pulse correlation decay amplitudes Also refactor internal function _get_integrand --- filter_functions/numeric.py | 144 +++++++++++++++++++++++++----------- 1 file changed, 100 insertions(+), 44 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 409100d..ea35c6a 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -453,11 +453,13 @@ def calculate_cumulant_function( return K +@util.parse_optional_parameter('which', ('total', 'correlations')) def calculate_decay_amplitudes( pulse: 'PulseSequence', S: ndarray, omega: Coefficients, n_oper_identifiers: Optional[Sequence[str]] = None, + which: str = 'total', show_progressbar: Optional[bool] = False, memory_parsimonious: Optional[bool] = False) -> ndarray: r""" @@ -469,14 +471,21 @@ def calculate_decay_amplitudes( pulse: PulseSequence The ``PulseSequence`` instance for which to compute the decay amplitudes. - S: array_like, shape (..., n_omega) - The two-sided noise power spectral density. + S: array_like, shape ([n_nops, n_nops,] n_omega) + The two-sided noise power spectral density. If 1-d, the same spectrum + is used for all noise operators. If 2-d, one (self-) spectrum for each + noise operator is expected. If 3-d, should be a matrix of cross-spectra + such that ``S[i, j] == S[j, i].conj()``. omega: array_like, The frequencies. Note that the frequencies are assumed to be symmetric about zero. n_oper_identifiers: array_like, optional The identifiers of the noise operators for which to calculate the decay amplitudes. The default is all. + which: str, optional + Which decay amplitudes should be calculated, may be either 'total' + (default) or 'correlations'. See :func:`infidelity` and + :ref:`Notes `. show_progressbar: bool, optional Show a progress bar for the calculation. memory_parsimonious: bool, optional @@ -499,12 +508,12 @@ def calculate_decay_amplitudes( Returns ------- - Gamma: ndarray, shape (..., d**2, d**2) + Gamma: ndarray, shape ([n_pls, n_pls, n_nops, n_nops,] d**2, d**2) The decay amplitudes. Notes ----- - The decay amplitudes are given by + The total decay amplitudes are given by .. math:: @@ -512,19 +521,42 @@ def calculate_decay_amplitudes( \frac{\mathrm{d}\omega}{2\pi}\mathcal{R}^\ast_{\alpha k}(\omega) S_{\alpha\beta}(\omega)\mathcal{R}_{\beta l}(\omega). + If pulse correlations are taken into account, they are given by + + .. math:: + + \Gamma_{\alpha\beta, kl}^{(gg')} = \int\frac{\mathrm{d}\omega}{2\pi} + S_{\alpha\beta}(\omega)F_{\alpha\beta, kl}^{(gg')}(\omega). + + See Also + -------- + infidelity: Compute the infidelity directly. + pulse_sequence.concatenate: Concatenate ``PulseSequence`` objects. + calculate_pulse_correlation_filter_function """ - # TODO: Implement for correlation FFs? Replace infidelity() by this? + # TODO: Replace infidelity() by this? # Noise operator indices idx = util.get_indices_from_identifiers(pulse, n_oper_identifiers, 'noise') - R = pulse.get_control_matrix(omega, show_progressbar)[idx] + if which == 'total': + R = pulse.get_control_matrix(omega, show_progressbar)[idx] + elif which =='correlations': + # Check if cached frequencies coincide with given + if pulse.is_cached('omega'): + if not np.array_equal(pulse.omega, omega): + raise ValueError('Pulse correlation decay amplitudes ' + + 'requested but omega not equal to ' + + 'cached frequencies.') + + R = pulse.get_pulse_correlation_control_matrix() if not memory_parsimonious: - integrand = _get_integrand(S, omega, idx, R=R) + integrand = _get_integrand(S, omega, idx, which, 'generalized', R=R) Gamma = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) return Gamma # Conserve memory by looping. Let _get_integrand determine the shape - integrand = _get_integrand(S, omega, idx, R=[R[:, 0:1], R]) + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=[R[..., 0:1], R]) n_kl = R.shape[1] Gamma = np.zeros(integrand.shape[:-3] + (n_kl,)*2, @@ -533,7 +565,8 @@ def calculate_decay_amplitudes( for k in util.progressbar_range(1, n_kl, show_progressbar=show_progressbar, desc='Integrating'): - integrand = _get_integrand(S, omega, idx, R=[R[:, k:k+1], R]) + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=[R[..., k:k+1], R]) Gamma[..., k:k+1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) @@ -783,6 +816,7 @@ def error_transfer_matrix( return U +@util.parse_optional_parameter('which', ('total', 'correlations')) def infidelity(pulse: 'PulseSequence', S: Union[Coefficients, Callable], omega: Union[Coefficients, Dict[str, Union[int, str]]], @@ -836,8 +870,7 @@ def infidelity(pulse: 'PulseSequence', Note that this option is only available if the pulse correlation filter functions have been computed during concatenation (see :func:`calculate_pulse_correlation_filter_function` and - :func:`~filter_functions.pulse_sequence.concatenate`) and that in this - case no checks are performed if the frequencies are compliant. + :func:`~filter_functions.pulse_sequence.concatenate`). return_smallness: bool, optional Return the smallness parameter :math:`\xi` for the given spectrum. test_convergence: bool, optional @@ -912,6 +945,12 @@ def infidelity(pulse: 'PulseSequence', Note that in practice, the integral is only evaluated on the interval :math:`\omega\in[\omega_\mathrm{min},\omega_\mathrm{max}]`. + See Also + -------- + calculate_decay_amplitudes + pulse_sequence.concatenate: Concatenate ``PulseSequence`` objects. + calculate_pulse_correlation_filter_function + References ---------- @@ -993,21 +1032,14 @@ def infidelity(pulse: 'PulseSequence', warn('Calculating pulse correlation fidelities with non-' + 'traceless basis. The results will be off.') - F = pulse.get_pulse_correlation_filter_function() - else: - raise ValueError("Unrecognized option for 'which': {}.".format(which)) - - S = np.asarray(S) - slices = [slice(None)]*F.ndim - if S.ndim == 3: - slices[-3] = idx[:, None] - slices[-2] = idx[None, :] - else: - slices[-3] = idx - slices[-2] = idx + if pulse.is_cached('omega'): + if not np.array_equal(pulse.omega, omega): + raise ValueError('Pulse correlation infidelities requested ' + + 'but omega not equal to cached frequencies.') - integrand = _get_integrand(S, omega, idx, F=F[tuple(slices)]) + F = pulse.get_pulse_correlation_filter_function() + integrand = _get_integrand(S, omega, idx, which, 'fidelity', F=F) infid = integrate.trapz(integrand, omega)/(2*np.pi*pulse.d) if return_smallness: @@ -1072,7 +1104,8 @@ def liouville_representation(U: ndarray, basis: Basis) -> ndarray: return R -def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, +def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, + which_FF: str, R: Optional[Union[ndarray, Sequence[ndarray]]] = None, F: Optional[ndarray] = None) -> ndarray: """ @@ -1088,6 +1121,11 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, about zero. idx: ndarray Noise operator indices to consider. + which_pulse: str, optional {'total', 'correlations'} + Use pulse correlations or total filter function. + which_FF: str, optional {'fidelity', 'generalized'} + Fidelity or generalized filter functions. Needed to determine output + shape. R: ndarray, optional Control matrix. If given, returns the integrand for :func:`calculate_decay_amplitudes`. If given as a list or tuple, taken @@ -1117,30 +1155,43 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, else: R_left, R_right = [f(r) for f, r in zip(funs, [R]*2)] + if which_pulse == 'correlations': + subs = ['g', '', 'h', 'gh'] + elif which_pulse == 'total': + subs = ['', '', '', ''] + + elif F is not None: + if which_FF == 'generalized': + # Everything simpler if noise operators always on 2nd-to-last axes + F = np.moveaxis(F, source=[-5, -4], destination=[-3, -2]) + S = np.asarray(S) S_err_str = 'S should be of shape {}, not {}.' - if S.ndim == 1: - # Only single spectrum - shape = (len(omega),) - if S.shape != shape: - raise ValueError(S_err_str.format(shape, S.shape)) - - # S is real, integrand therefore also - if F is not None: - integrand = (F*S).real - elif R is not None: - integrand = np.einsum('jko,jlo->jklo', R_left, S*R_right).real - elif S.ndim == 2: - # S is diagonal (no correlation between noise sources) - shape = (len(idx), len(omega)) - if S.shape != shape: - raise ValueError(S_err_str.format(shape, S.shape)) + if S.ndim == 1 or S.ndim == 2: + if S.ndim == 1: + # Only single spectrum + shape = (len(omega),) + if S.shape != shape: + raise ValueError(S_err_str.format(shape, S.shape)) + + S = np.expand_dims(S, 0) + elif S.ndim == 2: + # S is diagonal (no correlation between noise sources) + shape = (len(idx), len(omega)) + if S.shape != shape: + raise ValueError(S_err_str.format(shape, S.shape)) # S is real, integrand therefore also if F is not None: - integrand = (F*S).real + integrand = (F[..., tuple(idx), tuple(idx), :]*S).real + if which_FF == 'generalized': + # move axes back to expected position, ie (pulses, noise opers, + # basis elements, frequencies) + integrand = np.moveaxis(integrand, source=-2, destination=-4) elif R is not None: - integrand = np.einsum('jko,jo,jlo->jklo', R_left, S, R_right).real + s = [''.join(z) for z in zip(subs, ['ako', 'ao', 'alo', 'aklo'])] + einsum_str = ','.join(s[:3]) + '->' + s[3] + integrand = np.einsum(einsum_str, R_left, S, R_right).real elif S.ndim == 3: # General case where S is a matrix with correlation spectra on off-diag shape = (len(idx), len(idx), len(omega)) @@ -1149,8 +1200,13 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, if F is not None: integrand = F*S + if which_FF == 'generalized': + integrand = np.moveaxis(integrand, source=[-3, -2], + destination=[-5, -4]) elif R is not None: - integrand = np.einsum('iko,ijo,jlo->ijklo', R_left, S, R_right) + s = [''.join(z) for z in zip(subs, ['ako', 'abo', 'alo', 'abklo'])] + einsum_str = ','.join(s[:3]) + '->' + s[3] + integrand = np.einsum(einsum_str, R_left, S, R_right) elif S.ndim > 3: raise ValueError('Expected S to be array_like with < 4 dimensions') From d3c5519a4ebe18603be513679eb3a61d09cd23b0 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 11 Jun 2020 10:04:50 +0200 Subject: [PATCH 04/22] Make code more readable, update infidelity docstr --- filter_functions/numeric.py | 47 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index ea35c6a..b8aef59 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -824,9 +824,12 @@ def infidelity(pulse: 'PulseSequence', which: str = 'total', return_smallness: bool = False, test_convergence: bool = False) -> Union[ndarray, Any]: - r""" - Calculate the ensemble average of the entanglement infidelity of the - ``PulseSequence`` *pulse*. + r"""Calculate the leading order entanglement infidelity. + + This function calculates the infidelity approximately from the leading + peturbation (see :ref:`Notes `). To compute it exactly for Gaussian + noise and vanishing coherent errors (second order Magnus terms), use + :func:`error_transfer_matrix` to obtain it from the full process matrix. Parameters ---------- @@ -905,12 +908,12 @@ def infidelity(pulse: 'PulseSequence', .. math:: - \big\langle\mathcal{I}_\mathrm{e}\big\rangle_{\alpha\beta} &= - \frac{1}{2\pi d}\int_{-\infty}^{\infty}\mathrm{d}\omega\, + \mathcal{I}_{\alpha\beta} + &= 1 - \frac{1}{d^2}\mathrm{tr}\:\tilde{\mathcal{U}} \\ + &= \frac{1}{d}\int_{-\infty}^{\infty}\frac{\mathrm{d}\omega}{2\pi} S_{\alpha\beta}(\omega)F_{\alpha\beta}(\omega) +\mathcal{O}\big(\xi^4\big) \\ - &= \sum_{g,g'=1}^G \big\langle - \mathcal{I}_\mathrm{e}\big\rangle_{\alpha\beta}^{(gg')} + &= \sum_{g,g'=1}^G \mathcal{I}_{\alpha\beta}^{(gg')} with :math:`S_{\alpha\beta}(\omega)` the two-sided noise spectral density and :math:`F_{\alpha\beta}(\omega)` the first-order filter function for @@ -918,9 +921,8 @@ def infidelity(pulse: 'PulseSequence', correlated noise sources, that is, its entry at :math:`(\alpha,\beta)` corresponds to the correlations between sources :math:`\alpha` and :math:`\beta`. - :math:`\big\langle\mathcal{I}_\mathrm{e}\big\rangle_{\alpha\beta}^{(gg')}` - are the correlation infidelities that can be computed by setting - ``which='correlations'``. + :math:`\mathcal{I}_{\alpha\beta}^{(gg')}` are the correlation + infidelities that can be computed by setting ``which='correlations'``. To convert to the average gate infidelity, use the following relation given by Horodecki et al. [Hor99]_ and @@ -928,8 +930,7 @@ def infidelity(pulse: 'PulseSequence', .. math:: - \big\langle\mathcal{I}_\mathrm{avg}\big\rangle = \frac{d}{d+1} - \big\langle\mathcal{I}_\mathrm{e}\big\rangle. + \mathcal{I}_\mathrm{avg} = \frac{d}{d+1}\mathcal{I}. The smallness parameter is given by @@ -1154,12 +1155,6 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, R_left, R_right = [f(r) for f, r in zip(funs, R)] else: R_left, R_right = [f(r) for f, r in zip(funs, [R]*2)] - - if which_pulse == 'correlations': - subs = ['g', '', 'h', 'gh'] - elif which_pulse == 'total': - subs = ['', '', '', ''] - elif F is not None: if which_FF == 'generalized': # Everything simpler if noise operators always on 2nd-to-last axes @@ -1189,8 +1184,11 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, # basis elements, frequencies) integrand = np.moveaxis(integrand, source=-2, destination=-4) elif R is not None: - s = [''.join(z) for z in zip(subs, ['ako', 'ao', 'alo', 'aklo'])] - einsum_str = ','.join(s[:3]) + '->' + s[3] + if which_pulse == 'correlations': + einsum_str = 'gako,ao,halo->ghaklo' + elif which_pulse == 'total': + einsum_str = 'ako,ao,alo->aklo' + integrand = np.einsum(einsum_str, R_left, S, R_right).real elif S.ndim == 3: # General case where S is a matrix with correlation spectra on off-diag @@ -1204,10 +1202,13 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, integrand = np.moveaxis(integrand, source=[-3, -2], destination=[-5, -4]) elif R is not None: - s = [''.join(z) for z in zip(subs, ['ako', 'abo', 'alo', 'abklo'])] - einsum_str = ','.join(s[:3]) + '->' + s[3] + if which_pulse == 'correlations': + einsum_str = 'gako,abo,hblo->ghabklo' + elif which_pulse == 'total': + einsum_str = 'ako,abo,blo->abklo' + integrand = np.einsum(einsum_str, R_left, S, R_right) - elif S.ndim > 3: + else: raise ValueError('Expected S to be array_like with < 4 dimensions') return integrand From 077e8b0b696263056505304f060526cf396d3728 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Sun, 14 Jun 2020 15:00:50 +0200 Subject: [PATCH 05/22] Fix bugs and tests --- filter_functions/numeric.py | 95 +++++++++++------ filter_functions/plotting.py | 5 +- filter_functions/pulse_sequence.py | 8 +- filter_functions/util.py | 11 +- tests/test_plotting.py | 10 +- tests/test_precision.py | 160 +++++++++++++++-------------- 6 files changed, 162 insertions(+), 127 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index b8aef59..b84edf7 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -335,11 +335,13 @@ def calculate_control_matrix_periodic(phases: ndarray, R: ndarray, return R_tot +@util.parse_optional_parameter('which', ('total', 'correlations')) def calculate_cumulant_function( pulse: 'PulseSequence', S: ndarray, omega: Coefficients, n_oper_identifiers: Optional[Sequence[str]] = None, + which: Optional[str] = 'total', show_progressbar: Optional[bool] = False, memory_parsimonious: Optional[bool] = False) -> ndarray: r"""Calculate the cumulant function :math:`K(\tau)`. @@ -352,7 +354,7 @@ def calculate_cumulant_function( pulse: PulseSequence The ``PulseSequence`` instance for which to compute the cumulant function. - S: array_like, shape (..., n_omega) + S: array_like, shape ([[n_nops,] n_nops,] n_omega) The two-sided noise power spectral density in units of inverse frequencies as an array of shape (n_omega,), (n_nops, n_omega), or (n_nops, n_nops, n_omega). In the first case, the same spectrum is @@ -369,6 +371,10 @@ def calculate_cumulant_function( n_oper_identifiers: array_like, optional The identifiers of the noise operators for which to evaluate the cumulant function. The default is all. + which: str, optional + Which decay amplitudes should be calculated, may be either 'total' + (default) or 'correlations'. See :func:`infidelity` and + :ref:`Notes `. show_progressbar: bool, optional Show a progress bar for the calculation of the control matrix. memory_parsimonious: bool, optional @@ -377,10 +383,12 @@ def calculate_cumulant_function( Returns ------- - K: ndarray, shape ([n_nops,] n_nops, d**2, d**2) + K: ndarray, shape ([[n_pls, n_pls,] n_nops,] n_nops, d**2, d**2) The cumulant function. The individual noise operator contributions - chosen by ``n_oper_identifiers`` are on the first axis / axes, - depending on whether the noise is cross-correlated or not. + chosen by ``n_oper_identifiers`` are on the third to last axis / axes, + depending on whether the noise is cross-correlated or not. If + ``which == 'correlations'``, the first two axes correspond to the + contributions of the pulses in the sequence. Notes ----- @@ -388,7 +396,7 @@ def calculate_cumulant_function( .. math:: - K_{ij}(\tau) = -\frac{1}{2}\sum_{\alpha\beta} & \sum_{kl}\biggl( + K_{\alpha\beta,ij}(\tau) = -\frac{1}{2} \sum_{kl}\biggl( \Delta_{\alpha\beta,kl}\left( T_{klji} - T_{lkji} - T_{klij} + T_{lkij} \right) + \Gamma_{\alpha\beta,kl}\left( @@ -408,23 +416,38 @@ def calculate_cumulant_function( .. math:: - K_{ij}(\tau) = \begin{cases} - - \sum_{k\neq i}\Gamma_{kk} &\qif* i = j, \\ - - \Delta_{ij} + \Delta_{ji} + \Gamma_{ij} &\qif* i\neq j, + K_{\alpha\beta,ij}(\tau) = \begin{cases} + - \sum_{k\neq i}\Gamma_{\alpha\beta,kk} + &\quad\mathrm{if}\: i = j, \\ + - \Delta_{\alpha\beta,ij} + \Delta_{\alpha\beta,ji} + + \Gamma_{\alpha\beta,ij} + &\quad\mathrm{if}\: i\neq j, \end{cases} for :math:`i\in\{1,2,3\}` and :math:`K_{0j} = K_{i0} = 0`. + Lastly, the pulse correlation cumulant function resolves correlations in + the cumulant function of a sequence of pulses :math:`g = 1,\dotsc,G` such + that the following holds: + + .. math:: + + K_{\alpha\beta,ij}(\tau) = \sum_{g,g'=1}^G + K_{\alpha\beta,ij}^{(gg')}(\tau). + See Also -------- calculate_decay_amplitudes: Calculate the :math:`\Gamma_{\alpha\beta,kl}` error_transfer_matrix: Calculate the error transfer matrix :math:`\exp K`. infidelity: Calculate only infidelity of a pulse. + pulse_sequence.concatenate: Concatenate ``PulseSequence`` objects. + calculate_pulse_correlation_filter_function """ N, d = pulse.basis.shape[:2] Gamma = calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers, - show_progressbar, memory_parsimonious) + which, show_progressbar, + memory_parsimonious) if d == 2 and pulse.basis.btype in ('Pauli', 'GGM'): # Single qubit case. Can use simplified expression @@ -439,18 +462,18 @@ def calculate_cumulant_function( # start at K_11. diag_items = deque((True, False, True, True)) for i in range(1, N): - K[..., i, i] = -Gamma[..., diag_items, diag_items].sum(axis=-1) + K[..., i, i] = - Gamma[..., diag_items, diag_items].sum(axis=-1) # shift the item not summed over by one diag_items.rotate() else: # Multi qubit case. Use general expression. T = pulse.basis.four_element_traces - K = (oe.contract('...kl,lkji->...ij', Gamma, T, backend='sparse') + - oe.contract('...kl,klij->...ij', Gamma, T, backend='sparse') - - oe.contract('...kl,klji->...ij', Gamma, T, backend='sparse') - - oe.contract('...kl,lkij->...ij', Gamma, T, backend='sparse'))/2 + K = - (oe.contract('...kl,klji->...ij', Gamma, T, backend='sparse') - + oe.contract('...kl,kjli->...ij', Gamma, T, backend='sparse') - + oe.contract('...kl,kilj->...ij', Gamma, T, backend='sparse') + + oe.contract('...kl,kijl->...ij', Gamma, T, backend='sparse'))/2 - return K + return K.real @util.parse_optional_parameter('which', ('total', 'correlations')) @@ -471,7 +494,7 @@ def calculate_decay_amplitudes( pulse: PulseSequence The ``PulseSequence`` instance for which to compute the decay amplitudes. - S: array_like, shape ([n_nops, n_nops,] n_omega) + S: array_like, shape ([[n_nops,] n_nops,] n_omega) The two-sided noise power spectral density. If 1-d, the same spectrum is used for all noise operators. If 2-d, one (self-) spectrum for each noise operator is expected. If 3-d, should be a matrix of cross-spectra @@ -508,7 +531,7 @@ def calculate_decay_amplitudes( Returns ------- - Gamma: ndarray, shape ([n_pls, n_pls, n_nops, n_nops,] d**2, d**2) + Gamma: ndarray, shape ([[n_pls, n_pls,] n_nops,] n_nops, d**2, d**2) The decay amplitudes. Notes @@ -539,7 +562,7 @@ def calculate_decay_amplitudes( idx = util.get_indices_from_identifiers(pulse, n_oper_identifiers, 'noise') if which == 'total': R = pulse.get_control_matrix(omega, show_progressbar)[idx] - elif which =='correlations': + elif which == 'correlations': # Check if cached frequencies coincide with given if pulse.is_cached('omega'): if not np.array_equal(pulse.omega, omega): @@ -552,11 +575,11 @@ def calculate_decay_amplitudes( if not memory_parsimonious: integrand = _get_integrand(S, omega, idx, which, 'generalized', R=R) Gamma = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) - return Gamma + return Gamma.real # Conserve memory by looping. Let _get_integrand determine the shape integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., 0:1], R]) + R=[R[..., 0:1, :], R]) n_kl = R.shape[1] Gamma = np.zeros(integrand.shape[:-3] + (n_kl,)*2, @@ -566,11 +589,11 @@ def calculate_decay_amplitudes( for k in util.progressbar_range(1, n_kl, show_progressbar=show_progressbar, desc='Integrating'): integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., k:k+1], R]) + R=[R[..., k:k+1, :], R]) Gamma[..., k:k+1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) - return Gamma + return Gamma.real @util.parse_which_FF_parameter @@ -631,6 +654,9 @@ def calculate_pulse_correlation_filter_function( ---------- R: array_like, shape (n_pulses, n_nops, d**2, n_omega) The control matrix. + which : str, optional + Which filter function to return. Either 'fidelity' (default) or + 'generalized' (see :ref:`Notes `). Returns ------- @@ -638,9 +664,6 @@ def calculate_pulse_correlation_filter_function( The pulse correlation filter functions for each pulse and noise operator correlations. The first two axes hold the pulse correlations, the second two the noise correlations. - which : str, optional - Which filter function to return. Either 'fidelity' (default) or - 'generalized' (see :ref:`Notes `). Notes ----- @@ -747,7 +770,7 @@ def error_transfer_matrix( pulse: PulseSequence The ``PulseSequence`` instance for which to compute the error transfer matrix. - S: array_like, shape (..., n_omega) + S: array_like, shape ([[n_nops,] n_nops,] n_omega) The two-sided noise power spectral density in units of inverse frequencies as an array of shape (n_omega,), (n_nops, n_omega), or (n_nops, n_nops, n_omega). In the first case, the same spectrum is @@ -763,7 +786,10 @@ def error_transfer_matrix( about zero. n_oper_identifiers: array_like, optional The identifiers of the noise operators for which to evaluate the - error transfer matrix. The default is all. + error transfer matrix. The default is all. Note that, since in general + contributions from different noise operators won't commute, not + selecting all noise operators results in neglecting terms of order + :math:`\xi^4`. show_progressbar: bool, optional Show a progress bar for the calculation of the control matrix. memory_parsimonious: bool, optional @@ -810,9 +836,10 @@ def error_transfer_matrix( infidelity: Calculate only infidelity of a pulse. """ K = calculate_cumulant_function(pulse, S, omega, n_oper_identifiers, - show_progressbar, memory_parsimonious) + 'total', show_progressbar, + memory_parsimonious) - U = sla.expm(K.sum(axis=list(range(K.ndim - 2)))) + U = sla.expm(K.sum(axis=tuple(range(K.ndim - 2)))) return U @@ -836,7 +863,7 @@ def infidelity(pulse: 'PulseSequence', pulse: PulseSequence The ``PulseSequence`` instance for which to calculate the infidelity for. - S: array_like or callable + S: array_like, shape ([[n_nops,] n_nops,] omega) or callable The two-sided noise power spectral density in units of inverse frequencies as an array of shape (n_omega,), (n_nops, n_omega), or (n_nops, n_nops, n_omega). In the first case, the same spectrum is @@ -883,7 +910,7 @@ def infidelity(pulse: 'PulseSequence', Returns ------- - infid: ndarray + infid: ndarray, shape ([[n_pls, n_pls,], n_nops,] n_nops) Array with the infidelity contributions for each spectrum *S* on the last axis or axes, depending on the shape of *S* and *which*. If ``which`` is ``correlations``, the first two axes are the individual @@ -1041,7 +1068,7 @@ def infidelity(pulse: 'PulseSequence', F = pulse.get_pulse_correlation_filter_function() integrand = _get_integrand(S, omega, idx, which, 'fidelity', F=F) - infid = integrate.trapz(integrand, omega)/(2*np.pi*pulse.d) + infid = integrate.trapz(integrand, omega).real/(2*np.pi*pulse.d) if return_smallness: if S.ndim > 2: @@ -1115,7 +1142,7 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, Parameters ---------- - S: array_like, shape (..., n_omega) + S: array_like, shape ([[n_nops,] n_nops,] n_omega) The two-sided noise power spectral density. omega: array_like, The frequencies. Note that the frequencies are assumed to be symmetric @@ -1197,7 +1224,7 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, raise ValueError(S_err_str.format(shape, S.shape)) if F is not None: - integrand = F*S + integrand = F[..., idx[:, None], idx, :]*S if which_FF == 'generalized': integrand = np.moveaxis(integrand, source=[-3, -2], destination=[-5, -4]) diff --git a/filter_functions/plotting.py b/filter_functions/plotting.py index 2097548..9e5e8ee 100644 --- a/filter_functions/plotting.py +++ b/filter_functions/plotting.py @@ -678,12 +678,11 @@ def plot_cumulant_function( n_oper_identifiers, 'noise') n_oper_identifiers = pulse.n_oper_identifiers[n_oper_inds] - # Get the cumulant function K = numeric.calculate_cumulant_function(pulse, S, omega, - n_oper_identifiers) + n_oper_identifiers, 'total') if K.ndim == 4: # Only autocorrelated noise supported - K = K[range(len(n_oper_inds)), range(len(n_oper_inds))] + K = K[tuple(n_oper_inds), tuple(n_oper_inds)] # Only autocorrelated noise implemented for now, ie U is real K = K.real diff --git a/filter_functions/pulse_sequence.py b/filter_functions/pulse_sequence.py index f5864bc..49b976e 100644 --- a/filter_functions/pulse_sequence.py +++ b/filter_functions/pulse_sequence.py @@ -480,7 +480,7 @@ def cache_control_matrix(self, omega: Coefficients, ---------- omega: array_like, shape (n_omega,) The frequencies for which to cache the filter function. - R: array_like, shape (n_nops, [n_nops,] d**2, n_omega), optional + R: array_like, shape ([n_nops,] n_nops, d**2, n_omega), optional The control matrix for the frequencies *omega*. If ``None``, it is computed. show_progressbar: bool @@ -600,14 +600,14 @@ def cache_filter_function(self, omega: Coefficients, ---------- omega: array_like, shape (n_omega,) The frequencies for which to cache the filter function. - R: array_like, shape (n_nops, [n_nops,] d**2, n_omega), optional + R: array_like, shape ([n_nops,] n_nops, d**2, n_omega), optional The control matrix for the frequencies *omega*. If ``None``, it is computed and the filter function derived from it. F: array_like, shape (n_nops, n_nops, [d**2, d**2,] n_omega), optional The filter function for the frequencies *omega*. If ``None``, it is computed from R. which: str, optional - Which filter function to return. Either 'fidelity' (default) or + Which filter function to cache. Either 'fidelity' (default) or 'generalized'. show_progressbar: bool Show a progress bar for the calculation of the control matrix. @@ -667,7 +667,7 @@ def get_pulse_correlation_filter_function( Returns ------- - F_pc: ndarray, shape (n_pulses, n_pulses, n_nops, n_nops, n_omega) + F_pc: ndarray, shape (n_pls, n_pls, n_nops, n_nops, n_omega) The pulse correlation filter function for each noise operator as a function of omega. The first two axes correspond to the pulses in the sequence, i.e. if the concatenated pulse sequence is diff --git a/filter_functions/util.py b/filter_functions/util.py index ded367f..9fa1aa3 100644 --- a/filter_functions/util.py +++ b/filter_functions/util.py @@ -983,19 +983,22 @@ def get_sample_frequencies(pulse: 'PulseSequence', n_samples: int = 300, def symmetrize_spectrum(S: ndarray, omega: ndarray) -> Tuple[ndarray, ndarray]: - r""" - Symmetrize a one-sided power spectrum around zero frequency. + r"""Symmetrize a one-sided power spectrum around zero frequency. + + Cross-spectra will have their real parts symmetrized and imaginary parts + anti-symmetrized such that for the correlation functions in time space + :math:`C_{\alpha\beta}(t) = C_{\beta\alpha}(-t)` holds. Parameters ---------- - S: ndarray, shape (..., n_omega) + S: ndarray, shape ([[n_nops,] n_nops,] n_omega) The one-sided power spectrum. omega: ndarray, shape (n_omega,) The positive and strictly increasing frequencies. Returns ------- - S: ndarray, shape (..., 2*n_omega) + S: ndarray, shape ([[n_nops,] n_nops,], 2*n_omega) The two-sided power spectrum. omega: ndarray, shape (2*n_omega,) The frequencies mirrored about zero. diff --git a/tests/test_plotting.py b/tests/test_plotting.py index a954354..a9a1293 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -35,7 +35,7 @@ import qutip as qt import filter_functions as ff -from filter_functions import plotting +from filter_functions import numeric, plotting from tests import testutil simple_pulse = testutil.rand_pulse_sequence(2, 1, 1, 1, btype='Pauli') @@ -261,15 +261,15 @@ def test_plot_cumulant_function(self): grid=grid) # Test calling with precomputed transfer matrix - K = ff.calculate_cumulant_function(simple_pulse, S, omega) + K = numeric.calculate_cumulant_function(simple_pulse, S, omega) fig, grid = plotting.plot_cumulant_function(K=K) # Test calling with precomputed transfer matrix and pulse - K = ff.calculate_cumulant_function(simple_pulse, S, omega) + K = numeric.calculate_cumulant_function(simple_pulse, S, omega) fig, grid = plotting.plot_cumulant_function(simple_pulse, K=K) # Test calling with precomputed transfer matrix of ndim == 2 - K = ff.calculate_cumulant_function(simple_pulse, S, omega) + K = numeric.calculate_cumulant_function(simple_pulse, S, omega) fig, grid = plotting.plot_cumulant_function(K=K[0]) # Log colorscale @@ -288,7 +288,7 @@ def test_plot_cumulant_function(self): omega = ff.util.get_sample_frequencies(complicated_pulse, n_samples=50, spacing='log') S = np.exp(-omega**2) - K = ff.calculate_cumulant_function(complicated_pulse, S, omega) + K = numeric.calculate_cumulant_function(complicated_pulse, S, omega) fig, grid = plotting.plot_cumulant_function( complicated_pulse, S=S, omega=omega, n_oper_identifiers=n_oper_identifiers, basis_labels=basis_labels, diff --git a/tests/test_precision.py b/tests/test_precision.py index afe3cee..233d572 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -23,6 +23,7 @@ """ import numpy as np +from scipy import linalg as sla import qutip as qt import filter_functions as ff @@ -259,9 +260,9 @@ def test_infidelity_cnot(self): 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 + K = numeric.calculate_cumulant_function(cnot_full, S_t, omega_t, + identifiers[:3]) + infid_P = - np.trace(K[:, :16, :16], axis1=1, axis2=2).real/4**2 print(np.abs(1 - (infid.sum()/MC))) print(np.abs(1 - (infid_P.sum()/MC))) @@ -274,15 +275,15 @@ def test_infidelity(self): testutil.rng.seed(123456789) spectra = [ - lambda S0, omega: S0*omega**0, - lambda S0, omega: S0/omega**0.7, - lambda S0, omega: S0*np.exp(-omega), + 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*omega**0, S0/omega**0.7]), + lambda S0, omega: np.array([S0*abs(omega)**0, S0/abs(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]] + [[S0/abs(omega)**0.7, S0/(1 + omega**2) + 1j*S0*omega], + [S0/(1 + omega**2) - 1j*S0*omega, S0/abs(omega)**0.7]] ) ] @@ -291,20 +292,20 @@ def test_infidelity(self): [0.65826575772, 1.042914346335], [0.163303005479, 0.239032549377], [0.448468950307, 1.042914346335], - [[0.65826575772, 0.069510589685+0.069510589685j], - [0.069510589685-0.069510589685j, 1.042914346335]], + [[0.65826575772, 0.458623551679], + [0.458623551679, 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.590545568435, 0.577397865625], + [0.577397865625, 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]] + [[1.847740998731, 0.741483515315], + [0.741483515315, 1.559401345443]] ) count = 0 @@ -390,70 +391,71 @@ def test_single_qubit_error_transfer_matrix(self): # Single spectrum # Assert fidelity is same as computed by infidelity() - S = 1e-2/omega**2 + S = 1e-8/omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) - I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) - - # Check that _single_qubit_error_transfer_matrix and - # _multi_qubit_... # give the same + # Calculate on foot (multi-qubit way) Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers) - U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + - np.einsum('...kl,klji->...ij', Gamma, traces)/2 - - np.einsum('...kl,kilj->...ij', Gamma, traces)) - self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) + K = -(np.einsum('...kl,klji->...ij', Gamma, traces) - + np.einsum('...kl,kjli->...ij', Gamma, traces) - + np.einsum('...kl,kilj->...ij', Gamma, traces) + + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 + U_onfoot = sla.expm(K.sum(0)) + I_fidelity = ff.infidelity(pulse, S, omega) + I_decayamps = -np.einsum('...ii', K)/d**2 + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_fidelity, I_decayamps) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) + self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) # Different spectra for each noise oper - S = np.outer(1e-2*np.arange(1, 3), 400/(omega**2 + 400)) + S = np.outer(1e-6*np.arange(1, 3), 400/(omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) - # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) - I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) - - # Check that _single_qubit_error_transfer_matrix and - # _multi_qubit_... # give the same Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers) - U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + - np.einsum('...kl,klji->...ij', Gamma, traces)/2 - - np.einsum('...kl,kilj->...ij', Gamma, traces)) - self.assertArrayAlmostEqual(U, U_multi, atol=1e-14) - - # Cross-correlated spectra - S = np.einsum('i,j,o->ijo', - 1e-2*np.arange(1, 3), 1e-2*np.arange(1, 3), - 400/(omega**2 + 400), dtype=complex) - # Cross spectra are complex - S[0, 1] *= 1 + 1j - S[1, 0] *= 1 - 1j + K = -(np.einsum('...kl,klji->...ij', Gamma, traces) - + np.einsum('...kl,kjli->...ij', Gamma, traces) - + np.einsum('...kl,kilj->...ij', Gamma, traces) + + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 + U_onfoot = sla.expm(K.sum(0)) + I_fidelity = ff.infidelity(pulse, S, omega) + I_decayamps = -np.einsum('...ii', K)/d**2 + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_fidelity, I_decayamps) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) + self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) + + # Cross-correlated spectra are complex, real part symmetric and + # imaginary part antisymmetric + S = np.array([[1e-6/abs(omega), 1e-8/abs(omega) + 1j*1e-8/omega], + [1e-8/abs(omega) - 1j*1e-8/omega, 2e-6/abs(omega)]]) U = ff.error_transfer_matrix(pulse, S, omega) - # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) - I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) - - # Check that _single_qubit_error_transfer_matrix and - # _multi_qubit_... # give the same Gamma = numeric.calculate_decay_amplitudes(pulse, S, omega, n_oper_identifiers) - U_multi = np.zeros_like(U) - U_multi = (np.einsum('...kl,klij->...ij', Gamma, traces)/2 + - np.einsum('...kl,klji->...ij', Gamma, traces)/2 - - np.einsum('...kl,kilj->...ij', Gamma, traces)) - self.assertArrayAlmostEqual(U, U_multi, atol=1e-16) + K = -(np.einsum('...kl,klji->...ij', Gamma, traces) - + np.einsum('...kl,kjli->...ij', Gamma, traces) - + np.einsum('...kl,kilj->...ij', Gamma, traces) + + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 + U_onfoot = sla.expm(K.sum((0, 1))) + I_fidelity = ff.infidelity(pulse, S, omega) + I_decayamps = -np.einsum('...ii', K)/d**2 + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_fidelity, I_decayamps) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) + self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) def test_multi_qubit_error_transfer_matrix(self): """Test the calculation of the multi-qubit transfer matrix""" @@ -468,36 +470,40 @@ def test_multi_qubit_error_transfer_matrix(self): omega = ff.util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() - S = 1e-2/omega**2 + S = 1e-8/omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) - S = np.outer(1e-2*(np.arange(n_nops) + 1), + S = np.outer(1e-7*(np.arange(n_nops) + 1), 400/(omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) - - S = np.einsum('i,j,o->ijo', - 1e-2*(np.arange(n_nops) + 1), - 1e-2*(np.arange(n_nops) + 1), - 400/(omega**2 + 400)) + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) + + S = np.tile(1e-8/abs(omega)**2, (n_nops, n_nops, 1)).astype( + complex) + S[np.triu_indices(n_nops, 1)].imag = 1e-10*omega + S[np.tril_indices(n_nops, -1)].imag = \ + - S[np.triu_indices(n_nops, 1)].imag U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, memory_parsimonious=True) - self.assertArrayAlmostEqual(Up, U) I_fidelity = ff.infidelity(pulse, S, omega) - I_transfer = np.einsum('...ii', U)/d**2 - self.assertArrayAlmostEqual(I_transfer, I_fidelity) + I_transfer = 1 - np.einsum('...ii', U)/d**2 + self.assertArrayAlmostEqual(Up, U) + self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), + rtol=1e-4) From 1b670054a63aff2bf0bb4199c4abaa26df1f084f Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Sun, 14 Jun 2020 15:01:03 +0200 Subject: [PATCH 06/22] Make symmetrize_spectrum work with cross-correlated spectra --- filter_functions/util.py | 21 +++++++++++++++++++-- tests/test_util.py | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/filter_functions/util.py b/filter_functions/util.py index 9fa1aa3..fdc228a 100644 --- a/filter_functions/util.py +++ b/filter_functions/util.py @@ -1015,8 +1015,25 @@ def symmetrize_spectrum(S: ndarray, omega: ndarray) -> Tuple[ndarray, ndarray]: ix = 0 omega = np.concatenate((-omega[::-1], omega[ix:])) - S = np.concatenate((S[..., ::-1], S[ix:]), axis=-1)/2 - return S, omega + S_one = np.asarray(S) + S_two_real = np.concatenate( + (S_one[..., ::-1].real, S_one[..., ix:].real), axis=-1 + )/2 + if S_one.ndim < 3: + S_two = S_two_real + elif S_one.ndim == 3: + # Cross-correlated noise + diag = np.eye(S_one.shape[0], dtype=bool) + S_two_imag = np.zeros_like(S_two_real) + S_two_imag[~diag] = np.concatenate( + (-S_one[~diag, ::-1].imag, S_one[~diag, ix:].imag), axis=-1 + )/2 + + S_two = S_two_real + 1j*S_two_imag + else: + raise ValueError('Expected S.ndim <= 3 but found {}'.format(S.ndim)) + + return S_two, omega def hash_array_along_axis(arr: ndarray, axis: int = 0) -> List[int]: diff --git a/tests/test_util.py b/tests/test_util.py index 7396152..2f11f96 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -561,6 +561,31 @@ def test_symmetrize_spectrum(self): self.assertArrayEqual(S_sym, np.abs(np.arange(-9, 10)/2)) self.assertArrayEqual(omega_sym, np.arange(-9, 10)) + # Cross-correlated spectra + S = np.array([[np.ones_like(asym_omega), + np.ones_like(asym_omega) + 1j*asym_omega], + [np.ones_like(asym_omega) - 1j*asym_omega, + np.ones_like(asym_omega)]]) + S_symmetrized, omega_symmetrized = util.symmetrize_spectrum(S, + asym_omega) + self.assertArrayEqual(omega_symmetrized, sym_omega) + self.assertArrayEqual(S_symmetrized[..., 99::-1].real, + S_symmetrized[..., 100:].real) + self.assertArrayEqual(S_symmetrized[..., 99::-1].imag, + - S_symmetrized[..., 100:].imag) + self.assertArrayEqual(S_symmetrized[..., 100:]*2, S) + self.assertArrayEqual(S_symmetrized[..., 99::-1]*2, S.conj()) + + # 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)) + + with self.assertRaises(ValueError): + util.symmetrize_spectrum(testutil.rng.randn(2, 2, 2, omega.size), + omega) + def test_simple_progressbar(self): with self.assertRaises(TypeError): for i in util._simple_progressbar((i for i in range(10))): From 5b4cb08176f317806d94eb38c7211a31529b9b22 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 15 Jun 2020 10:44:02 +0200 Subject: [PATCH 07/22] Update documentation --- ...nb => calculating_quantum_processes.ipynb} | 74 +++++++++++-------- doc/source/examples/examples.rst | 2 +- doc/source/examples/getting_started.ipynb | 2 +- doc/source/examples/periodic_driving.ipynb | 22 +++--- .../examples/quantum_fourier_transform.ipynb | 2 +- doc/source/examples/qutip_integration.ipynb | 2 +- 6 files changed, 57 insertions(+), 47 deletions(-) rename doc/source/examples/{calculating_error_transfer_matrices.ipynb => calculating_quantum_processes.ipynb} (71%) diff --git a/doc/source/examples/calculating_error_transfer_matrices.ipynb b/doc/source/examples/calculating_quantum_processes.ipynb similarity index 71% rename from doc/source/examples/calculating_error_transfer_matrices.ipynb rename to doc/source/examples/calculating_quantum_processes.ipynb index fc048cc..5c77f6d 100644 --- a/doc/source/examples/calculating_error_transfer_matrices.ipynb +++ b/doc/source/examples/calculating_quantum_processes.ipynb @@ -4,26 +4,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Calculating error transfer matrices\n", - "In this example we would like to calculate the error transfer matrices of a two-qubit gateset for singlet-triplet qubits subject to $1/f$-like charge noise that are manipulated via a detuning-controlled exchange interaction. The error transfer matrix is here understood as the Liouville representation of the error propagator $\\tilde{U}(\\tau)$, or more precisely, as the first order correction to the identity process,\n", + "# Calculating Quantum Processes\n", + "In this example we would like to calculate the error transfer matrices (quantum processes of the error channels) of a two-qubit gateset for singlet-triplet qubits subject to $1/f$-like charge noise that are manipulated via a detuning-controlled exchange interaction. The error transfer matrix is here understood as the Liouville representation of the ensemble averaged error superpropagator $\\tilde{\\mathcal{U}}(\\tau)$. It is completely characterized by the cumulant function,\n", "\n", "$$\n", - "\\mathcal{\\tilde{U}} = \\mathbb{1} - \\mathcal{\\tilde{U}}^{(1)},\n", + "\\langle\\mathcal{\\tilde{U}}\\rangle = \\exp K(\\tau),\n", "$$\n", "\n", - "where $\\mathcal{\\tilde{U}}_{ij} = \\mathrm{tr}(C_i\\tilde{U}C_j\\tilde{U}^\\dagger)$ and $\\mathcal{\\tilde{U}}^{(1)}$ is what we refer to as the 'error transfer matrix'. It captures the deviation from the identity up to second order in noise and can be used to extract many useful quantities that describe the (unitary, in this case) channel. For instance, the entanglement infidelity of the pulse is given by\n", + "where $K(\\tau)$ is a $d^2\\times d^2$ matrix ($d$ being the dimension of the quantum system) expressed in a basis of orthonormal Hermitian matrices $\\mathcal{C}=\\{C_i\\}_{i=0}^{d^2-1}$. It captures the deviation from the identity channel and can be used to extract many useful quantities that describe the channel. For instance, the entanglement infidelity of the pulse is given by\n", "\n", "$$\n", - "d^2\\mathcal{I}_\\mathrm{e} = \\mathrm{tr}\\mathcal{\\tilde{U}}^{(1)}\n", + "\\mathcal{I}_\\mathrm{e} = 1 - \\frac{1}{d^2}\\mathrm{tr}\\,\\tilde{\\mathcal{U}} \\approx -\\mathrm{tr}\\,K(\\tau),\n", "$$\n", "\n", - "and the state fidelity (probability that a state is returned to itself) for pure input states by\n", + "where the approximation holds for small noise, and the state fidelity (probability that a state is returned to itself) for pure input states by\n", "\n", "$$\n", "p_{j\\rightarrow j} = \\langle\\!\\langle\\rho_j\\rvert\\mathcal{Q}\\mathcal{\\tilde{U}}\\lvert\\rho_j\\rangle\\!\\rangle\n", "$$\n", "\n", - "with $\\lvert\\rho_j\\rangle\\!\\rangle = \\sum_{k=0}^{d^2-1}\\mathrm{tr}(C_k\\rho_j)\\lvert k\\rangle\\!\\rangle$ the vectorized density matrix in the basis $\\mathcal{C}$ and $\\mathcal{Q}$ the Liouville representation of the total propagator $Q$.\n", + "with $\\lvert\\rho_j\\rangle\\!\\rangle = \\sum_{k=0}^{d^2-1}\\mathrm{tr}(C_k\\rho_j)\\lvert k\\rangle\\!\\rangle$ the vectorized density matrix in the basis $\\mathcal{C}$ and $\\mathcal{Q}$ the Liouville representation of the total propagator $Q$ of the control pulse.\n", + "\n", + "Within `filter_functions`, the cumulant function $K(\\tau)$ is calculated from first-order Magnus expansion terms alone. These terms induce dissipation. Additional second-order terms, inducing coherent errors, can be neglected if we assume that the experimentalist has calibrated their pulses. For Gaussian noise, higher orders cancel out and the above expressions are exact. In the case of non-Gaussian noise, they become peturbative in the noise parameter $\\xi$, being of order $\\xi^2$.\n", "\n", "Again we use the optimized gates presented in [Cerfontaine et al. (2019)] and start by loading the data and setting up the control operators $A_i$.\n", "\n", @@ -45,7 +47,7 @@ "from scipy import io\n", "\n", "import filter_functions as ff\n", - "from filter_functions import util" + "from filter_functions import numeric, util" ] }, { @@ -289,7 +291,7 @@ "Now that we have verified that our pulses do what they are supposed to, we can turn to calculating the error transfer matrix given a noise spectrum. As a quick reminder, the noise power spectral density $S_\\alpha(\\omega)$ is defined as the Fourier transform of the autocorrelation function of the noise variable $b_\\alpha(t)$. For *wide-sense stationary* noise the autocorrelation only depends on the time difference and we have\n", "\n", "$$\n", - " \\langle b_\\alpha(t_1)b_\\alpha(t_2)\\rangle = \\int_{-\\infty}^\\infty\\frac{\\mathrm{d}\\omega}{2\\pi}S_\\alpha(\\omega)e^{i\\omega(t_2 - t_1)}\n", + " \\langle b_\\alpha(t_1)b_\\alpha(t_2)\\rangle = \\int_{-\\infty}^\\infty\\frac{\\mathrm{d}\\omega}{2\\pi}S_\\alpha(\\omega)e^{-i\\omega(t_1 - t_2)}\n", "$$\n", "\n", "where $S_\\alpha(\\omega)$ is the two-sided power spectral density for noise source $\\alpha$. We consider here $1/f$-like noise with a one-sided spectrum of\n", @@ -325,8 +327,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Error transfer matrix\n", - "We now calculate the first order correction to the error transfer matrix, $\\mathcal{\\tilde{U}}^{(1)} = 1 - \\mathrm{tr}(C_i\\tilde{U}C_j\\tilde{U}^\\dagger)$ due to fast noise for each gate by numerically integrating over the spectrum times the filter function using $\\omega_\\text{IR}=1/T$ and $\\omega_\\text{UV}=100\\,\\text{GHz}$ as infrared and ultraviolet cutoffs, respectively, and compare to the Monte Carlo results presented by Cerfontaine et al. (2019).\n", + "## Error transfer matrices\n", + "We are now ready to calculate the error transfer matrix $\\langle\\tilde{\\mathcal{U}}(\\tau)\\rangle$. Since it is completely characterized by the cumulant function $K_\\epsilon(\\tau)$, we will deal with the latter. This has the advantage that we can inspect contributions from different noise sources separately. Because the error transfer matrix is given by the exponential of $K(\\tau)$, contributions from different noise operators will mix due to non-commutativity and a separate discussion of the contributions will neglect terms of order $\\xi^4$.\n", "\n", "### Performance considerations\n", "For the computation of the error transfer matrix it is beneficial for the basis used to be sparse since the elements of the matrix are sums of terms proportional to traces of four basis elements of the form $\\mathrm{tr}(C_i C_j C_k C_l)$. These traces are inherently mostly zero since the basis elements form an orthonormal set. If the elements themselves are sparse, the entire calculation can be performed quite efficiently. The Generalized Gell-Mann bases are sparse bases as most of their elements only have two non-zero entries. The Pauli bases have filling factor $1/2$ on the other hand and thus take longer to compute.\n", @@ -334,9 +336,9 @@ "Since the traces are cached, this only applies for the first time the error transfer matrix is computed.\n", "\n", "### Calculation using `filter_functions`\n", - "The calculation is implemented in the `ff.error_transfer_matrix()` function, which takes a `PulseSequence` instance, a noise spectrum and a list of frequencies as arguments. Optionally, we may also pass a list of identifiers corresponding to the noise operators whose contributions we are interested in. In our case we use the exchange terms affected by charge noise. The function will return an array with the separate noise operator contributions on the first axis.\n", + "The calculation is implemented in the `numeric.calculate_cumulant_function()` function, which takes a `PulseSequence` instance, a noise spectrum and a list of frequencies as arguments. Optionally, we may also pass a list of identifiers corresponding to the noise operators whose contributions we are interested in. In our case we use the exchange terms affected by charge noise. The function will return an array with the separate noise operator contributions on the first axis.\n", "\n", - "Since the `filter_functions` package assumes two-sided spectra, we need to rescale the above spectrum by a factor of two accordingly. This can be done using `util.symmetrize_spectrum()`. Visualization of the error transfer matrices is implemented by ``ff.plot_error_transfer_matrix()``." + "Since the `filter_functions` package assumes two-sided spectra, we need to rescale the above spectrum by a factor of two accordingly. This can be done using `util.symmetrize_spectrum()`. Visualization of the cumulant function is implemented by ``ff.plot_cumulant_function()``." ] }, { @@ -347,7 +349,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5db43feb22444799a5bec017b949fa67", + "model_id": "0e80dd2c9b494569883dee01f55d6f8d", "version_major": 2, "version_minor": 0 }, @@ -368,7 +370,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8bc1ef0f68c4406cb4351dfe1d13374d", + "model_id": "49327bb3305c4181ac2820d44e451426", "version_major": 2, "version_minor": 0 }, @@ -389,7 +391,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a8c86c35cf554615b463bc5c239fa7e8", + "model_id": "1fa1094d4aa44e4ba78af2d8635233f4", "version_major": 2, "version_minor": 0 }, @@ -409,7 +411,7 @@ }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -432,11 +434,11 @@ "# leakage subspace\n", "basis_labels.extend(['$C_{{{}}}$'.format(i) for i in range(16, 36)])\n", "\n", - "transfer_matrices = {}\n", + "cumulant_functions = {}\n", "for i, (key, pulse) in enumerate(pulses.items(), 1):\n", " omega_positive = np.geomspace(1/T[key], 1e2, 400)\n", " S_onesided = A/omega_positive**alpha\n", - " S_twosided, omega = ff.util.symmetrize_spectrum(S_onesided, omega_positive)\n", + " S_twosided, omega = util.symmetrize_spectrum(S_onesided, omega_positive)\n", "\n", " if key == 'CNOT':\n", " identifiers = ['J_12', 'J_23', 'J_34']\n", @@ -444,7 +446,7 @@ " # For single qubit pulses intermediate exchange is turned off\n", " identifiers = ['J_12', 'J_34']\n", " \n", - " transfer_matrices[key] = ff.error_transfer_matrix(\n", + " cumulant_functions[key] = numeric.calculate_cumulant_function(\n", " pulse, S_twosided, omega, n_oper_identifiers=identifiers,\n", " show_progressbar=True\n", " )\n", @@ -452,11 +454,13 @@ " # We can call plot_transfer matrix with the same arguments as\n", " # error_transfer_matrix in which case the transfer matrix is calculated on\n", " # the fly, or pass a pre-computed transfer matrix to the function\n", - " fig, grid = ff.plot_error_transfer_matrix(\n", - " U=transfer_matrices[key], n_oper_identifiers=identifiers,\n", + " fig, grid = ff.plot_cumulant_function(\n", + " K=cumulant_functions[key], n_oper_identifiers=identifiers,\n", " basis_labels=basis_labels, grid_kw=dict(rect=10*31+i), figsize=(11, 4),\n", " fig=fig, cbar_label=key, colorscale='log'\n", - " )" + " )\n", + " \n", + "plt.show()" ] }, { @@ -471,7 +475,7 @@ "metadata": {}, "source": [ "## Deriving quantities\n", - "With the error transfer matrix calculated, we can now derive different quantities. Here we take a look at the state fidelity of the computational basis states for the CNOT gate and the entanglement fidelities of the gates.\n", + "With the cumulant function calculated, we can now derive different quantities from the error transfer matrix $\\langle\\tilde{\\mathcal{U}}\\rangle = \\exp K$. Here we take a look at the state fidelity of the computational basis states for the CNOT gate and the entanglement fidelities of the gates.\n", "\n", "### State fidelity\n", "The state fidelity is given by (see above)\n", @@ -494,18 +498,24 @@ "text": [ "p(00->00) = 9.9991e-01\n", "p(01->01) = 9.9992e-01\n", - "p(10->10) = 2.3946e-05\n", - "p(11->11) = 2.3946e-05\n" + "p(10->10) = 2.3944e-05\n", + "p(11->11) = 2.3944e-05\n" ] } ], "source": [ + "# ff.error_transfer_matrix can also be called with a PulseSequence, spectrum\n", + "# and list of frequencies to compute the error transfer matrix directly instead\n", + "# of from a cumulant function\n", + "transfer_matrices = {gate: ff.error_transfer_matrix(K=K)\n", + " for gate, K in cumulant_functions.items()}\n", + "\n", "pulse = pulses['CNOT']\n", "basis = pulse.basis\n", "# Also exists as pulse.total_Q_liouville\n", "total_Q_liouville = ff.liouville_representation(pulse.total_Q, basis)\n", "# Sum up the individual noise operator contributions for simplicity\n", - "U_tilde = np.eye(36) - transfer_matrices['CNOT'].sum(axis=0)\n", + "U_tilde = transfer_matrices['CNOT']\n", "ket_00, ket_01, ket_10, ket_11 = np.zeros((4, 6, 1))\n", "ket_00[1] = 1\n", "ket_01[2] = 1\n", @@ -538,12 +548,12 @@ "The entanglement fidelity is given by (see above)\n", "\n", "$$\n", - "d^2\\mathcal{I}_\\mathrm{e} = \\mathrm{tr}\\mathcal{\\tilde{U}}^{(1)}.\n", + "\\mathcal{I}_\\mathrm{e} = 1 - \\frac{1}{d^2}\\mathrm{tr}\\,\\mathcal{\\tilde{U}}.\n", "$$\n", "\n", "We are only interested in the dynamics on the qubit subspace and thus do not care about contributions from basis elements on the leakage subspace, so that we only trace over the first 16 elements of the error transfer matrix. We will compare the results to the Monte Carlo results for fast noise from the reference.\n", "\n", - "Because the fidelity is an often-employed figure of merit, its calculation is also implemented in a separate function, `ff.infidelity()`, that is much faster than deriving the fidelity from the error transfer matrix. Its interface is very similiar to that of `ff.error_transfer_matrix()` and is thus not discussed here. Only note that some subtleties apply if calculating the fidelity in the presence of leakage levels." + "Because the fidelity is an often-employed figure of merit, its calculation is also implemented in a separate function, `ff.infidelity()`, that is much faster than deriving the fidelity from the error transfer matrix because it only calculates the leading order approximation. Its interface is very similiar to that of `ff.error_transfer_matrix()` and is thus not discussed here. Only note that some subtleties apply if calculating the fidelity in the presence of leakage levels." ] }, { @@ -557,7 +567,7 @@ "text": [ "Gate\tTransfer Matrix\tMonte Carlo\tRelative deviation\n", "----------------------------------------------------------\n", - "X2ID\t7.24e-05\t7.17e-05\t9.6e-03\n", + "X2ID\t7.24e-05\t7.17e-05\t9.5e-03\n", "Y2ID\t7.09e-05\t7.03e-05\t8.4e-03\n", "CNOT\t7.97e-05\t7.89e-05\t9.5e-03\n" ] @@ -567,7 +577,7 @@ "print('Gate', 'Transfer Matrix', 'Monte Carlo', 'Relative deviation', sep='\\t')\n", "print('----------------------------------------------------------')\n", "for key, pulse in pulses.items():\n", - " infidelity = np.einsum('...ii', transfer_matrices[key][:, :16, :16])/4**2\n", + " infidelity = 1 - transfer_matrices[key][:16, :16].trace()/4**2\n", " rel_div = (infidelity.sum() - infid_fast[key][1])/infid_fast[key][1]\n", " print(key,\n", " '{:.2e}'.format(infidelity.sum().real),\n", @@ -655,7 +665,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.8.3" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/doc/source/examples/examples.rst b/doc/source/examples/examples.rst index 664aad2..16f218e 100644 --- a/doc/source/examples/examples.rst +++ b/doc/source/examples/examples.rst @@ -8,7 +8,7 @@ This directory contains static examples that can also be run interactively from getting_started periodic_driving advanced_concatenation - calculating_error_transfer_matrices + calculating_quantum_processes extending_pulses qutip_integration quantum_fourier_transform diff --git a/doc/source/examples/getting_started.ipynb b/doc/source/examples/getting_started.ipynb index 84fb472..740113e 100644 --- a/doc/source/examples/getting_started.ipynb +++ b/doc/source/examples/getting_started.ipynb @@ -31,7 +31,7 @@ " {H}_n &= \\sum_\\alpha s_\\alpha(t) b_\\alpha(t) B_\\alpha \\\\\n", "\\end{align*}\n", "\n", - "where $A_i$ and $B_\\alpha$ are the control and noise operators, respectively, $a_i(t)$ the control strength of $A_i$ at time $t$, $s_\\alpha(t)$ the sensitivity at time $t$ of the system to noise source $\\alpha$, and $b_\\alpha(t)$ classically fluctuating noise variables. Since the noise is captured in a spectral density function that accounts for different realizations of the random noise, the $b_\\alpha(t)$ are not required at instantiation of a `PulseSequence` instance. Note that we always calculate in units where $\\hbar\\equiv 1$.\n", + "where $A_i$ and $B_\\alpha$ are the control and noise operators, respectively, $a_i(t)$ the control strength of $A_i$ at time $t$, $s_\\alpha(t)$ a deterministic time dependence of the noise operators to model, for instance, non-linear coupling to the noise sources, and $b_\\alpha(t)$ classically fluctuating noise variables. Since the noise is captured in a spectral density function that accounts for different realizations of the random noise, the $b_\\alpha(t)$ are not required at instantiation of a `PulseSequence` instance. Note that we always calculate in units where $\\hbar\\equiv 1$.\n", "\n", "The `PulseSequence` class requires three positional arguments at instantiation; `H_c`, `H_n`, and `dt`, with `dt` the time-deltas of piece-wise constant control. The former two represent the control and noise Hamiltonians and are passed in the same nested-list-of-lists structure of operators and coefficient lists similar to that required by [QuTiP](http://qutip.org/) (the difference being that QuTiP requires implicit functions for calculating the coefficients instead of explicit values). Optionally, we pass unique identifiers for each operator as a third element of the list. That is, \n", "\n", diff --git a/doc/source/examples/periodic_driving.ipynb b/doc/source/examples/periodic_driving.ipynb index d4bba52..28d854a 100644 --- a/doc/source/examples/periodic_driving.ipynb +++ b/doc/source/examples/periodic_driving.ipynb @@ -183,7 +183,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -249,7 +249,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "30b23e89df10479c8c339c7abd7c19ff", + "model_id": "04190ea704d242d58228b8357a015184", "version_major": 2, "version_minor": 0 }, @@ -296,16 +296,16 @@ "output_type": "stream", "text": [ "===========================================\n", - "ATOMIC initialization\t\t : 0.0013 s\n", - "ATOMIC filter function\t\t : 0.0395 s\n", - "NOT concatenation (periodic)\t : 0.0837 s\n", - "NOT concatenation (standard)\t : 5.5189 s\n", - "ECHO concatenation\t\t : 0.0384 s\n", + "ATOMIC initialization\t\t : 0.0018 s\n", + "ATOMIC filter function\t\t : 0.0354 s\n", + "NOT concatenation (periodic)\t : 0.1257 s\n", + "NOT concatenation (standard)\t : 5.8858 s\n", + "ECHO concatenation\t\t : 0.0454 s\n", "-------------------------------------------\n", - "Total (periodic)\t\t : 0.1628 s\n", - "Total (standard)\t\t : 5.5980 s\n", + "Total (periodic)\t\t : 0.2083 s\n", + "Total (standard)\t\t : 5.9684 s\n", "===========================================\n", - "Total (brute force)\t\t : 148.03 s\n", + "Total (brute force)\t\t : 242.65 s\n", "===========================================\n" ] } @@ -353,7 +353,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.8.3" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/doc/source/examples/quantum_fourier_transform.ipynb b/doc/source/examples/quantum_fourier_transform.ipynb index 8a66868..777f45e 100644 --- a/doc/source/examples/quantum_fourier_transform.ipynb +++ b/doc/source/examples/quantum_fourier_transform.ipynb @@ -1094,7 +1094,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.8.3" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/doc/source/examples/qutip_integration.ipynb b/doc/source/examples/qutip_integration.ipynb index 0d485e9..58de7d1 100644 --- a/doc/source/examples/qutip_integration.ipynb +++ b/doc/source/examples/qutip_integration.ipynb @@ -138,7 +138,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.8.3" } }, "nbformat": 4, From 4ff9fe4c364d445c4601aa6282ce30403d6ee27c Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 15 Jun 2020 12:12:29 +0200 Subject: [PATCH 08/22] Small changes to docstrings --- filter_functions/numeric.py | 21 +++++++++++++++------ filter_functions/plotting.py | 8 ++++---- filter_functions/pulse_sequence.py | 2 ++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index b84edf7..1cbe766 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -347,7 +347,7 @@ def calculate_cumulant_function( r"""Calculate the cumulant function :math:`K(\tau)`. The error transfer matrix is obtained from the cumulant function by - exponentiation, :math:`\tilde{\mathcal{U}} = \exp K(\tau)`. + exponentiation, :math:`\langle\tilde{\mathcal{U}}\rangle = \exp K(\tau)`. Parameters ---------- @@ -390,6 +390,8 @@ def calculate_cumulant_function( ``which == 'correlations'``, the first two axes correspond to the contributions of the pulses in the sequence. + .. _notes: + Notes ----- The cumulant function is given by @@ -397,9 +399,9 @@ def calculate_cumulant_function( .. math:: K_{\alpha\beta,ij}(\tau) = -\frac{1}{2} \sum_{kl}\biggl( - \Delta_{\alpha\beta,kl}\left( + &\Delta_{\alpha\beta,kl}\left( T_{klji} - T_{lkji} - T_{klij} + T_{lkij} - \right) + \Gamma_{\alpha\beta,kl}\left( + \right) \\ + &\Gamma_{\alpha\beta,kl}\left( T_{klji} - T_{kjli} - T_{kilj} + T_{kijl} \right) \biggr) @@ -534,6 +536,8 @@ def calculate_decay_amplitudes( Gamma: ndarray, shape ([[n_pls, n_pls,] n_nops,] n_nops, d**2, d**2) The decay amplitudes. + .. _notes: + Notes ----- The total decay amplitudes are given by @@ -614,6 +618,8 @@ def calculate_filter_function(R: ndarray, which: str = 'fidelity') -> ndarray: The filter functions for each noise operator correlation. The diagonal corresponds to the filter functions for uncorrelated noise sources. + .. _notes: + Notes ----- The generalized filter function is given by @@ -665,6 +671,8 @@ def calculate_pulse_correlation_filter_function( operator correlations. The first two axes hold the pulse correlations, the second two the noise correlations. + .. _notes: + Notes ----- The generalized pulse correlation filter function is given by @@ -757,9 +765,10 @@ def diagonalize(H: ndarray, dt: Coefficients) -> Tuple[ndarray]: def error_transfer_matrix( - pulse: 'PulseSequence', - S: ndarray, - omega: Coefficients, + pulse: Optional['PulseSequence'] = None, + S: Optional[ndarray] = None, + omega: Optional[Coefficients] = None, + K: Optional[ndarray] = None, n_oper_identifiers: Optional[Sequence[str]] = None, show_progressbar: Optional[bool] = False, memory_parsimonious: Optional[bool] = False) -> ndarray: diff --git a/filter_functions/plotting.py b/filter_functions/plotting.py index 9e5e8ee..5c36b05 100644 --- a/filter_functions/plotting.py +++ b/filter_functions/plotting.py @@ -610,13 +610,13 @@ def plot_cumulant_function( ---------- pulse: 'PulseSequence' The pulse sequence. - S: ndarray + S: array_like, shape ([[n_nops,] n_nops,] n_omega) The two-sided noise spectrum. omega: array_like The frequencies for which to evaluate the cumulant function. Note that they should be symmetric around zero, that is, include negative frequencies. - K: ndarray, shape + K: ndarray, shape (n_nops, d**2, d**2) A precomputed cumulant function. If given, *pulse*, *S*, *omega* are not required. n_oper_identifiers: array_like, optional @@ -671,8 +671,8 @@ def plot_cumulant_function( else: if pulse is None or S is None or omega is None: - raise ValueError('Require either precomputed cumulant function ' + - 'or pulse, S, and omega as arguments.') + raise ValueError('Require either precomputed cumulant function K' + + ' or pulse, S, and omega as arguments.') n_oper_inds = util.get_indices_from_identifiers(pulse, n_oper_identifiers, diff --git a/filter_functions/pulse_sequence.py b/filter_functions/pulse_sequence.py index 49b976e..797106f 100644 --- a/filter_functions/pulse_sequence.py +++ b/filter_functions/pulse_sequence.py @@ -539,6 +539,8 @@ def get_filter_function(self, omega: Coefficients, which: str = 'fidelity', The filter function for each combination of noise operators as a function of omega. + .. _notes + Notes ----- The generalized filter function is given by From 4f7e02034a5f25ae56b97cc514044e60c2aa1ddd Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 15 Jun 2020 12:12:35 +0200 Subject: [PATCH 09/22] Allow computing error transfer matrix from precomputed cumulant function --- filter_functions/numeric.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 1cbe766..75cf737 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -793,6 +793,9 @@ def error_transfer_matrix( omega: array_like, The frequencies. Note that the frequencies are assumed to be symmetric about zero. + K: ndarray, shape ([[n_pls, n_pls,] n_nops,] n_nops, d**2, d**2) + A precomputed cumulant function. If given, *pulse*, *S*, *omega* + are not required. n_oper_identifiers: array_like, optional The identifiers of the noise operators for which to evaluate the error transfer matrix. The default is all. Note that, since in general @@ -844,11 +847,22 @@ def error_transfer_matrix( calculate_decay_amplitudes: Calculate the :math:`\Gamma_{\alpha\beta,kl}` infidelity: Calculate only infidelity of a pulse. """ - K = calculate_cumulant_function(pulse, S, omega, n_oper_identifiers, - 'total', show_progressbar, - memory_parsimonious) + if K is None: + if pulse is None or S is None or omega is None: + raise ValueError('Require either precomputed cumulant function K' + + ' or pulse, S, and omega as arguments.') + + K = calculate_cumulant_function(pulse, S, omega, n_oper_identifiers, + 'total', show_progressbar, + memory_parsimonious) + + try: + U = sla.expm(K.sum(axis=tuple(range(K.ndim - 2)))) + except AttributeError as aerr: + raise TypeError('K invalid type: {}'.format(type(K))) from aerr + except ValueError as verr: + raise ValueError('K invalid shape: {}'.format(K.shape)) from verr - U = sla.expm(K.sum(axis=tuple(range(K.ndim - 2)))) return U From 6fac17fa08f4a238b6c04c5275401fe07786951b Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Mon, 15 Jun 2020 12:13:18 +0200 Subject: [PATCH 10/22] Update example scripts --- examples/qft.py | 3 +- examples/randomized_benchmarking.py | 87 +++++++++++++++-------------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/examples/qft.py b/examples/qft.py index 6e5ae26..db1ff7f 100644 --- a/examples/qft.py +++ b/examples/qft.py @@ -34,6 +34,7 @@ import filter_functions as ff import qutip as qt from qutip import qip +from qutip.qip.algorithms.qft import qft as qt_qft # %% Define some functions @@ -147,7 +148,7 @@ def QFT_pulse(N: int = 4, tau: float = 1): prop = ff.util.mdot(swaps) @ QFT.total_Q qt.matrix_histogram_complex(prop) print('Correct action: ', - ff.util.oper_equiv(prop, qip.algorithms.qft.qft(N), eps=1e-14)) + ff.util.oper_equiv(prop, qt_qft(N), eps=1e-14)) fig, ax, _ = ff.plot_filter_function(QFT, omega) # Move the legend to the side because of many entries diff --git a/examples/randomized_benchmarking.py b/examples/randomized_benchmarking.py index 774792d..84dd9c5 100644 --- a/examples/randomized_benchmarking.py +++ b/examples/randomized_benchmarking.py @@ -23,6 +23,7 @@ between gates captured in the filter functions. """ import time +from typing import Dict, Sequence import numpy as np import qutip as qt @@ -37,15 +38,15 @@ # %% -def fitfun(m, A, B): - return A*m + B +def fitfun(m, A): + return 1 - A*m def state_infidelity(pulse: PulseSequence, S: ndarray, omega: ndarray, - ind: int = 2) -> float: + ind: int = 3) -> float: """Compute state infidelity for input state eigenstate of pauli *ind*""" R = pulse.get_control_matrix(omega) - F = np.einsum('jko->jo', util.abs2(R[:, np.delete([0, 1, 2], ind)])) + F = np.einsum('jko->jo', util.abs2(R[:, np.delete([0, 1, 2, 3], ind)])) return np.trapz(F*S, omega)/(2*np.pi*pulse.d) @@ -68,8 +69,10 @@ def find_inverse(U: ndarray) -> ndarray: def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, - omega): - infidelities = np.empty((N_l, N_G), dtype=float) + alpha: Sequence[float], + spectra: Dict[float, Sequence[float]], + omega: Sequence[float]): + infidelities = {a: np.empty((N_l, N_G), dtype=float) for a in alpha} lengths = np.round(np.linspace(min_l, max_l, N_l)).astype(int) delta_t = [] t_now = [time.perf_counter()] @@ -85,9 +88,10 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, U = ff.concatenate(cliffords[randints]) U_inv = find_inverse(U.total_Q) pulse_sequence = U @ U_inv - infidelities[l, j] = state_infidelity( - pulse_sequence, S, omega - ).sum() + for k, a in enumerate(alpha): + infidelities[a][l, j] = state_infidelity( + pulse_sequence, spectra[a], omega + ).sum() return infidelities, delta_t @@ -97,17 +101,17 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, H_n = {} H_c = {} dt = {} -H_c['Id'] = [[qt.sigmax().full(), [0], 'X']] -H_n['Id'] = [[qt.sigmax().full(), [1], 'X'], - [qt.sigmay().full(), [1], 'Y']] +H_c['Id'] = [[qt.sigmax().full()/2, [0], 'X']] +H_n['Id'] = [[qt.sigmax().full()/2, [1], 'X'], + [qt.sigmay().full()/2, [1], 'Y']] dt['Id'] = [T] -H_c['X2'] = [[qt.sigmax().full(), [np.pi/4/T], 'X']] -H_n['X2'] = [[qt.sigmax().full(), [1], 'X'], - [qt.sigmay().full(), [1], 'Y']] +H_c['X2'] = [[qt.sigmax().full()/2, [np.pi/2/T], 'X']] +H_n['X2'] = [[qt.sigmax().full()/2, [1], 'X'], + [qt.sigmay().full()/2, [1], 'Y']] dt['X2'] = [T] -H_c['Y2'] = [[qt.sigmay().full(), [np.pi/4/T], 'Y']] -H_n['Y2'] = [[qt.sigmax().full(), [1], 'X'], - [qt.sigmay().full(), [1], 'Y']] +H_c['Y2'] = [[qt.sigmay().full()/2, [np.pi/2/T], 'Y']] +H_n['Y2'] = [[qt.sigmax().full()/2, [1], 'X'], + [qt.sigmay().full()/2, [1], 'Y']] dt['Y2'] = [T] # %% Set up PulseSequences @@ -134,7 +138,7 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, # %% Construct Clifford group tic = time.perf_counter() cliffords = np.array([ - Id, # Id + Y2 @ Y2 @ Y2 @ Y2, # Id X2 @ X2, # X Y2 @ Y2, # Y Y2 @ Y2 @ X2 @ X2, # Z @@ -166,8 +170,10 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, # %% Run simulation eps0 = 2.7241e-4 -# Scaling factor for the noise so that alpha = 0 and alpha = 0.7 have the same -# power + +alpha = (0.0, 0.7) +# Scaling factor for the noise so that alpha = 0 and alpha = 0.7 give the same +# average clifford fidelity noise_scaling_factor = { 0.0: 0.4415924985735799, 0.7: 1 @@ -176,37 +182,36 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, state_infidelities = {} clifford_infidelities = {} -for i, alpha in enumerate((0.0, 0.7)): - S0 = 1e-13*(2*np.pi*1e-3)**alpha/eps0**2*noise_scaling_factor[alpha] - S = S0/omega**alpha +spectra = {} +for i, a in enumerate(alpha): + S0 = 4e-11*(2*np.pi*1e-3)**a/eps0**2*noise_scaling_factor[a] + S = S0/omega**a + spectra[a], omega_twosided = util.symmetrize_spectrum(S, omega) # Need to calculate with two-sided spectra - clifford_infidelities[alpha] = [ - ff.infidelity(C, *util.symmetrize_spectrum(S, omega)).sum() + clifford_infidelities[a] = [ + ff.infidelity(C, spectra[a], omega_twosided).sum() for C in cliffords ] - print('=============================================') - print('\t\talpha = {}'.format(alpha)) - print('=============================================') - state_infidelities[alpha], exec_times = run_randomized_benchmarking( - N_G, N_l, m_min, m_max, omega - ) +state_infidelities, exec_times = run_randomized_benchmarking( + N_G, N_l, m_min, m_max, alpha, spectra, omega_twosided +) # %% Plot results fig, ax = plt.subplots(1, 2, sharex=True, sharey=True, figsize=(8, 3)) -fidelities = {alpha: 1 - infid for alpha, infid in state_infidelities.items()} -for i, alpha in enumerate((0.0, 0.7)): +fidelities = {a: 1 - infid for a, infid in state_infidelities.items()} +for i, a in enumerate(alpha): - means = np.mean(fidelities[alpha], axis=1) - stds = np.std(fidelities[alpha], axis=1) + means = np.mean(fidelities[a], axis=1) + stds = np.std(fidelities[a], axis=1) - popt, pcov = optimize.curve_fit(fitfun, lengths, means, [0, 1], stds, + popt, pcov = optimize.curve_fit(fitfun, lengths, means, [0], stds, absolute_sigma=True) for j in range(N_G): - fid = ax[i].plot(lengths, fidelities[alpha][:, j], 'k.', alpha=0.1, + fid = ax[i].plot(lengths, fidelities[a][:, j], 'k.', alpha=0.1, zorder=2) mean = ax[i].errorbar(lengths, means, stds, fmt='.', zorder=3, @@ -217,16 +222,16 @@ def run_randomized_benchmarking(N_G: int, N_l: int, min_l: int, max_l: int, # sequence length and r = 1 - F_avg = d/(d + 1)*(1 - F_ent) the average # error per gate exp = ax[i].plot(lengths, - 1 - np.mean(clifford_infidelities[alpha])*lengths*2/3, + 1 - np.mean(clifford_infidelities[a])*lengths*2/3, '--', zorder=4, color='tab:blue') - ax[i].set_title(r'$\alpha = {}$'.format(alpha)) + ax[i].set_title(r'$\alpha = {}$'.format(a)) ax[i].set_xlabel(r'Sequence length $m$') handles = [fid[0], mean[0], fit[0], exp[0]] labels = ['State Fidelity', 'Fidelity mean', 'Fit', 'RB theory w/o pulse correlations'] ax[0].set_xlim(0, max(lengths)) -ax[0].set_ylim(.993, 1) +# ax[0].set_ylim(.9, 1) ax[0].set_ylabel(r'Surival Probability') ax[0].legend(frameon=False, handles=handles, labels=labels) From df99d492e2ae75b273da4492ce27571365efbe33 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 10:31:08 +0200 Subject: [PATCH 11/22] Use tensordot instead of einsum in basis.expand Also fix rand_unit for 1d --- filter_functions/basis.py | 2 +- tests/testutil.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/filter_functions/basis.py b/filter_functions/basis.py index cdcad38..5d7488a 100644 --- a/filter_functions/basis.py +++ b/filter_functions/basis.py @@ -619,7 +619,7 @@ def expand(M: Union[ndarray, Basis], basis: Union[ndarray, Basis], {\mathrm{tr}\big(C_j^\dagger C_j\big)}. """ - coefficients = np.einsum('...ij,bji->...b', np.asarray(M), basis) + coefficients = np.tensordot(M, basis, axes=[(-2, -1), (-1, -2)]) if not normalized: coefficients /= np.einsum('bij,bji->b', basis, basis).real diff --git a/tests/testutil.py b/tests/testutil.py index d66a043..f2ca40a 100644 --- a/tests/testutil.py +++ b/tests/testutil.py @@ -146,10 +146,7 @@ def rand_herm_traceless(d: int, n: int = 1) -> np.ndarray: def rand_unit(d: int, n: int = 1) -> np.ndarray: """n random unitary matrices of dimension d""" H = rand_herm(d, n) - if n == 1: - return sla.expm(1j*H) - else: - return np.array([sla.expm(1j*h) for h in H]) + return np.array([sla.expm(1j*h) for h in H]) def rand_pulse_sequence(d: int, n_dt: int, n_cops: int = 3, n_nops: int = 3, From 4bc415ab955afa020d7b8af402f3ceca94a62fd4 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 16:47:50 +0200 Subject: [PATCH 12/22] Fix bug for pulse correlation decay amps --- filter_functions/numeric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 75cf737..19d24d8 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -574,7 +574,7 @@ def calculate_decay_amplitudes( 'requested but omega not equal to ' + 'cached frequencies.') - R = pulse.get_pulse_correlation_control_matrix() + R = pulse.get_pulse_correlation_control_matrix()[:, idx] if not memory_parsimonious: integrand = _get_integrand(S, omega, idx, which, 'generalized', R=R) From 706c802e46a338358674554a1fdbc3fa48873acb Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 16:49:23 +0200 Subject: [PATCH 13/22] Add test for which='total'/'correlations' argument --- tests/test_core.py | 3 +-- tests/test_sequencing.py | 57 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index fb59e5e..80a2e20 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -586,8 +586,7 @@ def test_filter_function(self): basis=total_pulse.basis, n_opers=n_opers, n_coeffs=n_coeffs, - dt=total_pulse.dt, - t=total_pulse.t + dt=total_pulse.dt ) self.assertArrayAlmostEqual(R, R_from_scratch) # first column (identity element) always zero but susceptible to diff --git a/tests/test_sequencing.py b/tests/test_sequencing.py index a0d7a95..63bc1dc 100644 --- a/tests/test_sequencing.py +++ b/tests/test_sequencing.py @@ -30,7 +30,7 @@ import numpy as np import filter_functions as ff -from filter_functions import pulse_sequence, util +from filter_functions import numeric, pulse_sequence, util from tests import testutil @@ -516,6 +516,61 @@ def test_concatenation_periodic(self): self.assertArrayAlmostEqual(F_LAB, F_CC, atol=1e-13) self.assertArrayAlmostEqual(F_LAB, F_CC_PERIODIC, atol=1e-13) + def test_pulse_correlations(self): + """Test calculating pulse correlation quantities.""" + for d, n_dt in zip(testutil.rng.randint(2, 9, 11), + testutil.rng.randint(1, 11, 11)): + pulses = [testutil.rand_pulse_sequence(d, n_dt, 1, 2) + for _ in range(testutil.rng.randint(2, 7))] + for pulse in pulses[1:]: + # Otherwise cannot concatenate + pulse.n_opers = pulses[0].n_opers + pulse.n_oper_identifiers = pulses[0].n_oper_identifiers + + omega = util.get_sample_frequencies(pulse, n_samples=51) + pulse = ff.concatenate(pulses, calc_pulse_correlation_ff=True, + omega=omega) + + spectra = [ + 1e-6/abs(omega), + 1e-6/np.power.outer(abs(omega), np.arange(2)).T, + np.array([[1e-6/abs(omega)**0.7, + 1e-6/(1 + omega**2) + 1j*1e-6*omega], + [1e-6/(1 + omega**2) - 1j*1e-6*omega, + 1e-6/abs(omega)**0.7]]) + ] + + idx = testutil.rng.choice(np.arange(2), testutil.rng.randint(1, 3), + replace=False) + identifiers = pulse.n_oper_identifiers[idx] + + funcs = [numeric.infidelity, + numeric.calculate_decay_amplitudes, + numeric.calculate_cumulant_function] + + for i, spectrum in enumerate(spectra): + if i == 0: + S = spectrum + elif i == 1: + S = spectrum[idx] + elif i == 3: + S = spectrum[idx[None, :], idx] + + for func in funcs: + with self.assertRaises(util.CalculationError): + func(ff.concatenate(pulses), S, omega, + which='correlations') + + with self.assertRaises(ValueError): + func(pulse, S, omega + 1, which='correlations') + + correl = func(pulse, S, omega, identifiers, + which='correlations') + total = func(pulse, S, omega, identifiers, + which='total') + + self.assertArrayAlmostEqual(correl.sum((0, 1)), total, + atol=1e-14) class ExtensionTest(testutil.TestCase): From 1d69b580f08a8bf0d1399f63bad10088610e4a50 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 17:26:32 +0200 Subject: [PATCH 14/22] Test Exceptions in error_transfer_matrix --- tests/test_core.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 80a2e20..a96288d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -783,17 +783,29 @@ def test_pulse_correlation_filter_function(self): self.assertArrayAlmostEqual(infid_1, infid_2.sum(axis=(0, 1))) def test_calculate_decay_amplitudes(self): - """Test raises of numeric.error_transfer_matrix""" + """Test raises of numeric.calculate_decay_amplitudes""" pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) - omega = testutil.rng.randn(43) - # single spectrum S = testutil.rng.randn(78) for i in range(4): with self.assertRaises(ValueError): numeric.calculate_decay_amplitudes(pulse, np.tile(S, [1]*i), omega) + def test_error_transfer_matrix(self): + """Test raises of numeric.error_transfer_matrix.""" + pulse = testutil.rand_pulse_sequence(2, 1, 1, 1) + omega = testutil.rng.randn(43) + S = np.ones_like(omega) + with self.assertRaises(ValueError): + ff.error_transfer_matrix(pulse, S) + + with self.assertRaises(TypeError): + ff.error_transfer_matrix(K=[1, 2, 3]) + + with self.assertRaises(ValueError): + ff.error_transfer_matrix(K=testutil.rng.randn(2, 3, 4)) + def test_infidelity_convergence(self): import matplotlib matplotlib.use('Agg') From 94f6a81a9d71417b2a9bbe0b30b41ce7ab968a49 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 17:28:16 +0200 Subject: [PATCH 15/22] Test calculation of error transfer matrix from K --- tests/test_precision.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_precision.py b/tests/test_precision.py index 233d572..3e3782a 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -404,6 +404,7 @@ def test_single_qubit_error_transfer_matrix(self): np.einsum('...kl,kilj->...ij', Gamma, traces) + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 U_onfoot = sla.expm(K.sum(0)) + U_from_K = ff.error_transfer_matrix(K=K) I_fidelity = ff.infidelity(pulse, S, omega) I_decayamps = -np.einsum('...ii', K)/d**2 I_transfer = 1 - np.einsum('...ii', U)/d**2 @@ -412,6 +413,7 @@ def test_single_qubit_error_transfer_matrix(self): self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) + self.assertArrayAlmostEqual(U_from_K, U_onfoot) # Different spectra for each noise oper S = np.outer(1e-6*np.arange(1, 3), 400/(omega**2 + 400)) @@ -425,6 +427,7 @@ def test_single_qubit_error_transfer_matrix(self): np.einsum('...kl,kilj->...ij', Gamma, traces) + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 U_onfoot = sla.expm(K.sum(0)) + U_from_K = ff.error_transfer_matrix(K=K) I_fidelity = ff.infidelity(pulse, S, omega) I_decayamps = -np.einsum('...ii', K)/d**2 I_transfer = 1 - np.einsum('...ii', U)/d**2 @@ -433,6 +436,7 @@ def test_single_qubit_error_transfer_matrix(self): self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) + self.assertArrayAlmostEqual(U_from_K, U_onfoot) # Cross-correlated spectra are complex, real part symmetric and # imaginary part antisymmetric @@ -448,6 +452,7 @@ def test_single_qubit_error_transfer_matrix(self): np.einsum('...kl,kilj->...ij', Gamma, traces) + np.einsum('...kl,kijl->...ij', Gamma, traces))/2 U_onfoot = sla.expm(K.sum((0, 1))) + U_from_K = ff.error_transfer_matrix(K=K) I_fidelity = ff.infidelity(pulse, S, omega) I_decayamps = -np.einsum('...ii', K)/d**2 I_transfer = 1 - np.einsum('...ii', U)/d**2 @@ -456,6 +461,7 @@ def test_single_qubit_error_transfer_matrix(self): self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), rtol=1e-4) self.assertArrayAlmostEqual(U, U_onfoot, atol=1e-14) + self.assertArrayAlmostEqual(U_from_K, U_onfoot) def test_multi_qubit_error_transfer_matrix(self): """Test the calculation of the multi-qubit transfer matrix""" From 42a4d63d1550801c7d3051c97378eee6ae3cb5ac Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 17:39:11 +0200 Subject: [PATCH 16/22] Use FF to calculate integrand if available This removes unnecessary double calculations --- filter_functions/numeric.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 19d24d8..5d9f44a 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -565,25 +565,40 @@ def calculate_decay_amplitudes( # Noise operator indices idx = util.get_indices_from_identifiers(pulse, n_oper_identifiers, 'noise') if which == 'total': - R = pulse.get_control_matrix(omega, show_progressbar)[idx] + # Faster to use filter function instead of control matrix + if pulse.is_cached('F_kl'): + R = None + F = pulse.get_filter_function( + omega, which='generalized' + )[idx[None, :], idx] + else: + R = pulse.get_control_matrix(omega, show_progressbar)[idx] + F = None elif which == 'correlations': - # Check if cached frequencies coincide with given if pulse.is_cached('omega'): if not np.array_equal(pulse.omega, omega): raise ValueError('Pulse correlation decay amplitudes ' + 'requested but omega not equal to ' + 'cached frequencies.') - R = pulse.get_pulse_correlation_control_matrix()[:, idx] + if pulse.is_cached('F_pc_kl'): + R = None + F = pulse.get_pulse_correlation_filter_function( + omega, which='generalized' + )[:, :, idx[None, :], idx] + else: + R = pulse.get_pulse_correlation_control_matrix()[:, idx] + F = None if not memory_parsimonious: - integrand = _get_integrand(S, omega, idx, which, 'generalized', R=R) + integrand = _get_integrand(S, omega, idx, which, 'generalized', R=R, + F=F) Gamma = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) return Gamma.real # Conserve memory by looping. Let _get_integrand determine the shape integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., 0:1, :], R]) + R=[R[..., 0:1, :], R], F=F) n_kl = R.shape[1] Gamma = np.zeros(integrand.shape[:-3] + (n_kl,)*2, @@ -593,7 +608,7 @@ def calculate_decay_amplitudes( for k in util.progressbar_range(1, n_kl, show_progressbar=show_progressbar, desc='Integrating'): integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., k:k+1, :], R]) + R=[R[..., k:k+1, :], R], F=F) Gamma[..., k:k+1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) From c469b760b812eced9b52545c7bb15f314f1916dd Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 17:51:28 +0200 Subject: [PATCH 17/22] Update readme with new is_cached aliases diff --git a/README.md b/README.md index bba48f6..24c15d6 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Y2.cache_filter_function(omega) X.cache_filter_function(omega) hadamard = Y2 @ X # equivalent: ff.concatenate((Y2, X)) -hadamard.is_cached('F') +hadamard.is_cached('filter function') # True (filter function cached during concatenation) ``` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bba48f6..24c15d6 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Y2.cache_filter_function(omega) X.cache_filter_function(omega) hadamard = Y2 @ X # equivalent: ff.concatenate((Y2, X)) -hadamard.is_cached('F') +hadamard.is_cached('filter function') # True (filter function cached during concatenation) ``` From 7381d846d3ae98cb67aef284c13a39741a9c6c07 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Thu, 18 Jun 2020 18:51:13 +0200 Subject: [PATCH 18/22] Fix integration --- filter_functions/numeric.py | 39 ++++++++++++++++++++++++------------- tests/test_sequencing.py | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 5d9f44a..340e4b4 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -568,11 +568,9 @@ def calculate_decay_amplitudes( # Faster to use filter function instead of control matrix if pulse.is_cached('F_kl'): R = None - F = pulse.get_filter_function( - omega, which='generalized' - )[idx[None, :], idx] + F = pulse.get_filter_function(omega, which='generalized') else: - R = pulse.get_control_matrix(omega, show_progressbar)[idx] + R = pulse.get_control_matrix(omega, show_progressbar) F = None elif which == 'correlations': if pulse.is_cached('omega'): @@ -584,10 +582,9 @@ def calculate_decay_amplitudes( if pulse.is_cached('F_pc_kl'): R = None F = pulse.get_pulse_correlation_filter_function( - omega, which='generalized' - )[:, :, idx[None, :], idx] + which='generalized') else: - R = pulse.get_pulse_correlation_control_matrix()[:, idx] + R = pulse.get_pulse_correlation_control_matrix() F = None if not memory_parsimonious: @@ -597,18 +594,28 @@ def calculate_decay_amplitudes( return Gamma.real # Conserve memory by looping. Let _get_integrand determine the shape - integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., 0:1, :], R], F=F) + if R is not None: + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=[R[..., 0:1, :], R], F=F) + n_kl = R.shape[-2] + elif F is not None: + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=R, F=F[..., 0:1, :, :]) + n_kl = F.shape[-2] - n_kl = R.shape[1] Gamma = np.zeros(integrand.shape[:-3] + (n_kl,)*2, dtype=integrand.dtype) Gamma[..., 0:1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) for k in util.progressbar_range(1, n_kl, show_progressbar=show_progressbar, desc='Integrating'): - integrand = _get_integrand(S, omega, idx, which, 'generalized', - R=[R[..., k:k+1, :], R], F=F) + if R is not None: + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=[R[..., k:k+1, :], R], F=F) + elif F is not None: + integrand = _get_integrand(S, omega, idx, which, 'generalized', + R=R, F=F[..., k:k+1, :, :]) + Gamma[..., k:k+1, :] = integrate.trapz(integrand, omega, axis=-1)/(2*np.pi) @@ -1254,7 +1261,9 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, elif which_pulse == 'total': einsum_str = 'ako,ao,alo->aklo' - integrand = np.einsum(einsum_str, R_left, S, R_right).real + integrand = np.einsum(einsum_str, + R_left[..., idx, :, :], S, + R_right[..., idx, :, :]).real elif S.ndim == 3: # General case where S is a matrix with correlation spectra on off-diag shape = (len(idx), len(idx), len(omega)) @@ -1272,7 +1281,9 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, elif which_pulse == 'total': einsum_str = 'ako,abo,blo->abklo' - integrand = np.einsum(einsum_str, R_left, S, R_right) + integrand = np.einsum(einsum_str, + R_left[..., idx, :, :], S, + R_right[..., idx, :, :]) else: raise ValueError('Expected S to be array_like with < 4 dimensions') diff --git a/tests/test_sequencing.py b/tests/test_sequencing.py index 63bc1dc..5af671c 100644 --- a/tests/test_sequencing.py +++ b/tests/test_sequencing.py @@ -529,7 +529,7 @@ def test_pulse_correlations(self): omega = util.get_sample_frequencies(pulse, n_samples=51) pulse = ff.concatenate(pulses, calc_pulse_correlation_ff=True, - omega=omega) + omega=omega, which='generalized') spectra = [ 1e-6/abs(omega), From c3d40767fcc36f630072daf162a39b0fc11fce9a Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Fri, 19 Jun 2020 13:41:28 +0200 Subject: [PATCH 19/22] Add test for numeric._get_integrand() --- filter_functions/numeric.py | 20 +++- filter_functions/pulse_sequence.py | 8 +- tests/test_precision.py | 147 ++++++++++++++++++++++++----- 3 files changed, 144 insertions(+), 31 deletions(-) diff --git a/filter_functions/numeric.py b/filter_functions/numeric.py index 340e4b4..e91d4be 100644 --- a/filter_functions/numeric.py +++ b/filter_functions/numeric.py @@ -1257,9 +1257,15 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, integrand = np.moveaxis(integrand, source=-2, destination=-4) elif R is not None: if which_pulse == 'correlations': - einsum_str = 'gako,ao,halo->ghaklo' + if which_FF == 'fidelity': + einsum_str = 'gako,ao,hako->ghao' + elif which_FF == 'generalized': + einsum_str = 'gako,ao,halo->ghaklo' elif which_pulse == 'total': - einsum_str = 'ako,ao,alo->aklo' + if which_FF == 'fidelity': + einsum_str = 'ako,ao,ako->ao' + elif which_FF == 'generalized': + einsum_str = 'ako,ao,alo->aklo' integrand = np.einsum(einsum_str, R_left[..., idx, :, :], S, @@ -1277,9 +1283,15 @@ def _get_integrand(S: ndarray, omega: ndarray, idx: ndarray, which_pulse: str, destination=[-5, -4]) elif R is not None: if which_pulse == 'correlations': - einsum_str = 'gako,abo,hblo->ghabklo' + if which_FF == 'fidelity': + einsum_str = 'gako,abo,hbko->ghabo' + elif which_FF == 'generalized': + einsum_str = 'gako,abo,hblo->ghabklo' elif which_pulse == 'total': - einsum_str = 'ako,abo,blo->abklo' + if which_FF == 'fidelity': + einsum_str = 'ako,abo,bko->abo' + elif which_FF == 'generalized': + einsum_str = 'ako,abo,blo->abklo' integrand = np.einsum(einsum_str, R_left[..., idx, :, :], S, diff --git a/filter_functions/pulse_sequence.py b/filter_functions/pulse_sequence.py index 797106f..31464e2 100644 --- a/filter_functions/pulse_sequence.py +++ b/filter_functions/pulse_sequence.py @@ -1533,12 +1533,12 @@ def concatenate(pulses: Iterable[PulseSequence], if not equal_omega: if calc_filter_function: raise ValueError("Calculation of filter function forced " + - "but not all pulses have the same " + - "frequencies cached and none were supplied!") + "but not all pulses have the same " + + "frequencies cached and none were supplied!") if calc_pulse_correlation_ff: raise ValueError("Cannot compute the pulse correlation " + - "filter functions; do not have the " + - "frequencies at which to evaluate.") + "filter functions; do not have the " + + "frequencies at which to evaluate.") return newpulse diff --git a/tests/test_precision.py b/tests/test_precision.py index 3e3782a..ad0e38d 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -27,7 +27,7 @@ import qutip as qt import filter_functions as ff -from filter_functions import analytic, numeric +from filter_functions import analytic, numeric, util from tests import testutil @@ -36,11 +36,11 @@ class PrecisionTest(testutil.TestCase): def test_FID(self): """FID""" tau = abs(testutil.rng.randn()) - FID_pulse = ff.PulseSequence([[ff.util.P_np[1]/2, [0]]], - [[ff.util.P_np[3]/2, [1]]], + FID_pulse = ff.PulseSequence([[util.P_np[1]/2, [0]]], + [[util.P_np[3]/2, [1]]], [tau]) - omega = ff.util.get_sample_frequencies(FID_pulse, 50, spacing='linear') + 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 @@ -55,10 +55,10 @@ def test_SE(self): H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cpmg') - H_n = [[ff.util.P_np[3]/2, np.ones_like(dt)]] + H_n = [[util.P_np[3]/2, np.ones_like(dt)]] SE_pulse = ff.PulseSequence(H_c, H_n, dt) - omega = ff.util.get_sample_frequencies(SE_pulse, 100, spacing='linear') + 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 @@ -67,7 +67,7 @@ def test_SE(self): # Test again with a factor of one between the noise operators and # coefficients r = testutil.rng.randn() - H_n = [[ff.util.P_np[3]/2*r, np.ones_like(dt)/r]] + H_n = [[util.P_np[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 @@ -84,10 +84,10 @@ def test_6_pulse_CPMG(self): H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cpmg') - H_n = [[ff.util.P_np[3]/2, np.ones_like(dt)]] + H_n = [[util.P_np[3]/2, np.ones_like(dt)]] CPMG_pulse = ff.PulseSequence(H_c, H_n, dt) - omega = ff.util.get_sample_frequencies(CPMG_pulse, 100, spacing='log') + omega = util.get_sample_frequencies(CPMG_pulse, 100, spacing='log') # Comparison to filter function defined with omega**2 F = CPMG_pulse.get_filter_function(omega)[0, 0]*omega**2 @@ -104,7 +104,7 @@ def test_6_pulse_UDD(self): H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='udd') - H_n = [[ff.util.P_np[3]/2, np.ones_like(dt)]] + H_n = [[util.P_np[3]/2, np.ones_like(dt)]] UDD_pulse = ff.PulseSequence(H_c, H_n, dt) # Comparison to filter function defined with omega**2 @@ -123,7 +123,7 @@ def test_6_pulse_PDD(self): H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='pdd') - H_n = [[ff.util.P_np[3]/2, np.ones_like(dt)]] + H_n = [[util.P_np[3]/2, np.ones_like(dt)]] PDD_pulse = ff.PulseSequence(H_c, H_n, dt) # Comparison to filter function defined with omega**2 @@ -142,7 +142,7 @@ def test_5_pulse_CDD(self): H_c, dt = testutil.generate_dd_hamiltonian(n, tau=tau, tau_pi=tau_pi, dd_type='cdd') - H_n = [[ff.util.P_np[3]/2, np.ones_like(dt)]] + H_n = [[util.P_np[3]/2, np.ones_like(dt)]] CDD_pulse = ff.PulseSequence(H_c, H_n, dt) # Comparison to filter function defined with omega**2 @@ -179,8 +179,8 @@ def test_liouville_representation(self): ) if d == 2: - U_liouville = numeric.liouville_representation( - ff.util.P_np[1:], basis) + U_liouville = numeric.liouville_representation(util.P_np[1:], + basis) self.assertArrayAlmostEqual(U_liouville[0], np.diag([1, 1, -1, -1]), atol=np.finfo(float).eps) @@ -212,13 +212,13 @@ def test_diagonalization_cnot(self): cnot.diagonalize() cnot_subspace.diagonalize() - phase_eq = ff.util.oper_equiv(cnot_subspace.total_Q[1:5, 1:5], - qt.cnot(), eps=1e-9) + phase_eq = util.oper_equiv(cnot_subspace.total_Q[1:5, 1:5], qt.cnot(), + eps=1e-9) self.assertTrue(phase_eq[0]) - phase_eq = ff.util.oper_equiv( - cnot.total_Q[np.ix_(*subspace)][1:5, 1:5], qt.cnot(), eps=1e-9) + phase_eq = util.oper_equiv(cnot.total_Q[np.ix_(*subspace)][1:5, 1:5], + qt.cnot(), eps=1e-9) self.assertTrue(phase_eq[0]) @@ -255,7 +255,7 @@ def test_infidelity_cnot(self): 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) + S_t, omega_t = util.symmetrize_spectrum(A/omega**alpha, omega) infid, xi = ff.infidelity(cnot, S_t, omega_t, identifiers[:3], return_smallness=True) @@ -270,6 +270,108 @@ def test_infidelity_cnot(self): self.assertLessEqual(np.abs(1 - (infid_P.sum()/MC)), rtol) self.assertLessEqual(infid.sum(), xi**2/4) + def test_integration(self): + """Test the private function used to set up the integrand.""" + pulses = [testutil.rand_pulse_sequence(3, 1, 2, 3), + testutil.rand_pulse_sequence(3, 1, 2, 3)] + pulses[1].n_opers = pulses[0].n_opers + pulses[1].n_oper_identifiers = pulses[0].n_oper_identifiers + + omega = np.linspace(-1, 1, 50) + spectra = [ + 1e-6/abs(omega), + 1e-6/np.power.outer(abs(omega), np.arange(2)).T, + np.array([[1e-6/abs(omega)**0.7, + 1e-6/(1 + omega**2) + 1j*1e-6*omega], + [1e-6/(1 + omega**2) - 1j*1e-6*omega, + 1e-6/abs(omega)**0.7]]) + ] + + pulse = ff.concatenate(pulses, omega=omega, + calc_pulse_correlation_ff=True) + + idx = testutil.rng.choice(np.arange(2), testutil.rng.randint(1, 3), + replace=False) + + R = pulse.get_control_matrix(omega) + R_pc = pulse.get_pulse_correlation_control_matrix() + F = pulse.get_filter_function(omega) + F_kl = pulse.get_filter_function(omega, 'generalized') + F_pc = pulse.get_pulse_correlation_filter_function() + F_pc_kl = pulse.get_pulse_correlation_filter_function('generalized') + + for i, spectrum in enumerate(spectra): + if i == 0: + S = spectrum + elif i == 1: + S = spectrum[idx] + elif i == 3: + S = spectrum[idx[None, :], idx] + + R_1 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='fidelity', + R=R, F=None) + R_2 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='fidelity', + R=[R, R], F=None) + F_1 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='fidelity', + R=None, F=F) + + self.assertArrayAlmostEqual(R_1, R_2) + self.assertArrayAlmostEqual(R_1, F_1) + + R_1 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='fidelity', + R=R_pc, F=None) + R_2 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='fidelity', + R=[R_pc, R_pc], F=None) + F_1 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='fidelity', + R=None, F=F_pc) + + self.assertArrayAlmostEqual(R_1, R_2) + self.assertArrayAlmostEqual(R_1, F_1) + + R_1 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='generalized', + R=R, F=None) + R_2 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='generalized', + R=[R, R], F=None) + F_1 = numeric._get_integrand(S, omega, idx, + which_pulse='total', + which_FF='generalized', + R=None, F=F_kl) + + self.assertArrayAlmostEqual(R_1, R_2) + self.assertArrayAlmostEqual(R_1, F_1) + + R_1 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='generalized', + R=R_pc, F=None) + R_2 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='generalized', + R=[R_pc, R_pc], F=None) + F_1 = numeric._get_integrand(S, omega, idx, + which_pulse='correlations', + which_FF='generalized', + R=None, F=F_pc_kl) + + self.assertArrayAlmostEqual(R_1, R_2) + self.assertArrayAlmostEqual(R_1, F_1) + def test_infidelity(self): """Benchmark infidelity results against previous version's results""" testutil.rng.seed(123456789) @@ -316,8 +418,7 @@ def test_infidelity(self): omega = np.geomspace(0.1, 10, 51) S0 = np.abs(testutil.rng.randn()) for spec in spectra: - S, omega_t = ff.util.symmetrize_spectrum(spec(S0, omega), - omega) + S, omega_t = 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], @@ -385,7 +486,7 @@ def test_single_qubit_error_transfer_matrix(self): d = 2 for n_dt in testutil.rng.randint(1, 11, 10): pulse = testutil.rand_pulse_sequence(d, n_dt, 3, 2, btype='Pauli') - omega = ff.util.get_sample_frequencies(pulse, n_samples=51) + omega = util.get_sample_frequencies(pulse, n_samples=51) n_oper_identifiers = pulse.n_oper_identifiers traces = pulse.basis.four_element_traces.todense() @@ -473,7 +574,7 @@ def test_multi_qubit_error_transfer_matrix(self): btype = 'Pauli' if f == 0.0 else 'GGM' pulse = testutil.rand_pulse_sequence(d, n_dt, n_cops, n_nops, btype) - omega = ff.util.get_sample_frequencies(pulse, n_samples=51) + omega = util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() S = 1e-8/omega**2 From 85ded2104edf1f7f25bb342e2257ebf9b6364b41 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Fri, 19 Jun 2020 13:54:05 +0200 Subject: [PATCH 20/22] Make fidelity comparison more lenient --- tests/test_precision.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_precision.py b/tests/test_precision.py index ad0e38d..a4697c5 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -577,7 +577,7 @@ def test_multi_qubit_error_transfer_matrix(self): omega = util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() - S = 1e-8/omega**2 + S = 1e-9/omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, @@ -586,9 +586,9 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-4) + rtol=1e-3) - S = np.outer(1e-7*(np.arange(n_nops) + 1), + S = np.outer(1e-8*(np.arange(n_nops) + 1), 400/(omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop @@ -598,10 +598,10 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-4) + rtol=1e-3) - S = np.tile(1e-8/abs(omega)**2, (n_nops, n_nops, 1)).astype( - complex) + S = np.tile(1e-9/abs(omega)**2, (n_nops, n_nops, 1)).astype( + complex) S[np.triu_indices(n_nops, 1)].imag = 1e-10*omega S[np.tril_indices(n_nops, -1)].imag = \ - S[np.triu_indices(n_nops, 1)].imag @@ -613,4 +613,4 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-4) + rtol=1e-3) From 9a88506784ff4c280b2597e3b09f7c66959a6348 Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Fri, 19 Jun 2020 14:29:24 +0200 Subject: [PATCH 21/22] atol instead of rtol --- tests/test_precision.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_precision.py b/tests/test_precision.py index a4697c5..f06364a 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -577,7 +577,7 @@ def test_multi_qubit_error_transfer_matrix(self): omega = util.get_sample_frequencies(pulse, n_samples=51) # Assert fidelity is same as computed by infidelity() - S = 1e-9/omega**2 + S = 1e-8/omega**2 U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop Up = ff.error_transfer_matrix(pulse, S, omega, @@ -586,9 +586,9 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-3) + atol=1e-4) - S = np.outer(1e-8*(np.arange(n_nops) + 1), + S = np.outer(1e-7*(np.arange(n_nops) + 1), 400/(omega**2 + 400)) U = ff.error_transfer_matrix(pulse, S, omega) # Calculate U in loop @@ -598,10 +598,10 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-3) + atol=1e-4) - S = np.tile(1e-9/abs(omega)**2, (n_nops, n_nops, 1)).astype( - complex) + S = np.tile(1e-8/abs(omega)**2, (n_nops, n_nops, 1)).astype( + complex) S[np.triu_indices(n_nops, 1)].imag = 1e-10*omega S[np.tril_indices(n_nops, -1)].imag = \ - S[np.triu_indices(n_nops, 1)].imag @@ -613,4 +613,4 @@ def test_multi_qubit_error_transfer_matrix(self): I_transfer = 1 - np.einsum('...ii', U)/d**2 self.assertArrayAlmostEqual(Up, U) self.assertArrayAlmostEqual(I_transfer, I_fidelity.sum(), - rtol=1e-3) + atol=1e-4) From 2a544995d326e6169c7f931b62b8d9142c7d0a6a Mon Sep 17 00:00:00 2001 From: Tobias Hangleiter Date: Fri, 19 Jun 2020 16:27:57 +0200 Subject: [PATCH 22/22] Test calc_cumulant_function for R=None and F=None --- tests/test_precision.py | 4 +-- tests/test_sequencing.py | 72 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/tests/test_precision.py b/tests/test_precision.py index f06364a..3494409 100644 --- a/tests/test_precision.py +++ b/tests/test_precision.py @@ -305,8 +305,8 @@ def test_integration(self): S = spectrum elif i == 1: S = spectrum[idx] - elif i == 3: - S = spectrum[idx[None, :], idx] + elif i == 2: + S = spectrum[idx[None, :], idx[:, None]] R_1 = numeric._get_integrand(S, omega, idx, which_pulse='total', diff --git a/tests/test_sequencing.py b/tests/test_sequencing.py index 5af671c..bfa1e74 100644 --- a/tests/test_sequencing.py +++ b/tests/test_sequencing.py @@ -518,8 +518,8 @@ def test_concatenation_periodic(self): def test_pulse_correlations(self): """Test calculating pulse correlation quantities.""" - for d, n_dt in zip(testutil.rng.randint(2, 9, 11), - testutil.rng.randint(1, 11, 11)): + for d, n_dt in zip(testutil.rng.randint(2, 7, 11), + testutil.rng.randint(1, 5, 11)): pulses = [testutil.rand_pulse_sequence(d, n_dt, 1, 2) for _ in range(testutil.rng.randint(2, 7))] for pulse in pulses[1:]: @@ -548,13 +548,21 @@ def test_pulse_correlations(self): numeric.calculate_decay_amplitudes, numeric.calculate_cumulant_function] + R = pulse.get_control_matrix(omega) + R_pc = pulse.get_pulse_correlation_control_matrix() + F = pulse.get_filter_function(omega) + F_kl = pulse.get_filter_function(omega, 'generalized') + F_pc = pulse.get_pulse_correlation_filter_function() + F_pc_kl = pulse.get_pulse_correlation_filter_function( + 'generalized') + for i, spectrum in enumerate(spectra): if i == 0: S = spectrum elif i == 1: S = spectrum[idx] - elif i == 3: - S = spectrum[idx[None, :], idx] + elif i == 2: + S = spectrum[idx[None, :], idx[:, None]] for func in funcs: with self.assertRaises(util.CalculationError): @@ -564,14 +572,68 @@ def test_pulse_correlations(self): with self.assertRaises(ValueError): func(pulse, S, omega + 1, which='correlations') + pulse._R = R + pulse._R_pc = R_pc + correl = func(pulse, S, omega, identifiers, + which='correlations') + total = func(pulse, S, omega, identifiers, + which='total') + pulse._R = None + pulse._R_pc = None + + self.assertArrayAlmostEqual(correl.sum((0, 1)), total, + atol=1e-14) + + pulse._F = F + pulse._F_kl = F_kl + pulse._F_pc = F_pc + pulse._F_pc_kl = F_pc_kl correl = func(pulse, S, omega, identifiers, which='correlations') total = func(pulse, S, omega, identifiers, - which='total') + which='total') + pulse._F = None + pulse._F_kl = None + pulse._F_pc = None + pulse._F_pc_kl = None self.assertArrayAlmostEqual(correl.sum((0, 1)), total, atol=1e-14) + if func != numeric.infidelity: + pulse._R = R + pulse._R_pc = R_pc + correl = func(pulse, S, omega, identifiers, + which='correlations', + memory_parsimonious=True) + total = func(pulse, S, omega, identifiers, + which='total', + memory_parsimonious=True) + pulse._R = None + pulse._R_pc = None + + self.assertArrayAlmostEqual(correl.sum((0, 1)), total, + atol=1e-14) + + pulse._F = F + pulse._F_kl = F_kl + pulse._F_pc = F_pc + pulse._F_pc_kl = F_pc_kl + correl = func(pulse, S, omega, identifiers, + which='correlations', + memory_parsimonious=True) + total = func(pulse, S, omega, identifiers, + which='total', + memory_parsimonious=True) + pulse._F = None + pulse._F_kl = None + pulse._F_pc = None + pulse._F_pc_kl = None + + self.assertArrayAlmostEqual(correl.sum((0, 1)), total, + atol=1e-14) + + class ExtensionTest(testutil.TestCase): def test_extend_with_identity(self):