Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add equivalence library entry for swap to ECR or CZ #12312

Merged
merged 10 commits into from
May 2, 2024

Conversation

mtreinish
Copy link
Member

Summary

This commit adds two new equivalence library entries to cover the conversion from a SWAP gate to either ecr or cz directly. These are common 2q basis gates and without these entries in the equivalence library the path found from a lookup ends up with a much less efficient translation. This commit adds the two new entries so that the BasisTranslator will use a more efficient decomposition from the start when targeting these basis. This will hopefully result in less work for the optimization stage as the output will already be optimal and not require simplification.

Testing for this PR is handled automatically by the built-in testing harness in test_gate_definitions.py that evaluates all the entries in the standard equivalence library for unitary equivalence.

Details and comments

This commit adds two new equivalence library entries to cover the
conversion from a SWAP gate to either ecr or cz directly. These are
common 2q basis gates and without these entries in the equivalence
library the path found from a lookup ends up with a much less efficient
translation. This commit adds the two new entries so that the
BasisTranslator will use a more efficient decomposition from the start
when targeting these basis. This will hopefully result in less work for
the optimization stage as the output will already be optimal and not
require simplification.

Testing for this PR is handled automatically by the built-in testing
harness in test_gate_definitions.py that evaluates all the entries in
the standard equivalence library for unitary equivalence.
@mtreinish mtreinish added performance Changelog: None Do not include in changelog mod: transpiler Issues and PRs related to Transpiler labels Apr 30, 2024
@mtreinish mtreinish added this to the 1.1.0 milestone Apr 30, 2024
@mtreinish mtreinish requested a review from a team as a code owner April 30, 2024 18:43
@qiskit-bot
Copy link
Collaborator

One or more of the the following people are requested to review this:

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

@coveralls
Copy link

coveralls commented Apr 30, 2024

Pull Request Test Coverage Report for Build 8921883699

Details

  • 24 of 24 (100.0%) changed or added relevant lines in 1 file are covered.
  • 16 unchanged lines in 4 files lost coverage.
  • Overall coverage increased (+0.008%) to 89.549%

Files with Coverage Reduction New Missed Lines %
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 1 88.02%
crates/qasm2/src/expr.rs 1 94.03%
crates/qasm2/src/lex.rs 2 93.13%
crates/qasm2/src/parse.rs 12 97.15%
Totals Coverage Status
Change from base Build 8917584107: 0.008%
Covered Lines: 61630
Relevant Lines: 68823

💛 - Coveralls

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.

I agree that to synthesize a SWAP gate we need 3 cx / cz / ecr gates. But are we sure that the number of sx gates is optimal ?

@mtreinish
Copy link
Member Author

I ran the two qubit basis decomposer to generate these circuits:

from qiskit.synthesis import TwoQubitBasisDecomposer
from qiskit.circuit.library import ECRGate, CZGate, SwapGate
decomp_ecr = TwoQubitBasisDecomposer(ECRGate(), euler_basis="ZSXX")
decomp_cz = TwoQubitBasisDecomposer(CZGate(), euler_basis="ZSXX")
decomp_ecr(SwapGate().to_matrix())
decomp_cz(SwapGate().to_matrix())

and also went about it a different way to verify by using the basis translator and then ran Optimize1qGatesDecomposition and got the same result. So it's at least as good as Qiskit itself will do after transpilation. If we can find a better synthesis we can always adjust this later.

@alexanderivrii was checking the results with this applied and found that doing this resulted in a modest reduction of gates when doing a full transpile in some 100 qubit workloads in the asv benchmarks. I'm about to kick off an asv run myself to see what effect this has on runtime.

@mtreinish
Copy link
Member Author

I ran some of the asv benchmarks with this PR:

Benchmarks that have improved:

| Change   | Before [f2b874b8] <efficient-swap-conversion^2>   | After [dd7ae517] <efficient-swap-conversion>   |   Ratio | Benchmark (Parameter)                                                     |
|----------|---------------------------------------------------|------------------------------------------------|---------|---------------------------------------------------------------------------|
| -        | 2.42±0.02s                                        | 2.20±0.02s                                     |    0.91 | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('cz')         |
| -        | 4.05±0.03s                                        | 3.50±0s                                        |    0.86 | utility_scale.UtilityScaleBenchmarks.time_qaoa('ecr')                     |
| -        | 2496                                              | 1971                                           |    0.79 | utility_scale.UtilityScaleBenchmarks.track_qft_depth('ecr')               |
| -        | 444                                               | 337                                            |    0.76 | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('ecr') |
| -        | 1622                                              | 1160                                           |    0.72 | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('ecr')              |

