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

Allow AutoRebase to ISWAPMax #1822

Merged
merged 12 commits into from
Mar 25, 2025
22 changes: 22 additions & 0 deletions pytket/binders/circuit_library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ PYBIND11_MODULE(circuit_library, library_m) {
library_m.def(
"CX_using_ZZMax", &CircPool::CX_using_ZZMax,
"Equivalent to CX, using only ZZMax, Rx and Rz gates");
library_m.def(
"CX_using_ISWAPMax", &CircPool::CX_using_ISWAPMax,
"Equivalent to CX, using only ISWAPMax and single-qubit gates");
library_m.def(
"CX_using_ISWAPMax_and_swap", &CircPool::CX_using_ISWAPMax_and_swap,
"Equivalent to CX, using only ISWAPMax and single-qubit gates, up to a "
"wire swap that is encoded in the implicit qubit permutation of the "
"Circuit");
library_m.def(
"CX_using_ZZPhase", &CircPool::CX_using_ZZPhase,
"Equivalent to CX, using only ZZPhase, Rx and Rz gates");
Expand Down Expand Up @@ -213,6 +221,12 @@ PYBIND11_MODULE(circuit_library, library_m) {
library_m.def(
"ISWAP_using_CX", &CircPool::ISWAP_using_CX,
"Equivalent to ISWAP, using CX, U3 and Rz gates");
library_m.def(
"ISWAPMax_using_TK2", &CircPool::ISWAPMax_using_TK2,
"Equivalent to ISWAPMax, using a TK2 gate");
library_m.def(
"ISWAPMax_using_CX", &CircPool::ISWAPMax_using_CX,
"Equivalent to ISWAPMax, using CX, U3 and Rz gates");
library_m.def(
"XXPhase_using_TK2", &CircPool::XXPhase_using_TK2,
"Equivalent to XXPhase, using a TK2 gate");
Expand Down Expand Up @@ -261,6 +275,14 @@ PYBIND11_MODULE(circuit_library, library_m) {
"TK2_using_ZZMax_and_swap", &CircPool::TK2_using_ZZMax_and_swap,
"Equivalent to TK2, up to a wire swap that is encoded in the implicit "
"qubit permutation of the Circuit, using up to 3 ZZMax gates.");
library_m.def(
"TK2_using_ISWAPMax", &CircPool::TK2_using_ISWAPMax,
"Equivalent to TK2, using only ISWAPMax and single-qubit gates.");
library_m.def(
"TK2_using_ISWAPMax_and_swap", &CircPool::TK2_using_ISWAPMax_and_swap,
"Equivalent to TK2, using only ISWAPMax and single-qubit gates, up to a "
"wire swap that is encoded in the implicit qubit permutation of the "
"Circuit.");
library_m.def(
"XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2,
"Equivalent to XXPhase3, using three TK2 gates");
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def requirements(self):
self.requires("pybind11_json/tci-0.2.15@tket/stable")
self.requires("symengine/tci-0.14.0@tket/stable")
self.requires("tkassert/0.3.4@tket/stable")
self.requires("tket/2.1.1@tket/stable")
self.requires("tket/2.1.2@tket/stable")
self.requires("tklog/0.3.3@tket/stable")
self.requires("tkrng/0.3.3@tket/stable")
self.requires("tktokenswap/0.3.11@tket/stable")
Expand Down
6 changes: 6 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

Unreleased
----------

* Add methods in `circuit_library` to convert `CX` and `TK2` to and from
`ISWAPMax`, and support for `AutoRebase` targeting `ISWAPMax`.

2.1.0 (March 2025)
------------------

Expand Down
26 changes: 25 additions & 1 deletion pytket/pytket/_tket/circuit_library.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ from __future__ import annotations
import pytket._tket.circuit
import sympy
import typing
__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_AAMS', 'CX_using_ECR', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'CnX_vchain_decomp', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'Rx_using_GPI', 'Ry_using_GPI', 'Rz_using_GPI', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RxRy', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_RzXSX', 'TK1_to_TK1', 'TK1_to_U3', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_AAMS', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_AAMS', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_AAMS', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up']
__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_AAMS', 'CX_using_ECR', 'CX_using_ISWAPMax', 'CX_using_ISWAPMax_and_swap', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'CnX_vchain_decomp', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAPMax_using_CX', 'ISWAPMax_using_TK2', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'Rx_using_GPI', 'Ry_using_GPI', 'Rz_using_GPI', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RxRy', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_RzXSX', 'TK1_to_TK1', 'TK1_to_U3', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_ISWAPMax', 'TK2_using_ISWAPMax_and_swap', 'TK2_using_TK2', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_AAMS', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_AAMS', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_AAMS', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up']
def BRIDGE() -> pytket._tket.circuit.Circuit:
"""
Just a BRIDGE[0,1,2] gate
Expand Down Expand Up @@ -139,6 +139,14 @@ def CX_using_ECR() -> pytket._tket.circuit.Circuit:
"""
Equivalent to CX, using only ECR, Rx and U3 gates
"""
def CX_using_ISWAPMax() -> pytket._tket.circuit.Circuit:
"""
Equivalent to CX, using only ISWAPMax and single-qubit gates
"""
def CX_using_ISWAPMax_and_swap() -> pytket._tket.circuit.Circuit:
"""
Equivalent to CX, using only ISWAPMax and single-qubit gates, up to a wire swap that is encoded in the implicit qubit permutation of the Circuit
"""
def CX_using_TK2() -> pytket._tket.circuit.Circuit:
"""
Equivalent to CX, using a TK2 and single-qubit gates
Expand Down Expand Up @@ -204,6 +212,14 @@ def H_CZ_H() -> pytket._tket.circuit.Circuit:
"""
H[1]; CZ[0,1]; H[1]
"""
def ISWAPMax_using_CX() -> pytket._tket.circuit.Circuit:
"""
Equivalent to ISWAPMax, using CX, U3 and Rz gates
"""
def ISWAPMax_using_TK2() -> pytket._tket.circuit.Circuit:
"""
Equivalent to ISWAPMax, using a TK2 gate
"""
def ISWAP_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit:
"""
Equivalent to ISWAP, using CX, U3 and Rz gates
Expand Down Expand Up @@ -302,6 +318,14 @@ def TK2_using_CX_and_swap(arg0: sympy.Expr | float, arg1: sympy.Expr | float, ar

The decomposition minimizes the number of CX gates.
"""
def TK2_using_ISWAPMax(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit:
"""
Equivalent to TK2, using only ISWAPMax and single-qubit gates.
"""
def TK2_using_ISWAPMax_and_swap(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit:
"""
Equivalent to TK2, using only ISWAPMax and single-qubit gates, up to a wire swap that is encoded in the implicit qubit permutation of the Circuit.
"""
def TK2_using_TK2(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit:
"""
A circuit of a single TK2 gate with given parameters
Expand Down
6 changes: 6 additions & 0 deletions pytket/pytket/circuit_library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
CX_using_AAMS,
CX_using_ECR,
CX_using_flipped_CX,
CX_using_ISWAPMax,
CX_using_ISWAPMax_and_swap,
CX_using_TK2,
CX_using_XXPhase_0,
CX_using_XXPhase_1,
Expand All @@ -66,6 +68,8 @@
FSim_using_TK2,
ISWAP_using_CX,
ISWAP_using_TK2,
ISWAPMax_using_CX,
ISWAPMax_using_TK2,
NPhasedX_using_PhasedX,
PhasedISWAP_using_CX,
PhasedISWAP_using_TK2,
Expand All @@ -84,6 +88,8 @@
TK2_using_AAMS,
TK2_using_CX,
TK2_using_CX_and_swap,
TK2_using_ISWAPMax,
TK2_using_ISWAPMax_and_swap,
TK2_using_normalised_TK2,
TK2_using_TK2_or_swap,
TK2_using_ZZMax,
Expand Down
6 changes: 6 additions & 0 deletions pytket/tests/predicates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,12 @@ def test_rz_sx_decomp() -> None:
assert c == comp


def test_iswapmax_autorebase() -> None:
c = Circuit(2).H(0).CX(0, 1)
assert AutoRebase({OpType.ISWAPMax, OpType.TK1}).apply(c)
assert c.n_gates_of_type(OpType.ISWAPMax) <= 2


def test_flatten_relabel_pass() -> None:
c = Circuit(3)
c.H(1).H(2)
Expand Down
2 changes: 1 addition & 1 deletion tket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

class TketConan(ConanFile):
name = "tket"
version = "2.1.1"
version = "2.1.2"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
24 changes: 24 additions & 0 deletions tket/include/tket/Circuit/CircPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ const Circuit &CX_using_ECR();
/** Equivalent to CX, using only ZZMax, Rx and Rz gates */
const Circuit &CX_using_ZZMax();

/** Equivalent to CX, using only ISWAPMax and single-qubit gates */
const Circuit &CX_using_ISWAPMax();

/** Equivalent to CX, using only ISWAPMax and single-qubit gates, with an
* implicit swap.
*/
const Circuit &CX_using_ISWAPMax_and_swap();

/** Equivalent to CX, using only ZZPhase, Rx and Rz gates */
const Circuit &CX_using_ZZPhase();

Expand Down Expand Up @@ -196,6 +204,12 @@ const Circuit &ECR_using_CX();
/** Equivalent to ZZMax, using CX, Rz and U3 gates */
const Circuit &ZZMax_using_CX();

/** Equivalent to ISWAPMax, using a TK2 gate **/
const Circuit &ISWAPMax_using_TK2();

/** Equivalent to ISWAPMax, using CX, Rz and U3 gates **/
const Circuit &ISWAPMax_using_CX();

/** Equivalent to CRz, using a TK2 and TK1 gates */
Circuit CRz_using_TK2(const Expr &alpha);

Expand Down Expand Up @@ -424,6 +438,16 @@ Circuit TK2_using_ZZMax(const Expr &alpha, const Expr &beta, const Expr &gamma);
Circuit TK2_using_ZZMax_and_swap(
const Expr &alpha, const Expr &beta, const Expr &gamma);

/** Equivalent to TK2, using only ISWAPMax and single-qubit gates */
Circuit TK2_using_ISWAPMax(
const Expr &alpha, const Expr &beta, const Expr &gamma);

/** Equivalent to TK2, using only ISWAPMax and single-qubit gates, with an
* implicit swap.
*/
Circuit TK2_using_ISWAPMax_and_swap(
const Expr &alpha, const Expr &beta, const Expr &gamma);

/** Equivalent to XXPhase3, using three TK2 gates */
Circuit XXPhase3_using_TK2(const Expr &alpha);

Expand Down
88 changes: 88 additions & 0 deletions tket/src/Circuit/CircPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,34 @@ const Circuit &CX_using_ZZMax() {
return *C;
}

const Circuit &CX_using_ISWAPMax() {
static std::unique_ptr<const Circuit> C = std::make_unique<Circuit>([]() {
Circuit c(2);
c.add_op<unsigned>(OpType::ISWAPMax, {0, 1});
c.add_op<unsigned>(OpType::H, {0});
c.add_op<unsigned>(OpType::ISWAPMax, {0, 1});
c.add_op<unsigned>(OpType::Sdg, {0});
c.add_op<unsigned>(OpType::V, {1});
return c;
}());
return *C;
}

const Circuit &CX_using_ISWAPMax_and_swap() {
static std::unique_ptr<const Circuit> C = std::make_unique<Circuit>([]() {
Circuit c(2);
c.add_op<unsigned>(OpType::H, {1});
c.add_op<unsigned>(OpType::Sdg, {0});
c.add_op<unsigned>(OpType::Sdg, {1});
c.add_op<unsigned>(OpType::ISWAPMax, {0, 1});
c.add_op<unsigned>(OpType::H, {0});
c.add_op<unsigned>(OpType::SWAP, {0, 1});
c.replace_SWAPs();
return c;
}());
return *C;
}

const Circuit &CX_using_ZZPhase() {
static std::unique_ptr<const Circuit> C = std::make_unique<Circuit>([]() {
Circuit c(2);
Expand Down Expand Up @@ -684,6 +712,31 @@ const Circuit &ZZMax_using_CX() {
return *C;
}

const Circuit &ISWAPMax_using_TK2() {
static std::unique_ptr<const Circuit> C = std::make_unique<Circuit>([]() {
Circuit c(2);
c.add_op<unsigned>(OpType::TK2, {-0.5, -0.5, 0}, {0, 1});
return c;
}());
return *C;
}

const Circuit &ISWAPMax_using_CX() {
static std::unique_ptr<const Circuit> C = std::make_unique<Circuit>([]() {
Circuit c(2);
c.add_op<unsigned>(OpType::U3, {0.5, -0.5, 0.5}, {0});
c.add_op<unsigned>(OpType::U3, {0.5, -0.5, 0.5}, {1});
c.add_op<unsigned>(OpType::CX, {0, 1});
c.add_op<unsigned>(OpType::U3, {-0.5, -0.5, 0.5}, {0});
c.add_op<unsigned>(OpType::Rz, -0.5, {1});
c.add_op<unsigned>(OpType::CX, {0, 1});
c.add_op<unsigned>(OpType::U3, {-0.5, -0.5, 0.5}, {0});
c.add_op<unsigned>(OpType::U3, {-0.5, -0.5, 0.5}, {1});
return c;
}());
return *C;
}

Circuit CRz_using_TK2(const Expr &alpha) {
Circuit c(2);
c.add_op<unsigned>(OpType::TK1, {0.5, 0.5, 1}, {0});
Expand Down Expand Up @@ -1116,6 +1169,41 @@ Circuit TK2_using_ZZMax_and_swap(
return TK2_using_ZZMax(alpha, beta, gamma);
}

Circuit TK2_using_ISWAPMax(
const Expr &alpha, const Expr &beta, const Expr &gamma) {
Circuit c = TK2_using_CX(alpha, beta, gamma);
// Find the CX gates and replace them with ISWAPMax.
VertexSet bin;
BGL_FORALL_VERTICES(v, c.dag, DAG) {
Op_ptr op = c.get_Op_ptr_from_Vertex(v);
if (op->get_type() == OpType::CX) {
c.substitute(CX_using_ISWAPMax(), v, Circuit::VertexDeletion::No);
bin.insert(v);
}
}
c.remove_vertices(
bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes);
return c;
}

Circuit TK2_using_ISWAPMax_and_swap(
const Expr &alpha, const Expr &beta, const Expr &gamma) {
Circuit c = TK2_using_CX_and_swap(alpha, beta, gamma);
// Find the CX gates and replace them with ISWAPMax.
VertexSet bin;
BGL_FORALL_VERTICES(v, c.dag, DAG) {
Op_ptr op = c.get_Op_ptr_from_Vertex(v);
if (op->get_type() == OpType::CX) {
c.substitute(
CX_using_ISWAPMax_and_swap(), v, Circuit::VertexDeletion::No);
bin.insert(v);
}
}
c.remove_vertices(
bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes);
return c;
}

Circuit XXPhase3_using_TK2(const Expr &alpha) {
Circuit c(3);
c.add_op<unsigned>(OpType::TK2, {alpha, 0, 0}, {0, 1});
Expand Down
4 changes: 3 additions & 1 deletion tket/src/Circuit/CircUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ Circuit with_TK2(Gate_ptr op) {
switch (op->get_type()) {
case OpType::ISWAP:
return CircPool::ISWAP_using_TK2(params[0]);
case OpType::ISWAPMax:
return CircPool::ISWAPMax_using_TK2();
case OpType::PhasedISWAP:
return CircPool::PhasedISWAP_using_TK2(params[0], params[1]);
case OpType::XXPhase:
Expand Down Expand Up @@ -603,7 +605,7 @@ Circuit with_CX(Gate_ptr op) {
case OpType::Sycamore:
return CircPool::FSim_using_CX(1. / 2., 1. / 6.);
case OpType::ISWAPMax:
return CircPool::ISWAP_using_CX(1.);
return CircPool::ISWAPMax_using_CX();
case OpType::PhasedISWAP:
return CircPool::PhasedISWAP_using_CX(params[0], params[1]);
case OpType::NPhasedX:
Expand Down
Loading
Loading