qdk_chemistry.utils.pauli_commutation module

Pauli string commutation utilities.

This module provides reusable functions for checking commutativity of Pauli operators and computing commutator norms.

Label-based functions (do_pauli_labels_*) operate on Pauli string labels such as "XIZI". Map-based functions (do_pauli_maps_*) operate on sparse dict[int, str] qubit-index-to-Pauli-axis mappings.

References

Childs, A. M., et al. “Toward the first quantum simulation with quantum speedup.” Proceedings of the National Academy of Sciences 115.38 (2018): 9456-9461.

Childs, A. M., et al. “Theory of Trotter Error with Commutator Scaling.” Physical Review X 11.1 (2021): 011020. https://arxiv.org/abs/1912.08854

qdk_chemistry.utils.pauli_commutation.commutator_bound_first_order(hamiltonian, weight_threshold=1e-12)[source]

Compute the first-order Trotter commutator bound.

For a Hamiltonian \(H = \sum_j \alpha_j P_j\) the first-order (Lie-Trotter) product formula has error bounded by

\[\lVert U(t) - S_1(t) \rVert \le \frac{t^2}{2} \sum_{j < k} \lVert [\alpha_j P_j,\, \alpha_k P_k] \rVert\]

For Pauli strings the spectral norm of the commutator is

  • 0 if \(P_j\) and \(P_k\) commute, or

  • \(2 |\alpha_j| |\alpha_k|\) if they anticommute.

This function returns \(\sum_{j < k} \lVert [\alpha_j P_j, \alpha_k P_k] \rVert\), so the user can multiply by \(t^{2} / (2N)\) to get the per-step error.

Parameters:
  • hamiltonian (QubitHamiltonian) – The qubit Hamiltonian whose terms to analyse.

  • weight_threshold (float) – Absolute threshold below which coefficients are discarded.

Returns:

The sum of commutator norms over all unique pairs.

Return type:

float

qdk_chemistry.utils.pauli_commutation.commutator_bound_higher_order(hamiltonian, order, weight_threshold=1e-12)[source]

Compute the commutator bound term \(\alpha\) for arbitrary-order Trotter errors.

Return type:

float

Parameters:
  • hamiltonian (QubitHamiltonian) – The qubit Hamiltonian for which to compute the bound.

  • order (int) – The order of the Trotter decomposition.

  • weight_threshold (float) – Absolute threshold for filtering small Hamiltonian coefficients.

Returns:

The commutator bound term \(\alpha\) multiplying \(t^{order+1}\) in Theorem 6 of Childs et. al (2021).

qdk_chemistry.utils.pauli_commutation.commutator_bound_second_order(hamiltonian, weight_threshold=1e-12)[source]

Compute the commutator bound term multiplying \(t^{3} / 12\) in Proposition 10 in Childs et. al (2021).

Return type:

float

Parameters:
  • hamiltonian (QubitHamiltonian) – The qubit Hamiltonian for which to compute the bound.

  • weight_threshold (float) – Absolute threshold for filtering small Hamiltonian coefficients.

Returns:

The commutator bound term multiplying \(t^{3} / 12\).

qdk_chemistry.utils.pauli_commutation.do_pauli_labels_commute(label_a, label_b)[source]

Check whether two Pauli strings commute.

Two multi-qubit Pauli strings \(P_a\) and \(P_b\) commute if and only if the number of qubit positions where the two single-qubit Pauli operators are both non-identity and different from each other is even.

The labels use the Qiskit / SparsePauliOp convention: the rightmost character corresponds to qubit 0 (little-endian bit ordering).

Return type:

bool

Parameters:
  • label_a (str) – Pauli string label (e.g. "XIZI").

  • label_b (str) – Pauli string label of the same length (e.g. "YZXI").

Returns:

True if the two Pauli strings commute, False otherwise.

Raises:

ValueError – If the labels have different lengths.

Examples

>>> do_pauli_labels_commute("XI", "IX")
True
>>> do_pauli_labels_commute("XX", "YY")
True
>>> do_pauli_labels_commute("XY", "YX")
True
>>> do_pauli_labels_commute("XI", "YI")
False
qdk_chemistry.utils.pauli_commutation.do_pauli_labels_qw_commute(label_a, label_b)[source]

Check whether two Pauli strings qubit-wise commute.

Two multi-qubit Pauli operators qubit-wise commute when every corresponding single-qubit pair commutes individually. This is strictly stronger than general commutativity: qubit-wise commuting operators always commute, but the converse is not true (e.g. "XY" and "YX" commute globally but do not qubit-wise commute).

The operators qubit-wise commute if and only if there are no qubit positions where both are non-identity and different.

Return type:

bool

Parameters:
  • label_a (str) – Pauli string label (e.g. "XIZI").

  • label_b (str) – Pauli string label of the same length.

Returns:

True if the terms qubit-wise commute.

Raises:

ValueError – If the labels have different lengths.

Examples

>>> do_pauli_labels_qw_commute("XI", "IX")
True
>>> do_pauli_labels_qw_commute("XI", "YI")
False
>>> do_pauli_labels_qw_commute("XY", "YX")   # commute, but NOT qw-commute
False
qdk_chemistry.utils.pauli_commutation.do_pauli_maps_commute(a, b)[source]

Check whether two Pauli terms commute (general/standard commutation).

Two multi-qubit Pauli operators commute if and only if the number of qubit positions where both are non-identity and different is even. This is weaker than qubit-wise commutation and allows larger merge groups.

Return type:

bool

Parameters:
  • a (dict[int, str]) – First Pauli term mapping (qubit index → Pauli axis).

  • b (dict[int, str]) – Second Pauli term mapping (qubit index → Pauli axis).

Returns:

True if the terms commute.

qdk_chemistry.utils.pauli_commutation.do_pauli_maps_qw_commute(a, b)[source]

Check whether two Pauli terms qubit-wise commute.

Two multi-qubit Pauli operators qubit-wise commute when every corresponding single-qubit pair commutes individually. This is strictly stronger than general commutativity: qubit-wise commuting operators always commute, but the converse is not true (e.g. {0: 'X', 1: 'Y'} and {0: 'Y', 1: 'X'} commute globally but do not qubit-wise commute).

The operators qubit-wise commute if and only if there are no qubit positions where both are non-identity and different.

Return type:

bool

Parameters:
  • a (dict[int, str]) – First Pauli term mapping (qubit index → Pauli axis).

  • b (dict[int, str]) – Second Pauli term mapping (qubit index → Pauli axis).

Returns:

True if the terms qubit-wise commute.

qdk_chemistry.utils.pauli_commutation.does_nested_commutator_vanish(*labels)[source]

Check whether a nested commutator of Pauli labels vanishes.

Return type:

bool

Parameters:

labels (str) – A sequence of Pauli labels representing the nested commutator.

Returns:

True if the nested commutator vanishes, False otherwise.

Raises:

ValueError – If fewer than two Pauli labels are provided.

qdk_chemistry.utils.pauli_commutation.get_commutation_checker(commutation_type)[source]

Return the commutation checker function for the given type.

Return type:

Callable[[dict[int, str], dict[int, str]], bool]

Parameters:

commutation_type (str) – "qubit_wise" or "general".

Returns:

A callable (a, b) -> bool that checks commutation of two Pauli term mappings.

Raises:

ValueError – If commutation_type is not recognised.