Benchmarks that have stayed the same:

| Change   | Before [f2b874b8] <efficient-swap-conversion^2>   | After [dd7ae517] <efficient-swap-conversion>   | Ratio   | Benchmark (Parameter)                                                         |
|----------|---------------------------------------------------|------------------------------------------------|---------|-------------------------------------------------------------------------------|
|          | 5.46±0.02s                                        | 4.61±0.04s                                     | ~0.85   | utility_scale.UtilityScaleBenchmarks.time_qaoa('cz')                          |
|          | 9.12±0.08ms                                       | 9.17±0.04ms                                    | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('ecr')              |
|          | 1.07±0.01s                                        | 1.08±0.01s                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_qaoa('cx')                          |
|          | 20.7±0.04s                                        | 20.9±0.06s                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_qft('cx')                           |
|          | 9.06±0.03ms                                       | 9.10±0.02ms                                    | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('cx')               |
|          | 9.08±0.1ms                                        | 9.04±0.02ms                                    | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('cz')               |
|          | 98.9±0.9ms                                        | 99.4±1ms                                       | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('cz')                |
|          | 97.9±1ms                                          | 98.2±0.6ms                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('ecr')               |
|          | 32.4±0.2ms                                        | 32.2±0.1ms                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('cx')  |
|          | 32.3±0.2ms                                        | 32.3±0.1ms                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('cz')  |
|          | 32.2±0.2ms                                        | 32.2±0.1ms                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('ecr') |
|          | 1.20±0.01s                                        | 1.20±0s                                        | 1.00    | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('cx')             |
|          | 1607                                              | 1607                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('cx')                   |
|          | 1618                                              | 1622                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('cz')                   |
|          | 2582                                              | 2582                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qft_depth('cx')                    |
|          | 2582                                              | 2582                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qft_depth('cz')                    |
|          | 444                                               | 444                                            | 1.00    | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('cx')      |
|          | 444                                               | 444                                            | 1.00    | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('cz')      |
|          | 99.4±0.5ms                                        | 98.9±0.9ms                                     | 0.99    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('cx')                |
|          | 25.5±0.03s                                        | 25.2±0.1s                                      | 0.99    | utility_scale.UtilityScaleBenchmarks.time_qft('ecr')                          |
|          | 26.2±0.01s                                        | 25.4±0.2s                                      | 0.97    | utility_scale.UtilityScaleBenchmarks.time_qft('cz')                           |
|          | 2.03±0.03s                                        | 1.92±0s                                        | 0.95    | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('ecr')            |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

@ShellyGarion
Copy link
Member

which 1-qubit gates could we use for the equivalence?

here is another possible definition for a swap gate using ecr gates and x, sx, sxdg and sdg gates:

swap_to_ecr = QuantumCircuit(2, global_phase=3 * math.pi / 4)
swap_to_ecr.sdg(0)
swap_to_ecr.sxdg(1)
swap_to_ecr.ecr(0, 1)
swap_to_ecr.sdg(1)
swap_to_ecr.sx(0)
swap_to_ecr.ecr(1, 0)
swap_to_ecr.sdg(0)
swap_to_ecr.sx(1)
swap_to_ecr.ecr(0, 1)
swap_to_ecr.x(0)
print (swap_to_ecr)

@mtreinish
Copy link
Member Author

In general it shouldn't matter too much as long as there is a path from the gates in the circuit to the target. I picked the basis here specifically because it's what IBM backends use and it is the most efficient to give the direct output with the minimum gates as the basis translator will see it can do that with 1 transform and opt for that. This will avoid needing to clean it up later during the optimization stage.

I like your definition though because it keeps everything in the clifford gates so people targeting the stabilizer simulator can use it (although I think aer recognizes that pi/2 angles for rz are cliffords now). We can add that definition to the library too, there isn't any harm in having multiple options available, the lookup should be fairly fast regardless and more options typically leads to better output.

@ShellyGarion
Copy link
Member

ShellyGarion commented May 1, 2024

