Skip to content

Conversation

kevinsung
Copy link
Contributor

Summary

Fixes #14125

Details and comments

@kevinsung kevinsung requested a review from a team as a code owner March 28, 2025 22:30
@qiskit-bot
Copy link
Collaborator

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

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

@coveralls
Copy link

coveralls commented Mar 28, 2025

Pull Request Test Coverage Report for Build 14271767169

Details

  • 11 of 11 (100.0%) changed or added relevant lines in 2 files are covered.
  • 23 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.02%) to 88.061%

Files with Coverage Reduction New Missed Lines %
crates/accelerate/src/unitary_synthesis.rs 1 94.79%
crates/qasm2/src/lex.rs 4 92.73%
crates/qasm2/src/parse.rs 18 96.68%
Totals Coverage Status
Change from base Build 14243929141: -0.02%
Covered Lines: 72895
Relevant Lines: 82778

💛 - Coveralls

@ShellyGarion
Copy link
Member

ShellyGarion commented Mar 30, 2025

Thanks for this contribution to Qiskit.

  1. Currently there are two classes: ExcitationPreserving (which is going to be deprecated in Qiskit 3.0) and the new excitation_preserving class.
    By updating only one of them, they will have a somewhat different behavior (when calling ansatz.decompose(), see an example below), which might confuse the users. I would suggest to update both classes.

  2. Could you please add release notes?

  3. I would also suggest to add this equivalence (of XXplusYY to rxx+ryy) to the equivalence library, since currently the basic translator outputs somewhat different circuits (see below).

  4. Please also add a relevant test for the transpilation. Note that your synthesis improves the CX gate count in this case (see below).

@ShellyGarion
Copy link
Member

from qiskit.circuit.library import *
from qiskit.transpiler import generate_preset_pass_manager

ansatz = excitation_preserving(3, reps=1, insert_barriers=True, entanglement="linear")
pm = generate_preset_pass_manager(
            optimization_level=0, basis_gates=["u", "cx"], seed_transpiler=12345)
transpiled_circuit = pm.run(ansatz)
pm = generate_preset_pass_manager(
            optimization_level=0, basis_gates=["u", "rzz"], seed_transpiler=12345)
transpiled_circuit2 = pm.run(ansatz)
print (ansatz)
print (ansatz.decompose())
print (ansatz.decompose().decompose().count_ops())
print (transpiled_circuit.count_ops())
print (transpiled_circuit2.count_ops())

The current code outputs:


     ┌──────────┐ ░ ┌────────────────────┐                       ░ ┌──────────┐
q_0: ┤ Rz(θ[0]) ├─░─┤0                   ├───────────────────────░─┤ Rz(θ[5]) ├
     ├──────────┤ ░ │  Interaction(θ[3]) │┌────────────────────┐ ░ ├──────────┤
q_1: ┤ Rz(θ[1]) ├─░─┤1                   ├┤0                   ├─░─┤ Rz(θ[6]) ├
     ├──────────┤ ░ └────────────────────┘│  Interaction(θ[4]) │ ░ ├──────────┤
q_2: ┤ Rz(θ[2]) ├─░───────────────────────┤1                   ├─░─┤ Rz(θ[7]) ├
     └──────────┘ ░                       └────────────────────┘ ░ └──────────┘
global phase: -θ[0]/2 - θ[1]/2 - θ[2]/2 - θ[5]/2 - θ[6]/2 - θ[7]/2
     ┌──────────┐ ░ ┌────────────┐┌────────────┐                             ░ »
q_0: ┤ U1(θ[0]) ├─░─┤0           ├┤0           ├─────────────────────────────░─»
     ├──────────┤ ░ │  Rxx(θ[3]) ││  Ryy(θ[3]) │┌────────────┐┌────────────┐ ░ »
q_1: ┤ U1(θ[1]) ├─░─┤1           ├┤1           ├┤0           ├┤0           ├─░─»
     ├──────────┤ ░ └────────────┘└────────────┘│  Rxx(θ[4]) ││  Ryy(θ[4]) │ ░ »
q_2: ┤ U1(θ[2]) ├─░─────────────────────────────┤1           ├┤1           ├─░─»
     └──────────┘ ░                             └────────────┘└────────────┘ ░ »
