Skip to content

Improve commutation checking of Pauli product rotations and measurements#15815

Open
alexanderivrii wants to merge 5 commits intoQiskit:mainfrom
alexanderivrii:opt_ppr_ppm_in_cc
Open

Improve commutation checking of Pauli product rotations and measurements#15815
alexanderivrii wants to merge 5 commits intoQiskit:mainfrom
alexanderivrii:opt_ppr_ppm_in_cc

Conversation

@alexanderivrii
Copy link
Member

Summary

This PR improves commutation checking of pairs of Pauli-based objects, that is of PauliProductRotationGate and PauliProductMeasurement. Without this PR, we construct the generators for operations for PPRs and PPMs as SparseObservables and then check if the two SparseObservables commute. With this PR, we first instead construct the generatoors as Paulis (represented using Z and X components) and check if two Paulis commute. The latter check is quite a bit faster than the former for large gates.

Based on top of #15810.

Details and comments

I run this on the 100 representative HamLib benchmarks from benchpress using the following script

ham_records = json.load(open("./100_representative.json", "r"))

for i, h in enumerate(ham_records):
    nq = h.pop("ham_qubits")
    terms = h.pop("ham_hamlib_hamiltonian_terms")
    coefficients = h.pop("ham_hamlib_hamiltonian_coefficients")

    # Construct circuit from PPRs
    qc = QuantumCircuit(nq)
    for t, c in zip(terms, coefficients):
        ppr = PauliProductRotationGate(Pauli(t), c)
        qc.append(ppr, range(nq))

    # Convert to DAG
    dag = circuit_to_dag(qc)

    # Run commutative optimization on DAG, measuring the time
    time_start = time.perf_counter()
    dagt = CommutativeOptimization().run(dag)
    time_end = time.perf_counter()
    print(f"Test {i}: {time_end-time_start}")

Here is the plot showing improvement in runtime (where commutation checker is used within CommutativeOptimization) .

imrovement

Note that CommutativeOptimization does not actually improve the quality of any of these HamLib benchmarks, however it tends to make a huge number of commutativity checks.

Here is an additional experiment is in the spirit of our FT compiler pipeline (in which case CommutativeOptimization can remove/merge many gates).

qc = qft_circuit(1000)
qc = transpile(qc, basis_gates=get_clifford_gate_names()+["rz"])
qc = LitinskiTransformation(fix_clifford=False, use_ppr=True)(qc)
time_start = time.perf_counter()
qc = CommutativeOptimization()(qc)
time_end = time.perf_counter()
print(f"Time: {time_end-time_start:.4f}")

Without this PR, the average time for CommutativeOptimization is 3.46 seconds, with this PR it's 0.29.

LLM tools used

Used copilot to suggest various micro-optimization and alternative implementations, but in the end the implementation and all possible bugs are purely mine.

Additional potential optimization

If we could assume that the qargs for PauliProductRotationGate and PauliProductMeasurement gate are always sorted by qubit index, we could optimize the implementation even more by replacing in_q1 by the standard intersection method for two sorted vectors. In theory, we could add an extra pass that sorts PPR and PPM instructions in this way.

For pairs of PPRs/PPMs we can construct generators as Paulis rather
than SparseObservables, and we can check commutativity by checking
the commutativity of Paulis.
@alexanderivrii alexanderivrii requested a review from a team as a code owner March 16, 2026 09:44
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Cryoris
  • @Qiskit/terra-core
  • @ajavadia

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: Added Add an "Added" entry in the GitHub Release changelog. performance

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

2 participants