here is another simple definition for a swap gate using cz and h gates:

swap_to_cz = QuantumCircuit(2)
swap_to_cz.h(0)
swap_to_cz.cz(0, 1)
swap_to_cz.h(0)
swap_to_cz.h(1)
swap_to_cz.cz(0, 1)
swap_to_cz.h(1)
swap_to_cz.h(0)
swap_to_cz.cz(0, 1)
swap_to_cz.h(0)
print (swap_to_cz)

@mtreinish
Copy link
Member Author

Oh actually, I think we might already have these definitions effectively in the library just not explicit entries. Looking at https://github.com/Qiskit/qiskit/blob/main/qiskit/circuit/library/standard_gates/equivalence_library.py#L1288-L1302 we're already using that definition for cx -> cz. Also for cx -> ecr: https://github.com/Qiskit/qiskit/blob/main/qiskit/circuit/library/standard_gates/equivalence_library.py#L728-L745. So when the basis translator goes from swap -> ecr it can find this by doing swap -> cx -> ecr. I'm not sure we need explicit entries in this case because that path existed already. For example:

>>> from qiskit import QuantumCircuit, transpile
>>> qc = QuantumCircuit(2)
>>> qc.swap(0, 1)
>>> print(transpile(qc, basis_gates=['h', 'cz'], optimization_level=0))
             ┌───┐   ┌───┐        
q_0: ──────■─┤ H ├─■─┤ H ├─■──────
     ┌───┐ │ ├───┤ │ ├───┤ │ ┌───┐
q_1: ┤ H ├─■─┤ H ├─■─┤ H ├─■─┤ H ├
     └───┘   └───┘   └───┘   └───┘
>>> print(transpile(qc, basis_gates=['sxdg', 'sdg', 'x', 'ecr'], optimization_level=0))
global phase: 3π/4
     ┌─────┐ ┌──────┐ ┌───┐ ┌──────┐┌──────┐┌─────┐        ┌──────┐┌───┐
q_0: ┤ Sdg ├─┤0     ├─┤ X ├─┤ √Xdg ├┤1     ├┤ Sdg ├────────┤0     ├┤ X ├
     ├─────┴┐│  Ecr │┌┴───┴┐└──────┘│  Ecr │└┬───┬┘┌──────┐│  Ecr │└───┘
q_1: ┤ √Xdg ├┤1     ├┤ Sdg ├────────┤0     ├─┤ X ├─┤ √Xdg ├┤1     ├─────
     └──────┘└──────┘└─────┘        └──────┘ └───┘ └──────┘└──────┘     

The extra cases help here because the synthesis output are not efficient when targeting rz, sx, x 1q gates.

mtreinish and others added 2 commits May 1, 2024 13:08
As fallout from the addition of SingletonGate and
SingletonControlledGate we were accidentally not running large portions
of the unit tests which validate the default session equivalence
library. This test dynamically runs based on all members of the standard
gate library by looking at all defined subclasses of Gate and
ControlledGate. But with the introduction of SingletonGate and
SingletonControlledGate all the unparameterized gates in the library
were not being run through the tests. This commit fixes this to catch
that the swap definition added in the previous commit on this PR branch
used an incorrect definition of SwapGate using ECRGate. The definition
will be fixed in a follow up PR.
The previous definition of a swap gate using ECR rz and sx was incorrect
and also not as efficient as possible. This was missed because the tests
were accidently broken since Qiskit#10314 which was fixed in the previous
commit. This commit updates the definition to use one that is actually
correct and also more efficient with fewer 1 qubit gates.

Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
@alexanderivrii
Copy link
Contributor

And here is one more variant of converting the SWAP gate to 3 ECR + single-qubits gates:

    qc2 = QuantumCircuit(2)
    qc2.rz(- pi / 2, 0)
    qc2.sx(1)
    qc2.ecr(0, 1)
    qc2.rz(- pi / 2, 1)
    qc2.sx(0)
    qc2.ecr(1, 0)
    qc2.rz(- pi / 2, 0)
    qc2.sx(1)
    qc2.ecr(0, 1)

which is actually exactly the second example from #12312 (comment) after pushing all the Pauli (X) gates to the end of the circuit (where they happen to cancel out).

@mtreinish
Copy link
Member Author