«     ┌──────────┐
«q_0: ┤ U1(θ[5]) ├
«     ├──────────┤
«q_1: ┤ U1(θ[6]) ├
«     ├──────────┤
«q_2: ┤ U1(θ[7]) ├
«     └──────────┘
OrderedDict({'h': 8, 'cx': 8, 'rx': 8, 'u3': 6, 'rz': 4, 'barrier': 2})
OrderedDict({'u': 26, 'cx': 8, 'barrier': 2})
OrderedDict({'u': 22, 'rzz': 4, 'barrier': 2})

The new code outputs:

     ┌──────────┐ ░ ┌────────────────────┐                       ░ ┌──────────┐
q_0: ┤ Rz(θ[0]) ├─░─┤0                   ├───────────────────────░─┤ Rz(θ[5]) ├
     ├──────────┤ ░ │  Interaction(θ[3]) │┌────────────────────┐ ░ ├──────────┤
q_1: ┤ Rz(θ[1]) ├─░─┤1                   ├┤0                   ├─░─┤ Rz(θ[6]) ├
     ├──────────┤ ░ └────────────────────┘│  Interaction(θ[4]) │ ░ ├──────────┤
q_2: ┤ Rz(θ[2]) ├─░───────────────────────┤1                   ├─░─┤ Rz(θ[7]) ├
     └──────────┘ ░                       └────────────────────┘ ░ └──────────┘
global phase: -θ[0]/2 - θ[1]/2 - θ[2]/2 - θ[5]/2 - θ[6]/2 - θ[7]/2
     ┌──────────┐ ░ ┌────────────────────┐                       ░ ┌──────────┐
q_0: ┤ U1(θ[0]) ├─░─┤0                   ├───────────────────────░─┤ U1(θ[5]) ├
     ├──────────┤ ░ │  (XX+YY)(2*θ[3],0) │┌────────────────────┐ ░ ├──────────┤
q_1: ┤ U1(θ[1]) ├─░─┤1                   ├┤0                   ├─░─┤ U1(θ[6]) ├
     ├──────────┤ ░ └────────────────────┘│  (XX+YY)(2*θ[4],0) │ ░ ├──────────┤
q_2: ┤ U1(θ[2]) ├─░───────────────────────┤1                   ├─░─┤ U1(θ[7]) ├
     └──────────┘ ░                       └────────────────────┘ ░ └──────────┘
OrderedDict({'rz': 12, 'u3': 6, 'cx': 4, 'ry': 4, 'barrier': 2, 's': 2, 'sx': 2, 'sdg': 2, 'sxdg': 2})
OrderedDict({'u': 30, 'cx': 4, 'barrier': 2})
OrderedDict({'u': 46, 'rzz': 4, 'barrier': 2}

@kevinsung
Copy link
Contributor Author

Thanks for this contribution to Qiskit.

1. Currently there are two classes: `ExcitationPreserving` (which is going to be deprecated in Qiskit 3.0) and the new `excitation_preserving` class.
   By updating only one of them, they will have a somewhat different behavior (when calling `ansatz.decompose()`, see an example below), which might confuse the users. I would suggest to update both classes.

2. Could you please add release notes?

3. I would also suggest to add this equivalence (of XXplusYY to rxx+ryy) to the equivalence library, since currently the basic translator outputs somewhat different circuits (see below).

4. Please also add a relevant test for the transpilation. Note that your synthesis improves the CX gate count in this case (see below).

Done.

@@ -116,8 +116,7 @@ def excitation_preserving(
theta = Parameter("θ")
if num_qubits > 1:
swap = QuantumCircuit(2, name="Interaction")
swap.rxx(theta, 0, 1)
swap.ryy(theta, 0, 1)
swap.append(XXPlusYYGate(2 * theta), [0, 1])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The factor of 2 is ugly but I added it to maintain equivalence with the previous code.

Copy link
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution.
I have two minor comments, but other than that this PR looks good.
@Cryoris - do you have any further comments?

@kevinsung kevinsung enabled auto-merge April 6, 2025 12:35
@kevinsung kevinsung added this pull request to the merge queue Apr 6, 2025
Merged via the queue into Qiskit:main with commit 95a10e2 Apr 6, 2025
23 of 24 checks passed
@kevinsung kevinsung deleted the excitation-preserving branch April 6, 2025 13:20
@eliarbel eliarbel added the Changelog: Bugfix Include in the "Fixed" section of the changelog label Jun 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: Bugfix Include in the "Fixed" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

excitation_preserving should use XXPlusYYGate
6 participants