diff --git a/pytket/binders/circuit/main.cpp b/pytket/binders/circuit/main.cpp index 2687745a63..2214622f9e 100644 --- a/pytket/binders/circuit/main.cpp +++ b/pytket/binders/circuit/main.cpp @@ -439,10 +439,18 @@ PYBIND11_MODULE(circuit, m) { ":math:`(\\alpha, \\beta) \\mapsto \\mathrm{PhasedX}(\\alpha, \\beta)" "^{\\otimes n}` (n-qubit gate composed of identical PhasedX in " "parallel.") + .value( + "CnRx", OpType::CnRx, + ":math:`(\\alpha)` := n-controlled " + ":math:`\\mathrm{Rx}(\\alpha)` gate.") .value( "CnRy", OpType::CnRy, ":math:`(\\alpha)` := n-controlled " ":math:`\\mathrm{Ry}(\\alpha)` gate.") + .value( + "CnRz", OpType::CnRz, + ":math:`(\\alpha)` := n-controlled " + ":math:`\\mathrm{Rz}(\\alpha)` gate.") .value("CnX", OpType::CnX, "n-controlled X gate.") .value("CnY", OpType::CnY, "n-controlled Y gate.") .value("CnZ", OpType::CnZ, "n-controlled Z gate.") diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index bd621e0cae..8bed732bc1 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -453,7 +453,7 @@ PYBIND11_MODULE(passes, m) { m.def( "DecomposeArbitrarilyControlledGates", &DecomposeArbitrarilyControlledGates, - "Decomposes CCX, CnX, CnY, CnZ, and CnRy gates into " + "Decomposes CCX, CnX, CnY, CnZ, CnRy, CnRz and CnRx gates into " "CX and single-qubit gates."); m.def( "DecomposeBoxes", &DecomposeBoxes, diff --git a/pytket/conanfile.py b/pytket/conanfile.py index dde43e56cd..04ceca9ea9 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.3.0@tket/stable") + self.requires("tket/1.3.1@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 70f385eb4d..f8a09210c6 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -4,6 +4,10 @@ Changelog Unreleased ---------- +Features: + +* Add ``OpType.CnRx`` and ``OpType.CnRz``. + Fixes: * Allow barriers when dagger or transpose a circuit. diff --git a/pytket/pytket/_tket/circuit.pyi b/pytket/pytket/_tket/circuit.pyi index 163ac46444..eb0e7f8de3 100644 --- a/pytket/pytket/_tket/circuit.pyi +++ b/pytket/pytket/_tket/circuit.pyi @@ -3250,8 +3250,12 @@ class OpType: NPhasedX : :math:`(\\alpha, \\beta) \\mapsto \\mathrm{PhasedX}(\\alpha, \\beta)^{\\otimes n}` (n-qubit gate composed of identical PhasedX in parallel. + CnRx : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Rx}(\\alpha)` gate. + CnRy : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Ry}(\\alpha)` gate. + CnRz : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Rz}(\\alpha)` gate. + CnX : n-controlled X gate. CnY : n-controlled Y gate. @@ -3319,22 +3323,24 @@ class OpType: CX: typing.ClassVar[OpType] # value = CY: typing.ClassVar[OpType] # value = CZ: typing.ClassVar[OpType] # value = - CircBox: typing.ClassVar[OpType] # value = - ClassicalExpBox: typing.ClassVar[OpType] # value = + CircBox: typing.ClassVar[OpType] # value = + ClassicalExpBox: typing.ClassVar[OpType] # value = ClassicalTransform: typing.ClassVar[OpType] # value = + CnRx: typing.ClassVar[OpType] # value = CnRy: typing.ClassVar[OpType] # value = - CnX: typing.ClassVar[OpType] # value = - CnY: typing.ClassVar[OpType] # value = - CnZ: typing.ClassVar[OpType] # value = - Conditional: typing.ClassVar[OpType] # value = - ConjugationBox: typing.ClassVar[OpType] # value = + CnRz: typing.ClassVar[OpType] # value = + CnX: typing.ClassVar[OpType] # value = + CnY: typing.ClassVar[OpType] # value = + CnZ: typing.ClassVar[OpType] # value = + Conditional: typing.ClassVar[OpType] # value = + ConjugationBox: typing.ClassVar[OpType] # value = CopyBits: typing.ClassVar[OpType] # value = - CustomGate: typing.ClassVar[OpType] # value = - DiagonalBox: typing.ClassVar[OpType] # value = - DummyBox: typing.ClassVar[OpType] # value = + CustomGate: typing.ClassVar[OpType] # value = + DiagonalBox: typing.ClassVar[OpType] # value = + DummyBox: typing.ClassVar[OpType] # value = ECR: typing.ClassVar[OpType] # value = ESWAP: typing.ClassVar[OpType] # value = - ExpBox: typing.ClassVar[OpType] # value = + ExpBox: typing.ClassVar[OpType] # value = ExplicitModifier: typing.ClassVar[OpType] # value = ExplicitPredicate: typing.ClassVar[OpType] # value = FSim: typing.ClassVar[OpType] # value = @@ -3347,19 +3353,19 @@ class OpType: Label: typing.ClassVar[OpType] # value = Measure: typing.ClassVar[OpType] # value = MultiBit: typing.ClassVar[OpType] # value = - MultiplexedRotationBox: typing.ClassVar[OpType] # value = - MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = - MultiplexedU2Box: typing.ClassVar[OpType] # value = - MultiplexorBox: typing.ClassVar[OpType] # value = + MultiplexedRotationBox: typing.ClassVar[OpType] # value = + MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = + MultiplexedU2Box: typing.ClassVar[OpType] # value = + MultiplexorBox: typing.ClassVar[OpType] # value = NPhasedX: typing.ClassVar[OpType] # value = - PauliExpBox: typing.ClassVar[OpType] # value = - PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = - PauliExpPairBox: typing.ClassVar[OpType] # value = + PauliExpBox: typing.ClassVar[OpType] # value = + PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = + PauliExpPairBox: typing.ClassVar[OpType] # value = Phase: typing.ClassVar[OpType] # value = - PhasePolyBox: typing.ClassVar[OpType] # value = + PhasePolyBox: typing.ClassVar[OpType] # value = PhasedISWAP: typing.ClassVar[OpType] # value = PhasedX: typing.ClassVar[OpType] # value = - QControlBox: typing.ClassVar[OpType] # value = + QControlBox: typing.ClassVar[OpType] # value = RangePredicate: typing.ClassVar[OpType] # value = Reset: typing.ClassVar[OpType] # value = Rx: typing.ClassVar[OpType] # value = @@ -3371,21 +3377,21 @@ class OpType: SXdg: typing.ClassVar[OpType] # value = Sdg: typing.ClassVar[OpType] # value = SetBits: typing.ClassVar[OpType] # value = - StatePreparationBox: typing.ClassVar[OpType] # value = + StatePreparationBox: typing.ClassVar[OpType] # value = Stop: typing.ClassVar[OpType] # value = Sycamore: typing.ClassVar[OpType] # value = T: typing.ClassVar[OpType] # value = TK1: typing.ClassVar[OpType] # value = TK2: typing.ClassVar[OpType] # value = Tdg: typing.ClassVar[OpType] # value = - TermSequenceBox: typing.ClassVar[OpType] # value = - ToffoliBox: typing.ClassVar[OpType] # value = + TermSequenceBox: typing.ClassVar[OpType] # value = + ToffoliBox: typing.ClassVar[OpType] # value = U1: typing.ClassVar[OpType] # value = U2: typing.ClassVar[OpType] # value = U3: typing.ClassVar[OpType] # value = - Unitary1qBox: typing.ClassVar[OpType] # value = - Unitary2qBox: typing.ClassVar[OpType] # value = - Unitary3qBox: typing.ClassVar[OpType] # value = + Unitary1qBox: typing.ClassVar[OpType] # value = + Unitary2qBox: typing.ClassVar[OpType] # value = + Unitary3qBox: typing.ClassVar[OpType] # value = V: typing.ClassVar[OpType] # value = Vdg: typing.ClassVar[OpType] # value = WASM: typing.ClassVar[OpType] # value = @@ -3397,7 +3403,7 @@ class OpType: Z: typing.ClassVar[OpType] # value = ZZMax: typing.ClassVar[OpType] # value = ZZPhase: typing.ClassVar[OpType] # value = - __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'GPI': , 'GPI2': , 'AAMS': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRy': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } + __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'GPI': , 'GPI2': , 'AAMS': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRx': , 'CnRy': , 'CnRz': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } noop: typing.ClassVar[OpType] # value = @staticmethod def from_name(arg0: str) -> OpType: diff --git a/pytket/pytket/_tket/passes.pyi b/pytket/pytket/_tket/passes.pyi index a73bbee83c..e945877d50 100644 --- a/pytket/pytket/_tket/passes.pyi +++ b/pytket/pytket/_tket/passes.pyi @@ -291,7 +291,7 @@ def CustomRoutingPass(arc: pytket._tket.architecture.Architecture, config: typin """ def DecomposeArbitrarilyControlledGates() -> BasePass: """ - Decomposes CCX, CnX, CnY, CnZ, and CnRy gates into CX and single-qubit gates. + Decomposes CCX, CnX, CnY, CnZ, CnRy, CnRz and CnRx gates into CX and single-qubit gates. """ def DecomposeBoxes(excluded_types: set[pytket._tket.circuit.OpType] = set(), excluded_opgroups: set[str] = set()) -> BasePass: """ diff --git a/pytket/tests/circuit_test.py b/pytket/tests/circuit_test.py index 2c400b205e..614e34e083 100644 --- a/pytket/tests/circuit_test.py +++ b/pytket/tests/circuit_test.py @@ -1536,6 +1536,21 @@ def test_pickle_bit() -> None: assert b == pickle.loads(pickle.dumps(b)) +def test_cnrx_cnrz() -> None: + c1rx = Circuit(2) + c1rx.add_gate(OpType.CnRx, 0.3, [0, 1]) + crx = Circuit(2) + crx.add_gate(OpType.CRx, 0.3, [0, 1]) + + c1rz = Circuit(2) + c1rz.add_gate(OpType.CnRz, 0.3, [0, 1]) + crz = Circuit(2) + crz.add_gate(OpType.CRz, 0.3, [0, 1]) + + assert np.allclose(c1rz.get_unitary(), crz.get_unitary()) + assert np.allclose(c1rx.get_unitary(), crx.get_unitary()) + + if __name__ == "__main__": test_circuit_gen() test_symbolic_ops() @@ -1551,3 +1566,4 @@ def test_pickle_bit() -> None: test_multi_controlled_gates() test_counting_n_qubit_gates() test_pauliexp_pair_box_serialisation() + test_cnrx_cnrz() diff --git a/tket/conanfile.py b/tket/conanfile.py index cc056cc54d..c53a41f49a 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.0" + version = "1.3.1" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 33de57c09e..f84346bf20 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -507,6 +507,10 @@ Circuit CnX_gray_decomp(unsigned n); Circuit CnRy_normal_decomp(const Op_ptr op, unsigned arity); +Circuit CnRx_normal_decomp(const Op_ptr op, unsigned arity); + +Circuit CnRz_normal_decomp(const Op_ptr op, unsigned arity); + /** * @brief Given a 2x2 numerical unitary matrix U and the number of control * qubits n return the decomposed CnU gate diff --git a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp index e04e14998d..248599cf1e 100644 --- a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp +++ b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp @@ -117,6 +117,10 @@ struct GateUnitaryMatrixImplementations { // to have a sparse version. static Eigen::MatrixXcd CnRy(unsigned int number_of_qubits, double alpha); + static Eigen::MatrixXcd CnRx(unsigned int number_of_qubits, double alpha); + + static Eigen::MatrixXcd CnRz(unsigned int number_of_qubits, double alpha); + static Eigen::MatrixXcd CnX(unsigned int number_of_qubits); static Eigen::MatrixXcd CnZ(unsigned int number_of_qubits); diff --git a/tket/include/tket/OpType/OpType.hpp b/tket/include/tket/OpType/OpType.hpp index 6be6ed96d5..e1b6c8fa5a 100644 --- a/tket/include/tket/OpType/OpType.hpp +++ b/tket/include/tket/OpType/OpType.hpp @@ -608,6 +608,20 @@ enum class OpType { */ CnRy, + /** + * Multiply-controlled \ref OpType::Rx + * + * The phase parameter is defined modulo \f$ 4 \f$. + */ + CnRx, + + /** + * Multiply-controlled \ref OpType::Rz + * + * The phase parameter is defined modulo \f$ 4 \f$. + */ + CnRz, + /** * Multiply-controlled \ref OpType::X */ diff --git a/tket/include/tket/Transformations/Decomposition.hpp b/tket/include/tket/Transformations/Decomposition.hpp index 91af43f824..ba44f8c172 100644 --- a/tket/include/tket/Transformations/Decomposition.hpp +++ b/tket/include/tket/Transformations/Decomposition.hpp @@ -241,7 +241,7 @@ Transform decomp_CCX(); Transform decomp_controlled_Rys(); // does not use ancillae -// Expects: CCX, CnX, CnY, CnZ, CnRy and any other gates +// Expects: CCX, CnX, CnY, CnZ, CnRy, CnRx, CnRz, and any other gates // returns CX and single-qubit gate + any previous gates Transform decomp_arbitrary_controlled_gates(); diff --git a/tket/include/tket/Transformations/Replacement.hpp b/tket/include/tket/Transformations/Replacement.hpp index 3018387424..f41a4ade07 100644 --- a/tket/include/tket/Transformations/Replacement.hpp +++ b/tket/include/tket/Transformations/Replacement.hpp @@ -54,7 +54,8 @@ Circuit CX_circ_from_multiq(const Op_ptr op); Circuit CX_ZX_circ_from_op(const Op_ptr op); /** - * Replace CnRy, CnX, CnZ, CnY with 2-qubit gates and single qubit gates + * Replace CnRy, CnRx, CnRz, CnX, CnZ, CnY with 2-qubit gates and single qubit + * gates * * @param op operation * @param two_q_type whether rebase 2-q gates to CX or TK2 diff --git a/tket/src/Circuit/CircUtils.cpp b/tket/src/Circuit/CircUtils.cpp index fe23674bc5..c97cb45f26 100644 --- a/tket/src/Circuit/CircUtils.cpp +++ b/tket/src/Circuit/CircUtils.cpp @@ -622,6 +622,8 @@ Circuit with_CX(Gate_ptr op) { #define CNZTYPE(n) (((n) == 2) ? OpType::CZ : OpType::CnZ) #define CNYTYPE(n) (((n) == 2) ? OpType::CY : OpType::CnY) #define CNRYTYPE(n) (((n) == 2) ? OpType::CRy : OpType::CnRy) +#define CNRXTYPE(n) (((n) == 2) ? OpType::CRx : OpType::CnRx) +#define CNRZTYPE(n) (((n) == 2) ? OpType::CRz : OpType::CnRz) /** * Construct a circuit representing CnU1. */ @@ -692,12 +694,13 @@ static Circuit with_controls_symbolic(const Circuit &c, unsigned n_controls) { } static const OpTypeSet multiq_gate_set = { - OpType::CX, OpType::CCX, OpType::CnX, OpType::CRy, OpType::CnRy, - OpType::CZ, OpType::CnZ, OpType::CY, OpType::CnY}; + OpType::CX, OpType::CCX, OpType::CnX, OpType::CRy, + OpType::CnRy, OpType::CZ, OpType::CnZ, OpType::CY, + OpType::CnY, OpType::CnRx, OpType::CnRz}; unsigned c_n_qubits = c1.n_qubits(); - // 1. Rebase to {CX, CCX, CnX, CnRy} and single-qubit gates + // 1. Rebase to {CX, CCX, CnX, CnRy, CnRx, CnRz} and single-qubit gates VertexList bin; BGL_FORALL_VERTICES(v, c1.dag, DAG) { Op_ptr op = c1.get_Op_ptr_from_Vertex(v); @@ -778,6 +781,16 @@ static Circuit with_controls_symbolic(const Circuit &c, unsigned n_controls) { case OpType::CnRy: c2.add_op(CNRYTYPE(n_new_args), params, new_args); break; + case OpType::Rx: + case OpType::CRx: + case OpType::CnRx: + c2.add_op(CNRXTYPE(n_new_args), params, new_args); + break; + case OpType::Rz: + case OpType::CRz: + case OpType::CnRz: + c2.add_op(CNRZTYPE(n_new_args), params, new_args); + break; case OpType::Z: case OpType::CZ: case OpType::CnZ: @@ -843,6 +856,7 @@ static Eigen::Matrix2cd get_target_op_matrix(const Op_ptr &op) { return Gate(OpType::V, {}, 1).get_unitary(); case OpType::CVdg: return Gate(OpType::Vdg, {}, 1).get_unitary(); + case OpType::CnRx: case OpType::CRx: return Gate(OpType::Rx, op->get_params(), 1).get_unitary(); case OpType::CnRy: @@ -851,6 +865,7 @@ static Eigen::Matrix2cd get_target_op_matrix(const Op_ptr &op) { case OpType::CY: case OpType::CnY: return Gate(OpType::Y, {}, 1).get_unitary(); + case OpType::CnRz: case OpType::CRz: return Gate(OpType::Rz, op->get_params(), 1).get_unitary(); case OpType::CZ: @@ -1248,6 +1263,8 @@ Circuit with_controls(const Circuit &c, unsigned n_controls) { #undef CNZTYPE #undef CNYTYPE #undef CNRYTYPE +#undef CNRXTYPE +#undef CNRZTYPE std::tuple, Circuit> normalise_TK2_angles( Expr a, Expr b, Expr c) { diff --git a/tket/src/Circuit/ControlledGates.cpp b/tket/src/Circuit/ControlledGates.cpp index f06790914f..f1f648d7a4 100644 --- a/tket/src/Circuit/ControlledGates.cpp +++ b/tket/src/Circuit/ControlledGates.cpp @@ -804,6 +804,46 @@ Circuit CnRy_normal_decomp(const Op_ptr op, unsigned arity) { return rep; } +Circuit CnRz_normal_decomp(const Op_ptr op, unsigned arity) { + if (op->get_type() != OpType::CnRz) { + throw CircuitInvalidity("Operation not CnRz"); + } + OpDesc desc = op->get_desc(); + Expr angle = op->get_params()[0]; + Circuit cnry_circuit = + CnRy_normal_decomp(get_op_ptr(OpType::CnRy, angle), arity); + TKET_ASSERT(cnry_circuit.n_qubits() == arity); + // The target to the CnRy gate will be to the qubit indexed as arity-1 + // Therefore we add basis change Clifford gates to this qubit + Circuit rep(arity); + + rep.add_op(OpType::H, {arity - 1}); + rep.add_op(OpType::S, {arity - 1}); + rep.append(cnry_circuit); + rep.add_op(OpType::Sdg, {arity - 1}); + rep.add_op(OpType::H, {arity - 1}); + return rep; +} + +Circuit CnRx_normal_decomp(const Op_ptr op, unsigned arity) { + if (op->get_type() != OpType::CnRx) { + throw CircuitInvalidity("Operation not CnR"); + } + OpDesc desc = op->get_desc(); + Expr angle = op->get_params()[0]; + Circuit cnry_circuit = + CnRy_normal_decomp(get_op_ptr(OpType::CnRy, angle), arity); + TKET_ASSERT(cnry_circuit.n_qubits() == arity); + // The target to the CnRy gate will be to the qubit indexed as arity-1 + // Therefore we add basis change Clifford gates to this qubit + Circuit rep(arity); + + rep.add_op(OpType::S, {arity - 1}); + rep.append(cnry_circuit); + rep.add_op(OpType::Sdg, {arity - 1}); + return rep; +} + // decompose CnX gate using lemma 7.1 // `n` = no. of controls Circuit CnX_gray_decomp(unsigned n) { diff --git a/tket/src/Circuit/basic_circ_manip.cpp b/tket/src/Circuit/basic_circ_manip.cpp index d00d39f929..49749eb4a1 100644 --- a/tket/src/Circuit/basic_circ_manip.cpp +++ b/tket/src/Circuit/basic_circ_manip.cpp @@ -108,6 +108,10 @@ Vertex Circuit::add_op( } if (optype == OpType::CnRy && args.size() == 1) { return add_op(get_op_ptr(OpType::Ry, gate->get_params()), arg_ids); + } else if (optype == OpType::CnRx && args.size() == 1) { + return add_op(get_op_ptr(OpType::Rx, gate->get_params()), arg_ids); + } else if (optype == OpType::CnRz && args.size() == 1) { + return add_op(get_op_ptr(OpType::Rz, gate->get_params()), arg_ids); } else if (optype == OpType::CnX && args.size() == 1) { return add_op(get_op_ptr(OpType::X), arg_ids); } else if (optype == OpType::CnZ && args.size() == 1) { diff --git a/tket/src/Circuit/latex_drawing.cpp b/tket/src/Circuit/latex_drawing.cpp index 56fab702bc..2949db930f 100644 --- a/tket/src/Circuit/latex_drawing.cpp +++ b/tket/src/Circuit/latex_drawing.cpp @@ -37,6 +37,8 @@ void add_latex_for_command(LatexContext& context, const Command& command) { unit_vector_t args = command.get_args(); const Op_ptr op = command.get_op_ptr(); switch (op->get_type()) { + case OpType::CnRz: + case OpType::CnRx: case OpType::CnRy: case OpType::CnX: case OpType::CnY: @@ -54,6 +56,16 @@ void add_latex_for_command(LatexContext& context, const Command& command) { << "\\gate{\\text{" << get_op_ptr(OpType::Ry, op->get_params())->get_name(true) << "}} & "; + } else if (op->get_type() == OpType::CnRx) { + lines.at(target_index).buffer + << "\\gate{\\text{" + << get_op_ptr(OpType::Rx, op->get_params())->get_name(true) + << "}} & "; + } else if (op->get_type() == OpType::CnRz) { + lines.at(target_index).buffer + << "\\gate{\\text{" + << get_op_ptr(OpType::Rz, op->get_params())->get_name(true) + << "}} & "; } else if (op->get_type() == OpType::CnX) { lines.at(target_index).buffer << "\\targ{} & "; } else if (op->get_type() == OpType::CnY) { diff --git a/tket/src/Gate/Gate.cpp b/tket/src/Gate/Gate.cpp index 6dc93da255..5c6f24958d 100644 --- a/tket/src/Gate/Gate.cpp +++ b/tket/src/Gate/Gate.cpp @@ -110,6 +110,8 @@ Op_ptr Gate::dagger() const { case OpType::Rx: case OpType::PhaseGadget: case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::XXPhase: case OpType::YYPhase: case OpType::ZZPhase: @@ -215,6 +217,8 @@ Op_ptr Gate::transpose() const { case OpType::SXdg: case OpType::CRz: case OpType::CRx: + case OpType::CnRz: + case OpType::CnRx: case OpType::CU1: case OpType::U1: case OpType::Rz: @@ -438,7 +442,9 @@ std::optional Gate::is_identity() const { case OpType::CRy: case OpType::PhaseGadget: case OpType::ISWAP: - case OpType::CnRy: { + case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: { return equiv_0(params[0], 4) ? 0. : notid; } case OpType::FSim: { @@ -575,6 +581,8 @@ bool Gate::has_symmetry(unsigned port1, unsigned port2) const { // n (+1) qubit gates case OpType::CnX: case OpType::CnY: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnRy: { // symmetry on first n ports not on n+1 auto last_port = n_q - 1; @@ -863,6 +871,7 @@ std::optional Gate::commuting_basis(unsigned i) const { } case OpType::CZ: case OpType::CRz: + case OpType::CnRz: case OpType::CS: case OpType::CSdg: case OpType::CU1: @@ -901,6 +910,7 @@ std::optional Gate::commuting_basis(unsigned i) const { case OpType::CSX: case OpType::CSXdg: case OpType::CRx: + case OpType::CnRx: case OpType::CX: case OpType::CCX: case OpType::CnX: { diff --git a/tket/src/Gate/GateUnitaryMatrixComposites.cpp b/tket/src/Gate/GateUnitaryMatrixComposites.cpp index 1ab23a34de..e88736ce15 100644 --- a/tket/src/Gate/GateUnitaryMatrixComposites.cpp +++ b/tket/src/Gate/GateUnitaryMatrixComposites.cpp @@ -105,6 +105,17 @@ Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRy( Ry(alpha), number_of_qubits); } +Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRx( + unsigned int number_of_qubits, double alpha) { + return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( + Rx(alpha), number_of_qubits); +} +Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRz( + unsigned int number_of_qubits, double alpha) { + return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( + Rz(alpha), number_of_qubits); +} + Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnX( unsigned int number_of_qubits) { return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( diff --git a/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp b/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp index d8902e5e3b..dea7ec852a 100644 --- a/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp +++ b/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp @@ -26,6 +26,8 @@ GateUnitaryMatrixVariableQubits::GateUnitaryMatrixVariableQubits( : op_type(op_type_), known_type(true), number_of_parameters(0) { switch (op_type) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: // Fall through. case OpType::PhaseGadget: number_of_parameters = 1; @@ -71,6 +73,12 @@ Eigen::MatrixXcd GateUnitaryMatrixVariableQubits::get_dense_unitary( if (op_type == OpType::CnRy) { return GateUnitaryMatrixImplementations::CnRy( number_of_qubits, parameters[0]); + } else if (op_type == OpType::CnRx) { + return GateUnitaryMatrixImplementations::CnRx( + number_of_qubits, parameters[0]); + } else if (op_type == OpType::CnRz) { + return GateUnitaryMatrixImplementations::CnRz( + number_of_qubits, parameters[0]); } else { TKET_ASSERT(op_type == OpType::PhaseGadget); return GateUnitaryMatrixImplementations::PhaseGadget( diff --git a/tket/src/Gate/GateUnitarySparseMatrix.cpp b/tket/src/Gate/GateUnitarySparseMatrix.cpp index cf25f6bed2..549d28d51e 100644 --- a/tket/src/Gate/GateUnitarySparseMatrix.cpp +++ b/tket/src/Gate/GateUnitarySparseMatrix.cpp @@ -47,6 +47,12 @@ static OpType get_primitive_type(OpType type_without_controls) { case OpType::CnRy: return OpType::Ry; + case OpType::CnRx: + return OpType::Rx; + + case OpType::CnRz: + return OpType::Rz; + default: return OpType::noop; } diff --git a/tket/src/OpType/OpTypeFunctions.cpp b/tket/src/OpType/OpTypeFunctions.cpp index adb5cae654..5a564c3171 100644 --- a/tket/src/OpType/OpTypeFunctions.cpp +++ b/tket/src/OpType/OpTypeFunctions.cpp @@ -26,23 +26,29 @@ bool find_in_set(const OpType& val, const OpTypeSet& set) { const OpTypeSet& all_gate_types() { static const OpTypeSet optypes{ - OpType::Z, OpType::X, OpType::Y, OpType::S, - OpType::Sdg, OpType::T, OpType::Tdg, OpType::V, - OpType::Vdg, OpType::SX, OpType::SXdg, OpType::H, - OpType::Rx, OpType::Ry, OpType::Rz, OpType::U3, - OpType::U2, OpType::U1, OpType::TK1, OpType::CX, - OpType::CY, OpType::CZ, OpType::CH, OpType::CV, - OpType::CVdg, OpType::CSX, OpType::CSXdg, OpType::CS, - OpType::CSdg, OpType::CRz, OpType::CRx, OpType::CRy, - OpType::CU1, OpType::CU3, OpType::PhaseGadget, OpType::CCX, - OpType::SWAP, OpType::CSWAP, OpType::noop, OpType::Measure, - OpType::Reset, OpType::ECR, OpType::ISWAP, OpType::PhasedX, - OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, - OpType::CnRy, OpType::CnX, OpType::CnZ, OpType::CnY, - OpType::BRIDGE, OpType::Collapse, OpType::ESWAP, OpType::FSim, - OpType::Sycamore, OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2, OpType::Phase, OpType::GPI, - OpType::GPI2, OpType::AAMS}; + OpType::Z, OpType::X, OpType::Y, + OpType::S, OpType::Sdg, OpType::T, + OpType::Tdg, OpType::V, OpType::Vdg, + OpType::SX, OpType::SXdg, OpType::H, + OpType::Rx, OpType::Ry, OpType::Rz, + OpType::U3, OpType::U2, OpType::U1, + OpType::TK1, OpType::CX, OpType::CY, + OpType::CZ, OpType::CH, OpType::CV, + OpType::CVdg, OpType::CSX, OpType::CSXdg, + OpType::CS, OpType::CSdg, OpType::CRz, + OpType::CRx, OpType::CRy, OpType::CU1, + OpType::CU3, OpType::PhaseGadget, OpType::CCX, + OpType::SWAP, OpType::CSWAP, OpType::noop, + OpType::Measure, OpType::Reset, OpType::ECR, + OpType::ISWAP, OpType::PhasedX, OpType::ZZMax, + OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, + OpType::CnRx, OpType::CnRz, OpType::CnRy, + OpType::CnX, OpType::CnZ, OpType::CnY, + OpType::BRIDGE, OpType::Collapse, OpType::ESWAP, + OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, + OpType::PhasedISWAP, OpType::XXPhase3, OpType::NPhasedX, + OpType::TK2, OpType::Phase, OpType::GPI, + OpType::GPI2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -50,19 +56,20 @@ const OpTypeSet& all_gate_types() { const OpTypeSet& all_multi_qubit_types() { static const OpTypeSet optypes{ - OpType::CX, OpType::CY, OpType::CZ, - OpType::CH, OpType::CV, OpType::CVdg, - OpType::CSX, OpType::CSXdg, OpType::CS, - OpType::CSdg, OpType::CRz, OpType::CRx, - OpType::CRy, OpType::CU1, OpType::CU3, - OpType::PhaseGadget, OpType::CCX, OpType::SWAP, - OpType::CSWAP, OpType::ECR, OpType::ISWAP, - OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, - OpType::ZZPhase, OpType::CnRy, OpType::CnX, - OpType::CnZ, OpType::CnY, OpType::BRIDGE, - OpType::ESWAP, OpType::FSim, OpType::Sycamore, - OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2, OpType::AAMS}; + OpType::CX, OpType::CY, OpType::CZ, + OpType::CH, OpType::CV, OpType::CVdg, + OpType::CSX, OpType::CSXdg, OpType::CS, + OpType::CSdg, OpType::CRz, OpType::CRx, + OpType::CRy, OpType::CU1, OpType::CU3, + OpType::PhaseGadget, OpType::CCX, OpType::SWAP, + OpType::CSWAP, OpType::ECR, OpType::ISWAP, + OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, + OpType::ZZPhase, OpType::CnRx, OpType::CnRz, + OpType::CnRy, OpType::CnX, OpType::CnZ, + OpType::CnY, OpType::BRIDGE, OpType::ESWAP, + OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, + OpType::PhasedISWAP, OpType::XXPhase3, OpType::NPhasedX, + OpType::TK2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -120,8 +127,9 @@ const OpTypeSet& all_controlled_gate_types() { static const OpTypeSet optypes{ OpType::CX, OpType::CCX, OpType::CnX, OpType::CnZ, OpType::CnY, OpType::CSX, OpType::CSXdg, OpType::CS, OpType::CSdg, OpType::CV, - OpType::CVdg, OpType::CRx, OpType::CnRy, OpType::CRy, OpType::CY, - OpType::CRz, OpType::CZ, OpType::CH, OpType::CU1, OpType::CU3}; + OpType::CVdg, OpType::CRx, OpType::CnRx, OpType::CnRz, OpType::CnRy, + OpType::CRy, OpType::CY, OpType::CRz, OpType::CZ, OpType::CH, + OpType::CU1, OpType::CU3}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -214,10 +222,11 @@ bool is_flowop_type(OpType optype) { bool is_rotation_type(OpType optype) { static const OpTypeSet rotation_gates = { - OpType::Rx, OpType::Ry, OpType::Rz, OpType::U1, - OpType::CnRy, OpType::CRz, OpType::CRx, OpType::CRy, - OpType::CU1, OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, - OpType::ESWAP, OpType::ISWAP, OpType::XXPhase3}; + OpType::Rx, OpType::Ry, OpType::Rz, OpType::U1, + OpType::CnRy, OpType::CnRx, OpType::CnRz, OpType::CRz, + OpType::CRx, OpType::CRy, OpType::CU1, OpType::XXPhase, + OpType::YYPhase, OpType::ZZPhase, OpType::ESWAP, OpType::ISWAP, + OpType::XXPhase3}; return find_in_set(optype, rotation_gates); } diff --git a/tket/src/OpType/OpTypeInfo.cpp b/tket/src/OpType/OpTypeInfo.cpp index a2605edd20..19e292bb70 100644 --- a/tket/src/OpType/OpTypeInfo.cpp +++ b/tket/src/OpType/OpTypeInfo.cpp @@ -123,6 +123,8 @@ const std::map& optypeinfo() { {OpType::XXPhase3, {"XXPhase3", "$R_{X_0X_1}R_{X_0X_2}R_{X_1X_2}$", {4}, tripleq}}, {OpType::CnRy, {"CnRy", "CnRy", {4}, std::nullopt}}, + {OpType::CnRx, {"CnRx", "CnRx", {4}, std::nullopt}}, + {OpType::CnRz, {"CnRz", "CnRz", {4}, std::nullopt}}, {OpType::CnX, {"CnX", "CnX", {}, std::nullopt}}, {OpType::CnZ, {"CnZ", "CnZ", {}, std::nullopt}}, {OpType::CnY, {"CnY", "CnY", {}, std::nullopt}}, diff --git a/tket/src/Transformations/Decomposition.cpp b/tket/src/Transformations/Decomposition.cpp index 22a25376d1..9e431ffb24 100644 --- a/tket/src/Transformations/Decomposition.cpp +++ b/tket/src/Transformations/Decomposition.cpp @@ -1805,7 +1805,8 @@ Transform decomp_controlled_Rys() { Transform decomp_arbitrary_controlled_gates() { static const std::set cn_gate_set = { - OpType::CCX, OpType::CnX, OpType::CnRy, OpType::CnZ, OpType::CnY}; + OpType::CCX, OpType::CnX, OpType::CnRy, OpType::CnZ, + OpType::CnY, OpType::CnRx, OpType::CnRz}; std::set all_gates; std::copy( all_gate_types().begin(), all_gate_types().end(), diff --git a/tket/src/Transformations/Replacement.cpp b/tket/src/Transformations/Replacement.cpp index ceda73f655..8c44674d7f 100644 --- a/tket/src/Transformations/Replacement.cpp +++ b/tket/src/Transformations/Replacement.cpp @@ -37,6 +37,12 @@ Circuit multi_controlled_to_2q( case OpType::CnRy: c = CircPool::CnRy_normal_decomp(op, n_qubits); break; + case OpType::CnRx: + c = CircPool::CnRx_normal_decomp(op, n_qubits); + break; + case OpType::CnRz: + c = CircPool::CnRz_normal_decomp(op, n_qubits); + break; case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -87,6 +93,8 @@ Circuit TK2_circ_from_multiq(const Op_ptr op) { "Can only build replacement circuits for basic gates", desc.type()); switch (desc.type()) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -110,6 +118,8 @@ Circuit CX_circ_from_multiq(const Op_ptr op) { "Can only build replacement circuits for basic gates", desc.type()); switch (desc.type()) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -288,6 +298,8 @@ Circuit CX_ZX_circ_from_op(const Op_ptr op) { case OpType::ZZPhase: case OpType::YYPhase: case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::ESWAP: case OpType::FSim: diff --git a/tket/test/src/Circuit/test_Boxes.cpp b/tket/test/src/Circuit/test_Boxes.cpp index bfaed1f186..d60824782e 100644 --- a/tket/test/src/Circuit/test_Boxes.cpp +++ b/tket/test/src/Circuit/test_Boxes.cpp @@ -933,17 +933,22 @@ SCENARIO("QControlBox", "[boxes]") { REQUIRE(*c == d); } - GIVEN("controlled phase_gadget") { + GIVEN("controlled phase_gadget, numerical") { Expr a; - WHEN("numerical") { a = 0.3; } - WHEN("symbolic") { - Sym s = SymEngine::symbol("a"); - a = Expr(s); - } + a = 0.3; QControlBox qbox(get_op_ptr(OpType::PhaseGadget, {a}, 2)); std::shared_ptr c = qbox.to_circuit(); REQUIRE(c->count_gates(OpType::CX) == 4); } + GIVEN("controlled phase_gadget, symbolic") { + Expr a; + Sym s = SymEngine::symbol("a"); + a = Expr(s); + QControlBox qbox(get_op_ptr(OpType::PhaseGadget, {a}, 2)); + std::shared_ptr c = qbox.to_circuit(); + REQUIRE(c->count_gates(OpType::CX) == 2); + REQUIRE(c->count_gates(OpType::CRz) == 1); + } GIVEN("controlled PauliExpBox") { // https://github.com/CQCL/tket/issues/1109 PauliExpBox pbox( diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index 5e96517130..d1724bdd13 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -2712,6 +2712,10 @@ SCENARIO("Confirm that LaTeX output compiles", "[latex][.long]") { c.add_conditional_gate(OpType::CnZ, {}, {0, 1, 2, 4, 3}, {}, 0); c.add_conditional_gate( OpType::CnRy, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); + c.add_conditional_gate( + OpType::CnRx, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); + c.add_conditional_gate( + OpType::CnRz, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); c.add_conditional_gate(OpType::CH, {}, {1, 0}, {}, 0); c.add_conditional_gate(OpType::CY, {}, {2, 3}, {}, 0); c.add_conditional_gate(OpType::CRz, {1.42}, {0, 2}, {}, 0); diff --git a/tket/test/src/Ops/test_Ops.cpp b/tket/test/src/Ops/test_Ops.cpp index 6078d78e76..b07654bbd5 100644 --- a/tket/test/src/Ops/test_Ops.cpp +++ b/tket/test/src/Ops/test_Ops.cpp @@ -134,6 +134,16 @@ SCENARIO("Check op retrieval overloads are working correctly.", "[ops]") { CHECK(cnry->get_name() == "CnRy(0.5)"); CHECK(cnry->get_params().size() == 1); REQUIRE(cnry->transpose()->get_params() == rhs); + const Op_ptr cnrx = (get_op_ptr(OpType::CnRx, 0.5)); + + std::vector rhs_same = {Expr(0.5)}; + CHECK(cnrx->get_name() == "CnRx(0.5)"); + CHECK(cnrx->get_params().size() == 1); + REQUIRE(cnrx->transpose()->get_params() == rhs_same); + const Op_ptr cnrz = (get_op_ptr(OpType::CnRz, 0.5)); + CHECK(cnrz->get_name() == "CnRz(0.5)"); + CHECK(cnrz->get_params().size() == 1); + REQUIRE(cnrz->transpose()->get_params() == rhs_same); const Op_ptr xxphase = (get_op_ptr(OpType::XXPhase, 0.5)); CHECK(xxphase->get_name() == "XXPhase(0.5)"); REQUIRE(*xxphase->transpose() == *xxphase); @@ -307,6 +317,8 @@ SCENARIO("Examples for is_singleq_unitary") { } GIVEN("Variable-qubit gates") { REQUIRE(!(get_op_ptr(OpType::CnRy, 0.2))->get_desc().is_singleq_unitary()); + REQUIRE(!(get_op_ptr(OpType::CnRx, 0.2))->get_desc().is_singleq_unitary()); + REQUIRE(!(get_op_ptr(OpType::CnRz, 0.2))->get_desc().is_singleq_unitary()); REQUIRE(!(get_op_ptr(OpType::PhaseGadget, Expr(0.4))) ->get_desc() .is_singleq_unitary()); diff --git a/tket/test/src/Passes/test_SynthesiseTK.cpp b/tket/test/src/Passes/test_SynthesiseTK.cpp index eb49eb03ca..b41c2a1a7d 100644 --- a/tket/test/src/Passes/test_SynthesiseTK.cpp +++ b/tket/test/src/Passes/test_SynthesiseTK.cpp @@ -90,6 +90,8 @@ SCENARIO("SynthesiseTK correctness") { c.add_op(OpType::CCX, {0, 1, 2}); c.add_op(OpType::CnX, {0, 1, 2, 3}); c.add_op(OpType::CnRy, 0.25, {0, 1, 2, 3}); + c.add_op(OpType::CnRx, 0.25, {0, 1, 2, 3}); + c.add_op(OpType::CnRz, 0.25, {0, 1, 2, 3}); c.add_op(OpType::CSWAP, {1, 2, 3}); c.add_op(OpType::BRIDGE, {1, 3, 0}); check_synthesise_tk(c); @@ -119,6 +121,8 @@ SCENARIO("SynthesiseTK correctness") { c.add_op(OpType::PhasedX, {a, a + 0.2}, {0}); c.add_op(OpType::NPhasedX, {a + 0.3, a}, {0, 1, 2}); c.add_op(OpType::CnRy, a - 0.3, {1, 2, 0}); + c.add_op(OpType::CnRx, a - 0.3, {1, 2, 0}); + c.add_op(OpType::CnRz, a - 0.3, {1, 2, 0}); c.add_op(OpType::ESWAP, a, {1, 2}); c.add_op(OpType::FSim, {a + 0.1, a + 0.2}, {2, 0}); check_synthesise_tk(c); diff --git a/tket/test/src/Simulation/test_CircuitSimulator.cpp b/tket/test/src/Simulation/test_CircuitSimulator.cpp index f65bc069d7..a0355b0bf8 100644 --- a/tket/test/src/Simulation/test_CircuitSimulator.cpp +++ b/tket/test/src/Simulation/test_CircuitSimulator.cpp @@ -243,6 +243,8 @@ SCENARIO("Directly simulate circuits with >= 3 qubit gates") { circ.add_op(OpType::BRIDGE, {0, 1, 2}); circ.add_op(OpType::CSWAP, {0, 1, 2}); circ.add_op(OpType::CnRy, 0.1234, {0, 1, 2, 3}); + circ.add_op(OpType::CnRx, 0.1234, {0, 1, 2, 3}); + circ.add_op(OpType::CnRz, 0.1234, {0, 1, 2, 3}); circ.add_op(OpType::CnX, {0, 1, 2, 3}); circ.add_op(OpType::PhaseGadget, 0.1, {0, 1, 2, 3}); const auto u = tket_sim::get_unitary(circ); diff --git a/tket/test/src/test_ControlDecomp.cpp b/tket/test/src/test_ControlDecomp.cpp index 3c61b5cdaf..9200554cd4 100644 --- a/tket/test/src/test_ControlDecomp.cpp +++ b/tket/test/src/test_ControlDecomp.cpp @@ -780,6 +780,8 @@ SCENARIO("Test decomp_arbitrary_controlled_gates") { Circuit circ(3); circ.add_op(OpType::CnRy, 0.33, {0, 1, 2}); + circ.add_op(OpType::CnRx, 0.33, {0, 1, 2}); + circ.add_op(OpType::CnRz, 0.33, {0, 1, 2}); circ.add_op(OpType::CnY, {0, 1, 2}); circ.add_op(OpType::CnZ, {1, 0, 2}); circ.add_op(OpType::CnX, {0, 2, 1}); @@ -787,6 +789,8 @@ SCENARIO("Test decomp_arbitrary_controlled_gates") { auto u = tket_sim::get_unitary(circ); REQUIRE(Transforms::decomp_arbitrary_controlled_gates().apply(circ)); REQUIRE(circ.count_gates(OpType::CnRy) == 0); + REQUIRE(circ.count_gates(OpType::CnRx) == 0); + REQUIRE(circ.count_gates(OpType::CnRz) == 0); REQUIRE(circ.count_gates(OpType::CnY) == 0); REQUIRE(circ.count_gates(OpType::CnZ) == 0); REQUIRE(circ.count_gates(OpType::CnX) == 0); diff --git a/tket/test/src/test_Synthesis.cpp b/tket/test/src/test_Synthesis.cpp index 124d92c85a..6c2138b326 100644 --- a/tket/test/src/test_Synthesis.cpp +++ b/tket/test/src/test_Synthesis.cpp @@ -1646,16 +1646,20 @@ SCENARIO("Test barrier blocks transforms successfully") { GIVEN("Controlled gates with barrier") { Circuit circ(8); circ.add_op(OpType::CnRy, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRx, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRz, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); circ.add_op(OpType::CX, {6, 7}); circ.add_barrier({0, 1, 2, 3}); circ.add_op(OpType::CX, {6, 7}); + circ.add_op(OpType::CnRz, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRx, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); circ.add_op(OpType::CnRy, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); REQUIRE(verify_n_qubits_for_ops(circ)); - REQUIRE(circ.n_gates() == 5); + REQUIRE(circ.n_gates() == 9); REQUIRE(Transforms::remove_redundancies().apply(circ)); REQUIRE(verify_n_qubits_for_ops(circ)); REQUIRE(circ.depth_by_type(OpType::Barrier) == 1); - REQUIRE(circ.n_gates() == 3); // both CXs removed + REQUIRE(circ.n_gates() == 7); // both CXs removed Circuit rep(4); const Op_ptr bar = std::make_shared(op_signature_t(4, EdgeType::Quantum)); @@ -2201,6 +2205,8 @@ SCENARIO("Synthesis with conditional gates") { c.add_measure(1, 1); c.add_conditional_gate(OpType::U1, {0.25}, {1}, {0}, 1); c.add_conditional_gate(OpType::CnRy, {0.25}, {0, 1, 2}, {0, 1}, 0); + c.add_conditional_gate(OpType::CnRx, {0.25}, {0, 1, 2}, {0, 1}, 0); + c.add_conditional_gate(OpType::CnRz, {0.25}, {0, 1, 2}, {0, 1}, 0); c.add_measure(2, 2); check_conditions(SynthesiseOQC(), c); check_conditions(SynthesiseTK(), c); diff --git a/tket/test/src/test_json.cpp b/tket/test/src/test_json.cpp index 177ab0f177..238c1449ae 100644 --- a/tket/test/src/test_json.cpp +++ b/tket/test/src/test_json.cpp @@ -132,6 +132,8 @@ SCENARIO("Test Command serialization") { const Qubit a = Qubit("a", 1, 2); c.add_qubit(a); c.add_op(OpType::CnRy, 0.1, {q[0], a, q[1]}); + c.add_op(OpType::CnRx, 0.1, {q[0], a, q[1]}); + c.add_op(OpType::CnRz, 0.1, {q[0], a, q[1]}); c.add_barrier({q[0], a}); check_cases(c.get_commands());