And here is one more variant of converting the SWAP gate to 3 ECR + single-qubits gates:

    qc2 = QuantumCircuit(2)
    qc2.rz(- pi / 2, 0)
    qc2.sx(1)
    qc2.ecr(0, 1)
    qc2.rz(- pi / 2, 1)
    qc2.sx(0)
    qc2.ecr(1, 0)
    qc2.rz(- pi / 2, 0)
    qc2.sx(1)
    qc2.ecr(0, 1)

which is actually exactly the second example from #12312 (comment) after pushing all the Pauli (X) gates to the end of the circuit (where they happen to cancel out).

Thanks, I updated the PR to use this in: 1a0521e and also updated the tests in 4e90c08 which were inadvertently broken by #10314 and it's follow-on PRs. The definition I was using before was incorrect because of typos on my part and the tests didn't flag that which has been corrected now.

@alexanderivrii
Copy link
Contributor

alexanderivrii commented May 2, 2024

Here is an even shorter decomposition of SWAP into CZ + SX that Shelly and I have just found (no need for RZ gate):


qc = QuantumCircuit(2)
qc.swap(0, 1)

qc2 = QuantumCircuit(2, global_phase=-pi/2)

qc2.sx(0)
qc2.sx(1)
qc2.cz(0, 1)

qc2.sx(0)
qc2.sx(1)
qc2.cz(0, 1)

qc2.sx(0)
qc2.sx(1)
qc2.cz(0, 1)

The two circuits are equal (as operators). And here is a picture:

global phase: 3π/2
     ┌────┐   ┌────┐   ┌────┐   
q_0: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─
     ├────┤ │ ├────┤ │ ├────┤ │ 
q_1: ┤ √X ├─■─┤ √X ├─■─┤ √X ├─■─
     └────┘   └────┘   └────┘   

P.S. We have used qiskit-sat-synthesis for the task. :)

Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
@mtreinish
Copy link
Member Author

Ok sure, updated in: d8d819f

@sbrandhsn
Copy link
Contributor

See https://algassert.com/post/1717 on why this works :-) I think it is awesome that qiskit-sat-synthesis generates a decomposition that is lower in depth than the approach in the link! :-) Is the decomposition optimal in number of gates/depth?

@mtreinish
Copy link
Member Author

I've rerun the asv benchmarks with the current state of this PR with the more efficient decompositions:

Benchmarks that have improved:

| Change   | Before [676a5ed9] <efficient-swap-conversion~1^2>   | After [d8d819f5] <efficient-swap-conversion>   |   Ratio | Benchmark (Parameter)                                              |
|----------|-----------------------------------------------------|------------------------------------------------|---------|--------------------------------------------------------------------|
| -        | 2.14±0.02s                                          | 1.77±0.01s                                     |    0.83 | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('ecr') |
| -        | 2.54±0.02s                                          | 1.91±0.02s                                     |    0.75 | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('cz')  |
| -        | 4.14±0.01s                                          | 2.40±0.02s                                     |    0.58 | utility_scale.UtilityScaleBenchmarks.time_qaoa('ecr')              |
| -        | 5.54±0.02s                                          | 2.90±0.01s                                     |    0.52 | utility_scale.UtilityScaleBenchmarks.time_qaoa('cz')               |

Benchmarks that have stayed the same:

| Change   | Before [676a5ed9] <efficient-swap-conversion~1^2>   | After [d8d819f5] <efficient-swap-conversion>   | Ratio   | Benchmark (Parameter)                                                         |
|----------|-----------------------------------------------------|------------------------------------------------|---------|-------------------------------------------------------------------------------|
|          | 26.3±0.01s                                          | 23.7±0.2s                                      | ~0.90   | utility_scale.UtilityScaleBenchmarks.time_qft('cz')                           |
|          | 2496                                                | 2582                                           | 1.03    | utility_scale.UtilityScaleBenchmarks.track_qft_depth('ecr')                   |
|          | 95.4±0.4ms                                          | 97.1±0.8ms                                     | 1.02    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('ecr')               |
|          | 8.92±0.05ms                                         | 8.98±0.07ms                                    | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('cz')               |
|          | 9.00±0.05ms                                         | 9.09±0.2ms                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('ecr')              |
|          | 98.0±0.4ms                                          | 98.5±0.7ms                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('cx')                |
|          | 31.6±0.2ms                                          | 31.8±0.1ms                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('cx')  |
|          | 31.4±0.1ms                                          | 31.6±0.2ms                                     | 1.01    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('ecr') |
|          | 1.10±0s                                             | 1.10±0s                                        | 1.01    | utility_scale.UtilityScaleBenchmarks.time_qaoa('cx')                          |
|          | 20.6±0.03s                                          | 20.8±0s                                        | 1.01    | utility_scale.UtilityScaleBenchmarks.time_qft('cx')                           |
|          | 96.7±0.5ms                                          | 96.4±0.4ms                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_qft_n100('cz')                |
|          | 31.6±0.2ms                                          | 31.7±0.03ms                                    | 1.00    | utility_scale.UtilityScaleBenchmarks.time_parse_square_heisenberg_n100('cz')  |
|          | 1.28±0.01s                                          | 1.27±0.01s                                     | 1.00    | utility_scale.UtilityScaleBenchmarks.time_square_heisenberg('cx')             |
|          | 1607                                                | 1607                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('cx')                   |
|          | 1618                                                | 1622                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('cz')                   |
|          | 1622                                                | 1622                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qaoa_depth('ecr')                  |
|          | 2582                                                | 2582                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qft_depth('cx')                    |
|          | 2582                                                | 2582                                           | 1.00    | utility_scale.UtilityScaleBenchmarks.track_qft_depth('cz')                    |
|          | 444                                                 | 444                                            | 1.00    | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('cx')      |
|          | 444                                                 | 444                                            | 1.00    | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('cz')      |
|          | 444                                                 | 444                                            | 1.00    | utility_scale.UtilityScaleBenchmarks.track_square_heisenberg_depth('ecr')     |
|          | 8.99±0.07ms                                         | 8.92±0.08ms                                    | 0.99    | utility_scale.UtilityScaleBenchmarks.time_parse_qaoa_n100('cx')               |
|          | 25.6±0.01s                                          | 23.9±0.2s                                      | 0.93    | utility_scale.UtilityScaleBenchmarks.time_qft('ecr')                          |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

Copy link
Contributor

@alexanderivrii alexanderivrii left a comment

Choose a reason for hiding this comment

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

LGTM! And very nice runtime reductions!

@alexanderivrii alexanderivrii added this pull request to the merge queue May 2, 2024
Merged via the queue into Qiskit:main with commit c53984f May 2, 2024
15 checks passed
ElePT pushed a commit to ElePT/qiskit that referenced this pull request May 31, 2024
* Add equivalence library entry for swap to ECR or CZ

This commit adds two new equivalence library entries to cover the
conversion from a SWAP gate to either ecr or cz directly. These are
common 2q basis gates and without these entries in the equivalence
library the path found from a lookup ends up with a much less efficient
translation. This commit adds the two new entries so that the
BasisTranslator will use a more efficient decomposition from the start
when targeting these basis. This will hopefully result in less work for
the optimization stage as the output will already be optimal and not
require simplification.

Testing for this PR is handled automatically by the built-in testing
harness in test_gate_definitions.py that evaluates all the entries in
the standard equivalence library for unitary equivalence.

* Add name to annotated gate circuit in qpy backwards compat tests

* Fix equivalence library tests

As fallout from the addition of SingletonGate and
SingletonControlledGate we were accidentally not running large portions
of the unit tests which validate the default session equivalence
library. This test dynamically runs based on all members of the standard
gate library by looking at all defined subclasses of Gate and
ControlledGate. But with the introduction of SingletonGate and
SingletonControlledGate all the unparameterized gates in the library
were not being run through the tests. This commit fixes this to catch
that the swap definition added in the previous commit on this PR branch
used an incorrect definition of SwapGate using ECRGate. The definition
will be fixed in a follow up PR.

* Use a more efficient and actually correct circuit for ECR target

The previous definition of a swap gate using ECR rz and sx was incorrect
and also not as efficient as possible. This was missed because the tests
were accidently broken since Qiskit#10314 which was fixed in the previous
commit. This commit updates the definition to use one that is actually
correct and also more efficient with fewer 1 qubit gates.

Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>

* Update ECR circuit diagram in comment

* Simplify cz equivalent circuit

* Simplify cz circuit even more

Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>

---------

Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
Co-authored-by: Shelly Garion <46566946+ShellyGarion@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog mod: transpiler Issues and PRs related to Transpiler performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants