diff --git a/pytket/binders/circuit/Circuit/add_op.cpp b/pytket/binders/circuit/Circuit/add_op.cpp index e9da0cfb32..e19fa42e69 100644 --- a/pytket/binders/circuit/Circuit/add_op.cpp +++ b/pytket/binders/circuit/Circuit/add_op.cpp @@ -173,6 +173,28 @@ void init_circuit_add_op(py::class_> &c) { "\n\n:param data: additional data stored in the barrier" "\n:return: the new :py:class:`Circuit`", py::arg("qubits"), py::arg("bits") = no_bits, py::arg("data") = "") + .def( + "add_conditional_barrier", + [](Circuit *circ, const std::vector &barrier_qubits, + const std::vector &barrier_bits, + const std::vector &condition_bits, unsigned value, + const std::string &_data) { + circ->add_conditional_barrier( + barrier_qubits, barrier_bits, condition_bits, value, _data); + return circ; + }, + "Append a Conditional Barrier on the given barrier qubits and " + "barrier bits, conditioned on the given condition bits." + "\n\n:param barrier_qubits: Qubit in Barrier operation." + "\n:param barrier_bits: Bit in Barrier operation." + "\n:param condition_bits: Bit covering classical control condition " + "of barrier operation." + "\n:param value: Value that classical condition must have to " + "hold (little-endian)." + "\n:param data: Additional data stored in Barrier operation." + "\n:return: the new :py:class:`Circuit`", + py::arg("barrier_qubits"), py::arg("barrier_bits"), + py::arg("condition_bits"), py::arg("value"), py::arg("data") = "") .def( "add_circbox", [](Circuit *circ, const CircBox &box, @@ -415,6 +437,26 @@ void init_circuit_add_op(py::class_> &c) { "\n\n:param data: additional data stored in the barrier" "\n:return: the new :py:class:`Circuit`", py::arg("units"), py::arg("data") = "") + .def( + "add_conditional_barrier", + [](Circuit *circ, const unit_vector_t &barrier_args, + const bit_vector_t &condition_bits, unsigned value, + const std::string &_data) { + circ->add_conditional_barrier( + barrier_args, condition_bits, value, _data); + return circ; + }, + "Append a Conditional Barrier on the given barrier qubits and " + "barrier bits, conditioned on the given condition bits." + "\n\n:param barrier_args: Qubit and Bit in Barrier operation." + "\n:param condition_bits: Bit covering classical control " + " condition of barrier operation." + "\n:param value: Value that classical condition must have to " + "hold (little-endian)." + "\n:param data: Additional data stored in Barrier operation." + "\n:return: the new :py:class:`Circuit`", + py::arg("barrier_args"), py::arg("condition_bits"), py::arg("value"), + py::arg("data") = "") .def( "add_circbox", [](Circuit *circ, const CircBox &box, const unit_vector_t &args, diff --git a/pytket/binders/circuit/main.cpp b/pytket/binders/circuit/main.cpp index d3adbe3009..48936c8435 100644 --- a/pytket/binders/circuit/main.cpp +++ b/pytket/binders/circuit/main.cpp @@ -27,6 +27,7 @@ #include "tket/Gate/Gate.hpp" #include "tket/Gate/OpPtrFunctions.hpp" #include "tket/Gate/SymTable.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/Ops/MetaOp.hpp" #include "tket/Ops/Op.hpp" #include "tket/Utils/Constants.hpp" @@ -615,7 +616,7 @@ PYBIND11_MODULE(circuit, m) { ":return: set of symbolic parameters for the command"); py::class_, Op>( - m, "MetaOp", "Meta operation, for example used as barrier") + m, "MetaOp", "Meta operation, such as input or output vertices.") .def( py::init(), "Construct MetaOp with optype, signature and additional data string" @@ -625,6 +626,17 @@ PYBIND11_MODULE(circuit, m) { py::arg("type"), py::arg("signature"), py::arg("data")) .def_property_readonly("data", &MetaOp::get_data, "Get data from MetaOp"); + py::class_, Op>( + m, "BarrierOp", "Barrier operations.") + .def( + py::init(), + "Construct BarrierOp with signature and additional data string" + "\n:param signature: signature for the op" + "\n:param data: additional string stored in the op", + py::arg("signature"), py::arg("data")) + .def_property_readonly( + "data", &BarrierOp::get_data, "Get data from BarrierOp"); + auto pyCircuit = py::class_>( m, "Circuit", py::dynamic_attr(), "Encapsulates a quantum circuit using a DAG representation.\n\n>>> " diff --git a/pytket/binders/include/add_gate.hpp b/pytket/binders/include/add_gate.hpp index e8ef12b85c..1131d4cdc3 100644 --- a/pytket/binders/include/add_gate.hpp +++ b/pytket/binders/include/add_gate.hpp @@ -27,9 +27,12 @@ static Circuit *add_gate_method( Circuit *circ, const Op_ptr &op, const std::vector &args, const py::kwargs &kwargs) { if (op->get_desc().is_meta()) { + throw CircuitInvalidity("Cannot add metaop to a circuit."); + } + if (op->get_desc().is_barrier()) { throw CircuitInvalidity( - "Cannot add metaop. Please use `add_barrier` to add a " - "barrier."); + "Please use `add_barrier` to add a " + "barrier to a circuit."); } static const std::set allowed_kwargs = { "opgroup", "condition", "condition_bits", "condition_value"}; diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 9eccfb041d..22fe5446e0 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.2.39@tket/stable") + self.requires("tket/1.2.40@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.3@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 189e880fc8..95e2e641ab 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -6,7 +6,8 @@ Unreleased Minor new features: -* Add ``apply_clifford_basis_change_tensor``. +* ``Circuit.add_conditional_barrier`` +* Add ``apply_clifford_basis_change_tensor`` method Fixes: diff --git a/pytket/pytket/_tket/circuit.pyi b/pytket/pytket/_tket/circuit.pyi index 95897c44e8..99237764ac 100644 --- a/pytket/pytket/_tket/circuit.pyi +++ b/pytket/pytket/_tket/circuit.pyi @@ -8,6 +8,11 @@ import pytket._tket.pauli import pytket._tket.unit_id import sympy +class BarrierOp(Op): + def __init__(self, signature: List[EdgeType], data: str) -> None: ... + @property + def data(self) -> str: ... + class BasisOrder: __members__: ClassVar[dict] = ... # read-only __entries: ClassVar[dict] = ... @@ -348,6 +353,10 @@ class Circuit: def add_classicalexpbox_bit(self, expression: object, target: List[pytket._tket.unit_id.Bit], **kwargs: Any) -> Circuit: ... def add_classicalexpbox_register(self, expression: object, target: List[pytket._tket.unit_id.Bit], **kwargs: Any) -> Circuit: ... @overload + def add_conditional_barrier(self, barrier_qubits: List[int], barrier_bits: List[int], condition_bits: List[int], value: int, data: str = ...) -> Circuit: ... + @overload + def add_conditional_barrier(self, barrier_args: List[pytket._tket.unit_id.UnitID], condition_bits: List[pytket._tket.unit_id.Bit], value: int, data: str = ...) -> Circuit: ... + @overload def add_conjugation_box(self, box: ConjugationBox, args: List[pytket._tket.unit_id.UnitID], **kwargs: Any) -> Circuit: ... @overload def add_conjugation_box(self, box: ConjugationBox, args: List[int], **kwargs: Any) -> Circuit: ... diff --git a/pytket/pytket/backends/backend.py b/pytket/pytket/backends/backend.py index d1b2d989c4..c71e155ff8 100644 --- a/pytket/pytket/backends/backend.py +++ b/pytket/pytket/backends/backend.py @@ -109,7 +109,6 @@ def valid_circuit(self, circuit: Circuit) -> bool: def _check_all_circuits( self, circuits: Iterable[Circuit], nomeasure_warn: Optional[bool] = None ) -> bool: - if nomeasure_warn is None: nomeasure_warn = not ( self._supports_state @@ -267,7 +266,6 @@ def process_circuits( valid_check: bool = True, **kwargs: KwargTypes, ) -> List[ResultHandle]: - """ Submit circuits to the backend for running. The results will be stored in the backend's result cache to be retrieved by the corresponding diff --git a/pytket/pytket/circuit/decompose_classical.py b/pytket/pytket/circuit/decompose_classical.py index 43b43c433e..6ab6feab12 100644 --- a/pytket/pytket/circuit/decompose_classical.py +++ b/pytket/pytket/circuit/decompose_classical.py @@ -342,7 +342,7 @@ def _decompose_expressions(circ: Circuit) -> Tuple[Circuit, bool]: modified = True continue if optype == OpType.Barrier: - # add_gate doesn't work for metaops like barrier + # add_gate doesn't work for metaops newcirc.add_barrier(args) else: newcirc.add_gate(op, args, **kwargs) diff --git a/pytket/pytket/qasm/qasm.py b/pytket/pytket/qasm/qasm.py index 55be9dbf0b..735ccaba45 100644 --- a/pytket/pytket/qasm/qasm.py +++ b/pytket/pytket/qasm/qasm.py @@ -54,7 +54,7 @@ MultiBitOp, WASMOp, CustomGate, - MetaOp, + BarrierOp, ) from pytket._tket.unit_id import _TEMP_BIT_NAME from pytket.circuit import ( @@ -362,9 +362,19 @@ def meas(self, tree: List[Token]) -> Iterable[CommandDict]: def barr(self, tree: List[Arg]) -> Iterable[CommandDict]: args = [q for qs in self.unroll_all_args(tree[0]) for q in qs] + signature: List[str] = [] + for arg in args: + if arg[0] in self.c_registers: + signature.append("C") + elif arg[0] in self.q_registers: + signature.append("Q") + else: + raise QASMParseError( + "UnitID " + str(arg) + " in Barrier arguments is not declared." + ) yield { "args": args, - "op": {"signature": ["Q"] * len(args), "type": "Barrier"}, + "op": {"signature": signature, "type": "Barrier"}, } def reset(self, tree: List[Token]) -> Iterable[CommandDict]: @@ -379,7 +389,6 @@ def mixedcall(self, tree: List) -> Iterator[CommandDict]: optoken = next(child_iter) opstr = optoken.value - next_tree = next(child_iter) try: args = next(child_iter) @@ -1321,7 +1330,7 @@ def circuit_to_qasm_io( param = -2 + param params = [param] # type: ignore elif optype == OpType.Barrier and header == "hqslib1_dev": - assert isinstance(op, MetaOp) + assert isinstance(op, BarrierOp) if op.data == "": opstr = _tk_to_qasm_noparams[optype] else: diff --git a/pytket/tests/classical_test.py b/pytket/tests/classical_test.py index 83c32c8954..ea255a2984 100644 --- a/pytket/tests/classical_test.py +++ b/pytket/tests/classical_test.py @@ -494,7 +494,6 @@ def qubit_register( index=strategies.integers(min_value=0, max_value=32), ) def test_registers(reg: Union[BitRegister, QubitRegister], index: int) -> None: - unit_type = Qubit if type(reg) is QubitRegister else Bit if index < reg.size: assert reg[index] == unit_type(reg.name, index) diff --git a/pytket/tests/qasm_test.py b/pytket/tests/qasm_test.py index 7cbf454469..1d92d84334 100644 --- a/pytket/tests/qasm_test.py +++ b/pytket/tests/qasm_test.py @@ -216,6 +216,7 @@ def test_conditional_gates() -> None: circ.Measure(0, 0) circ.Measure(1, 1) circ.Z(0, condition_bits=[0, 1], condition_value=2) + circ.add_conditional_barrier([0, 1], [], [0, 1], 1) circ.Measure(0, 0, condition_bits=[0, 1], condition_value=1) qasm_out = str(curr_file_path / "qasm_test_files/testout5.qasm") circuit_to_qasm(circ, qasm_out) @@ -223,6 +224,21 @@ def test_conditional_gates() -> None: assert circ == c2 +def test_named_conditional_barrier() -> None: + circ = Circuit(2, 2) + circ.add_bit(Bit("test", 3)) + circ.Z(0, condition_bits=[0, 1], condition_value=2) + circ.add_conditional_barrier( + [Qubit("q", 0), Bit("test", 3)], + [Bit("c", 0), Bit("c", 1)], + 0, + data="cond_barrier", + ) + qs_str: str = circuit_to_qasm_str(circ) + c_from_qs: Circuit = circuit_from_qasm_str(qs_str) + assert qs_str == circuit_to_qasm_str(c_from_qs) + + def test_hqs_conditional() -> None: c = Circuit(1) a = c.add_c_register("a", 8) @@ -273,7 +289,6 @@ def test_barrier() -> None: c.H(0) c.H(2) c.add_barrier([0], [0], "comment") - result = """OPENQASM 2.0;\ninclude "hqslib1_dev.inc";\n\nqreg q[3]; creg c[3];\nh q[0];\nh q[2];\ncomment q[0],c[0];\n""" assert result == circuit_to_qasm_str(c, header="hqslib1_dev") @@ -773,6 +788,7 @@ def test_rxxyyzz_conversion() -> None: test_extended_qasm() test_register_commands() test_conditional_gates() + test_named_conditional_barrier() test_hqs_conditional() test_hqs_conditional_params() test_barrier() diff --git a/tket/CMakeLists.txt b/tket/CMakeLists.txt index 0a4bc7e830..79dc867346 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -111,6 +111,7 @@ target_sources(tket src/Clifford/ChoiMixTableau.cpp src/Clifford/SymplecticTableau.cpp src/Clifford/UnitaryTableau.cpp + src/Ops/BarrierOp.cpp src/Ops/FlowOp.cpp src/Ops/MetaOp.cpp src/Ops/Op.cpp @@ -278,6 +279,7 @@ target_sources(tket include/tket/Clifford/SymplecticTableau.hpp include/tket/Clifford/UnitaryTableau.hpp include/tket/Ops/ClassicalOps.hpp + include/tket/Ops/BarrierOp.hpp include/tket/Ops/FlowOp.hpp include/tket/Ops/MetaOp.hpp include/tket/Ops/Op.hpp diff --git a/tket/conanfile.py b/tket/conanfile.py index dd7fbc1ad0..97f9578eb3 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.39" + version = "1.2.40" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Circuit/Circuit.hpp b/tket/include/tket/Circuit/Circuit.hpp index 95fcf6822d..f8f246eb4f 100644 --- a/tket/include/tket/Circuit/Circuit.hpp +++ b/tket/include/tket/Circuit/Circuit.hpp @@ -836,9 +836,9 @@ class Circuit { Vertex add_op( OpType type, const std::vector ¶ms, const std::vector &args, std::optional opgroup = std::nullopt) { - if (is_metaop_type(type)) { + if (is_metaop_type(type) || is_barrier_type(type)) { throw CircuitInvalidity( - "Cannot add metaop. Please use `add_barrier` to add a " + "Cannot add metaop or barrier. Please use `add_barrier` to add a " "barrier."); } return add_op(get_op_ptr(type, params, args.size()), args, opgroup); @@ -904,6 +904,11 @@ class Circuit { if (is_metaop_type(type)) { throw CircuitInvalidity("Cannot add a conditional metaop."); } + if (is_barrier_type(type)) { + throw CircuitInvalidity( + "Please use 'add_conditional_barrier' to add a conditional barrier " + "gate."); + } Op_ptr cond = std::make_shared( get_op_ptr(type, params, (unsigned)args.size()), (unsigned)bits.size(), value); @@ -918,6 +923,18 @@ class Circuit { Vertex add_barrier(const unit_vector_t &args, const std::string &_data = ""); + Vertex add_conditional_barrier( + const std::vector &barrier_qubits, + const std::vector &barrier_bits, + const std::vector &condition_bits, unsigned value, + const std::string &_data, + std::optional opgroup = std::nullopt); + + Vertex add_conditional_barrier( + const unit_vector_t &barrier_args, const bit_vector_t &condition_bits, + unsigned value, const std::string &_data, + std::optional opgroup = std::nullopt); + /** * Add a postfix to a classical register name if the register exists * Example: tket_c results in tket_c_2 if tket_c and tket_c_1 both exist diff --git a/tket/include/tket/Gate/OpPtrFunctions.hpp b/tket/include/tket/Gate/OpPtrFunctions.hpp index 68759ff96b..5d6cbc72eb 100644 --- a/tket/include/tket/Gate/OpPtrFunctions.hpp +++ b/tket/include/tket/Gate/OpPtrFunctions.hpp @@ -25,8 +25,8 @@ namespace tket { * * @param chosen_type operation type * @param param operation parameter - * @param n_qubits number of qubits (only necessary for gates and metaops - * with variable quantum arity) + * @param n_qubits number of qubits (only necessary for gates, barrier + * and metaops with variable quantum arity) */ Op_ptr get_op_ptr(OpType chosen_type, const Expr ¶m, unsigned n_qubits = 0); @@ -35,8 +35,8 @@ Op_ptr get_op_ptr(OpType chosen_type, const Expr ¶m, unsigned n_qubits = 0); * * @param chosen_type operation type * @param params operation parameters - * @param n_qubits number of qubits (only necessary for gates and metaops - * with variable quantum arity) + * @param n_qubits number of qubits (only necessary for gates, barrier + * and metaops with variable quantum arity) */ Op_ptr get_op_ptr( OpType chosen_type, const std::vector ¶ms = {}, diff --git a/tket/include/tket/OpType/OpDesc.hpp b/tket/include/tket/OpType/OpDesc.hpp index 420f0813e7..fa3c34aa61 100644 --- a/tket/include/tket/OpType/OpDesc.hpp +++ b/tket/include/tket/OpType/OpDesc.hpp @@ -61,9 +61,12 @@ class OpDesc { /** Number of classical bits written to */ OptUInt n_classical() const; - /** Whether the 'operation' is actually an input or output or barrier */ + /** Whether the 'operation' is actually an input or output */ bool is_meta() const; + /** Whether the operation is a Barrier */ + bool is_barrier() const; + /** Whether the operation is a box of some kind */ bool is_box() const; @@ -111,6 +114,7 @@ class OpDesc { const OpType type_; const OpTypeInfo info_; const bool is_meta_; + const bool is_barrier_; const bool is_box_; const bool is_gate_; const bool is_flowop_; diff --git a/tket/include/tket/OpType/OpTypeFunctions.hpp b/tket/include/tket/OpType/OpTypeFunctions.hpp index 5f65258bb2..1556ba2dbc 100644 --- a/tket/include/tket/OpType/OpTypeFunctions.hpp +++ b/tket/include/tket/OpType/OpTypeFunctions.hpp @@ -48,9 +48,12 @@ const OpTypeSet &all_controlled_gate_types(); /** Set of all classical gates */ const OpTypeSet &all_classical_types(); -/** Test for initial, final and barrier "ops" */ +/** Test for initial and final "ops" */ bool is_metaop_type(OpType optype); +/** Test for Barrier "ops" */ +bool is_barrier_type(OpType optype); + /** Test for input or creation quantum "ops" */ bool is_initial_q_type(OpType optype); diff --git a/tket/include/tket/Ops/BarrierOp.hpp b/tket/include/tket/Ops/BarrierOp.hpp new file mode 100644 index 0000000000..bae0747dfb --- /dev/null +++ b/tket/include/tket/Ops/BarrierOp.hpp @@ -0,0 +1,59 @@ +// Copyright 2019-2023 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "Op.hpp" +#include "tket/Utils/Json.hpp" + +namespace tket { + +class BarrierOp : public Op { + public: + explicit BarrierOp( + op_signature_t signature = {}, const std::string &_data = ""); + + Op_ptr symbol_substitution( + const SymEngine::map_basic_basic &sub_map) const override; + + SymSet free_symbols() const override; + + unsigned n_qubits() const override; + + op_signature_t get_signature() const override; + + std::string get_data() const { return data_; } + + bool is_clifford() const override; + + ~BarrierOp() override; + + /** + * Equality check between two BarrierOp instances + */ + bool is_equal(const Op &other) const override; + + nlohmann::json serialize() const override; + + static Op_ptr deserialize(const nlohmann::json &j); + + private: + op_signature_t signature_; /**< Types of inputs */ + /** + * additional data given by the user, can be passed on to backend + */ + const std::string data_; +}; + +} // namespace tket diff --git a/tket/include/tket/Ops/MetaOp.hpp b/tket/include/tket/Ops/MetaOp.hpp index 14b89398c9..64b5616889 100644 --- a/tket/include/tket/Ops/MetaOp.hpp +++ b/tket/include/tket/Ops/MetaOp.hpp @@ -30,8 +30,6 @@ class MetaOp : public Op { SymSet free_symbols() const override; - unsigned n_qubits() const override; - op_signature_t get_signature() const override; std::string get_data() const { return data_; } @@ -40,18 +38,6 @@ class MetaOp : public Op { ~MetaOp() override; - /** - * Equality check between two MetaOp instances - */ - bool is_equal(const Op &other) const override; - - nlohmann::json serialize() const override; - - static Op_ptr deserialize(const nlohmann::json &j); - - protected: - MetaOp(); - private: op_signature_t signature_; /**< Types of inputs, when not deducible from op type */ diff --git a/tket/src/Characterisation/FrameRandomisation.cpp b/tket/src/Characterisation/FrameRandomisation.cpp index 5cddd3903c..92bd53a401 100644 --- a/tket/src/Characterisation/FrameRandomisation.cpp +++ b/tket/src/Characterisation/FrameRandomisation.cpp @@ -16,7 +16,7 @@ #include -#include "tket/Ops/MetaOp.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/PauliGraph/ConjugatePauliFunctions.hpp" #include "tket/Utils/PauliStrings.hpp" @@ -97,7 +97,7 @@ void add_noop_frames(std::vector& cycles, Circuit& circ) { full_cycle.add_vertex_pair({input_noop_vert, output_noop_vert}); } std::vector sig(barrier_ins.size(), EdgeType::Quantum); - Op_ptr o_ptr = std::make_shared(OpType::Barrier, sig); + Op_ptr o_ptr = std::make_shared(sig); Vertex input_barrier_vert = circ.add_vertex(o_ptr); Vertex output_barrier_vert = circ.add_vertex(o_ptr); circ.rewire(input_barrier_vert, barrier_ins, sig); diff --git a/tket/src/Circuit/OpJson.cpp b/tket/src/Circuit/OpJson.cpp index 5611585538..46e47f42b2 100644 --- a/tket/src/Circuit/OpJson.cpp +++ b/tket/src/Circuit/OpJson.cpp @@ -17,6 +17,7 @@ #include "tket/Gate/Gate.hpp" #include "tket/OpType/OpType.hpp" #include "tket/OpType/OpTypeFunctions.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/Ops/ClassicalOps.hpp" #include "tket/Ops/MetaOp.hpp" #include "tket/Ops/OpPtr.hpp" @@ -26,8 +27,8 @@ namespace tket { void from_json(const nlohmann::json& j, Op_ptr& op) { OpType optype = j.at("type").get(); - if (is_metaop_type(optype)) { - op = MetaOp::deserialize(j); + if (is_barrier_type(optype)) { + op = BarrierOp::deserialize(j); } else if (is_box_type(optype)) { op = Box::deserialize(j); } else if (optype == OpType::Conditional) { diff --git a/tket/src/Circuit/basic_circ_manip.cpp b/tket/src/Circuit/basic_circ_manip.cpp index 03214dd5d3..029452e026 100644 --- a/tket/src/Circuit/basic_circ_manip.cpp +++ b/tket/src/Circuit/basic_circ_manip.cpp @@ -24,6 +24,7 @@ #include "tket/Circuit/Boxes.hpp" #include "tket/Circuit/Circuit.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/Ops/MetaOp.hpp" namespace tket { @@ -125,7 +126,7 @@ Vertex Circuit::add_barrier( sig.insert(sig.end(), cl_sig.begin(), cl_sig.end()); std::vector args = qubits; args.insert(args.end(), bits.begin(), bits.end()); - return add_op(std::make_shared(OpType::Barrier, sig, _data), args); + return add_op(std::make_shared(sig, _data), args); } Vertex Circuit::add_barrier( @@ -138,7 +139,48 @@ Vertex Circuit::add_barrier( sig.push_back(EdgeType::Classical); } } - return add_op(std::make_shared(OpType::Barrier, sig, _data), args); + return add_op(std::make_shared(sig, _data), args); +} + +Vertex Circuit::add_conditional_barrier( + const std::vector& barrier_qubits, + const std::vector& barrier_bits, + const std::vector& condition_bits, unsigned value, + const std::string& _data, std::optional opgroup) { + op_signature_t sig(barrier_qubits.size(), EdgeType::Quantum); + sig.insert(sig.end(), barrier_bits.size(), EdgeType::Classical); + + std::vector args = condition_bits; + args.insert(args.end(), barrier_qubits.begin(), barrier_qubits.end()); + args.insert(args.end(), barrier_bits.begin(), barrier_bits.end()); + return add_op( + std::make_shared( + std::make_shared(sig, _data), + (unsigned)condition_bits.size(), value), + args, opgroup); +} + +Vertex Circuit::add_conditional_barrier( + const unit_vector_t& barrier_args, const bit_vector_t& condition_bits, + unsigned value, const std::string& _data, + std::optional opgroup) { + op_signature_t sig; + for (const UnitID& arg : barrier_args) { + if (arg.type() == UnitType::Qubit) { + sig.push_back(EdgeType::Quantum); + } else { + TKET_ASSERT(arg.type() == UnitType::Bit); + sig.push_back(EdgeType::Classical); + } + } + unit_vector_t args; + args.insert(args.end(), condition_bits.begin(), condition_bits.end()); + args.insert(args.end(), barrier_args.begin(), barrier_args.end()); + return add_op( + std::make_shared( + std::make_shared(sig, _data), + (unsigned)condition_bits.size(), value), + args, opgroup); } std::string Circuit::get_next_c_reg_name(const std::string& reg_name) { diff --git a/tket/src/Converters/PhasePoly.cpp b/tket/src/Converters/PhasePoly.cpp index 579ff634f0..20f6cf36b7 100644 --- a/tket/src/Converters/PhasePoly.cpp +++ b/tket/src/Converters/PhasePoly.cpp @@ -24,7 +24,6 @@ #include "tket/Circuit/Circuit.hpp" #include "tket/Converters/Gauss.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/Ops/MetaOp.hpp" #include "tket/Ops/OpJsonFactory.hpp" #include "tket/Ops/OpPtr.hpp" #include "tket/Utils/GraphHeaders.hpp" diff --git a/tket/src/Converters/ZXConverters.cpp b/tket/src/Converters/ZXConverters.cpp index 39e8e7c208..3a33eb6c51 100644 --- a/tket/src/Converters/ZXConverters.cpp +++ b/tket/src/Converters/ZXConverters.cpp @@ -311,6 +311,11 @@ BoundaryVertMap circuit_to_zx_recursive( } op = cond.get_op(); } + if (is_barrier_type(op->get_type())) { + throw Unsupported( + "Cannot convert conditional barrier operations to a ZX node. \n"); + } + unsigned port_conditions_size = static_cast(port_conditions.size()); // Convert the underlying op to zx. diff --git a/tket/src/Gate/OpPtrFunctions.cpp b/tket/src/Gate/OpPtrFunctions.cpp index e96423c91c..29fea23f5c 100644 --- a/tket/src/Gate/OpPtrFunctions.cpp +++ b/tket/src/Gate/OpPtrFunctions.cpp @@ -16,6 +16,7 @@ #include "tket/Gate/Gate.hpp" #include "tket/Gate/SymTable.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/Ops/MetaOp.hpp" namespace tket { @@ -29,6 +30,8 @@ Op_ptr get_op_ptr( if (is_gate_type(chosen_type)) { SymTable::register_symbols(expr_free_symbols(params)); return std::make_shared(chosen_type, params, n_qubits); + } else if (is_barrier_type(chosen_type)) { + return std::make_shared(); } else { return std::make_shared(chosen_type); } diff --git a/tket/src/Mapping/MappingFrontier.cpp b/tket/src/Mapping/MappingFrontier.cpp index 1a8f22330e..2d6fa90212 100644 --- a/tket/src/Mapping/MappingFrontier.cpp +++ b/tket/src/Mapping/MappingFrontier.cpp @@ -1011,29 +1011,25 @@ void MappingFrontier::merge_ancilla( bool MappingFrontier::valid_boundary_operation( const ArchitecturePtr& architecture, const Op_ptr& op, const std::vector& uids) const { - // boxes are never allowed OpType ot = op->get_type(); - if (is_box_type(ot)) { - return false; - } if (ot == OpType::Conditional) { Op_ptr cond_op_ptr = static_cast(*op).get_op(); // conditional boxes are never allowed, too - OpType ot = cond_op_ptr->get_type(); + ot = cond_op_ptr->get_type(); while (ot == OpType::Conditional) { cond_op_ptr = static_cast(*op).get_op(); ot = cond_op_ptr->get_type(); - if (is_box_type(ot)) { - return false; - } } } - - // Barriers are allways allowed - if (ot == OpType::Barrier) { + // Barriers are always allowed + if (is_barrier_type(ot)) { return true; } + // boxes are never allowed + if (is_box_type(ot)) { + return false; + } // this currently allows unplaced single qubits gates // this should be changes in the future diff --git a/tket/src/Mapping/Verification.cpp b/tket/src/Mapping/Verification.cpp index 167cf4a4a7..dbb094d624 100644 --- a/tket/src/Mapping/Verification.cpp +++ b/tket/src/Mapping/Verification.cpp @@ -30,12 +30,12 @@ bool respects_connectivity_constraints( if (qb_lookup.find(arg) != qb_lookup.end()) qbs.push_back(arg); } Op_ptr op = com.get_op_ptr(); - if (op->get_type() == OpType::Barrier) continue; if (op->get_type() == OpType::Conditional) { std::shared_ptr cond_ptr = std::dynamic_pointer_cast(op); op = cond_ptr->get_op(); } + if (op->get_type() == OpType::Barrier) continue; if (op->get_type() == OpType::CircBox) { std::shared_ptr box_ptr = std::dynamic_pointer_cast(op); diff --git a/tket/src/OpType/OpDesc.cpp b/tket/src/OpType/OpDesc.cpp index 27723f8ff0..f2ea0ab4fb 100644 --- a/tket/src/OpType/OpDesc.cpp +++ b/tket/src/OpType/OpDesc.cpp @@ -24,6 +24,7 @@ OpDesc::OpDesc(OpType type) : type_(type), info_(optypeinfo().at(type)), is_meta_(is_metaop_type(type)), + is_barrier_(is_barrier_type(type)), is_box_(is_box_type(type)), is_gate_(is_gate_type(type)), is_flowop_(is_flowop_type(type)), @@ -75,6 +76,8 @@ OptUInt OpDesc::n_classical() const { bool OpDesc::is_meta() const { return is_meta_; } +bool OpDesc::is_barrier() const { return is_barrier_; } + bool OpDesc::is_box() const { return is_box_; } bool OpDesc::is_gate() const { return is_gate_; } diff --git a/tket/src/OpType/OpTypeFunctions.cpp b/tket/src/OpType/OpTypeFunctions.cpp index 092f2cbcad..7c20aafcf8 100644 --- a/tket/src/OpType/OpTypeFunctions.cpp +++ b/tket/src/OpType/OpTypeFunctions.cpp @@ -126,12 +126,13 @@ const OpTypeSet& all_controlled_gate_types() { bool is_metaop_type(OpType optype) { static const OpTypeSet metaops = { - OpType::Input, OpType::Output, OpType::ClInput, - OpType::ClOutput, OpType::WASMInput, OpType::WASMOutput, - OpType::Barrier, OpType::Create, OpType::Discard}; + OpType::Input, OpType::Output, OpType::ClInput, OpType::ClOutput, + OpType::WASMInput, OpType::WASMOutput, OpType::Create, OpType::Discard}; return find_in_set(optype, metaops); } +bool is_barrier_type(OpType optype) { return optype == OpType::Barrier; } + bool is_initial_q_type(OpType optype) { return optype == OpType::Input || optype == OpType::Create; } diff --git a/tket/src/Ops/BarrierOp.cpp b/tket/src/Ops/BarrierOp.cpp new file mode 100644 index 0000000000..db0a11e4dc --- /dev/null +++ b/tket/src/Ops/BarrierOp.cpp @@ -0,0 +1,70 @@ +// Copyright 2019-2023 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tket/Ops/BarrierOp.hpp" + +#include + +#include "tket/OpType/EdgeType.hpp" +#include "tket/OpType/OpType.hpp" +#include "tket/Utils/Json.hpp" + +namespace tket { + +BarrierOp::BarrierOp(op_signature_t signature, const std::string& _data) + : Op(OpType::Barrier), signature_(signature), data_(_data) {} + +Op_ptr BarrierOp::symbol_substitution(const SymEngine::map_basic_basic&) const { + return Op_ptr(); +} + +SymSet BarrierOp::free_symbols() const { return {}; } + +unsigned BarrierOp::n_qubits() const { + return std::count(signature_.begin(), signature_.end(), EdgeType::Quantum); +} + +op_signature_t BarrierOp::get_signature() const { return signature_; } + +nlohmann::json BarrierOp::serialize() const { + nlohmann::json j; + j["type"] = get_type(); + j["signature"] = get_signature(); + j["data"] = get_data(); + return j; +} + +Op_ptr BarrierOp::deserialize(const nlohmann::json& j) { + op_signature_t sig = j.at("signature").get(); + std::string data; + try { + data = j.at("data").get(); + } catch (const nlohmann::json::out_of_range& e) { + data = ""; + } + return std::make_shared(sig, data); +} + +bool BarrierOp::is_clifford() const { return true; } + +BarrierOp::~BarrierOp() {} + +bool BarrierOp::is_equal(const Op& op_other) const { + const BarrierOp& other = dynamic_cast(op_other); + return ( + get_signature() == other.get_signature() && + get_data() == other.get_data()); +} + +} // namespace tket diff --git a/tket/src/Ops/MetaOp.cpp b/tket/src/Ops/MetaOp.cpp index 5ec22cb93f..4417423734 100644 --- a/tket/src/Ops/MetaOp.cpp +++ b/tket/src/Ops/MetaOp.cpp @@ -34,15 +34,6 @@ Op_ptr MetaOp::symbol_substitution(const SymEngine::map_basic_basic&) const { SymSet MetaOp::free_symbols() const { return {}; } -unsigned MetaOp::n_qubits() const { - OptUInt n = desc_.n_qubits(); - if (n == any) { - return std::count(signature_.begin(), signature_.end(), EdgeType::Quantum); - } else { - return n.value(); - } -} - op_signature_t MetaOp::get_signature() const { std::optional sig = desc_.signature(); if (sig) @@ -51,35 +42,8 @@ op_signature_t MetaOp::get_signature() const { return signature_; } -nlohmann::json MetaOp::serialize() const { - nlohmann::json j; - j["type"] = get_type(); - j["signature"] = get_signature(); - j["data"] = get_data(); - return j; -} - -Op_ptr MetaOp::deserialize(const nlohmann::json& j) { - OpType optype = j.at("type").get(); - op_signature_t sig = j.at("signature").get(); - std::string data; - try { - data = j.at("data").get(); - } catch (const nlohmann::json::out_of_range& e) { - data = ""; - } - return std::make_shared(optype, sig, data); -} - bool MetaOp::is_clifford() const { return true; } MetaOp::~MetaOp() {} -bool MetaOp::is_equal(const Op& op_other) const { - const MetaOp& other = dynamic_cast(op_other); - return (get_signature() == other.get_signature()); -} - -MetaOp::MetaOp() : Op(OpType::Barrier) {} - } // namespace tket diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index bfa242003f..42430d4f24 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -1240,6 +1240,26 @@ SCENARIO("Functions with symbolic ops") { REQUIRE(op3->get_type() == OpType::PhaseGadget); REQUIRE(test_equiv_val(op3->get_params()[0], 0.6)); } + GIVEN("A simple circuit with symbolics to instantiate, and a Barrier gate.") { + Circuit circ(2); + Sym a = SymEngine::symbol("alpha"); + Expr alpha(a); + circ.add_op(OpType::Rx, alpha, {0}); + circ.add_barrier({0, 1}); + REQUIRE(circ.is_symbolic()); + SymSet symbols = circ.free_symbols(); + REQUIRE(symbols.size() == 1); + REQUIRE(symbols.find(a) != symbols.end()); + symbol_map_t symbol_map; + symbol_map[a] = Expr(0.2); + circ.symbol_substitution(symbol_map); + VertexVec vertices = circ.vertices_in_order(); + Op_ptr op2 = circ.get_Op_ptr_from_Vertex(vertices[2]); + Op_ptr op3 = circ.get_Op_ptr_from_Vertex(vertices[3]); + REQUIRE(op2->get_type() == OpType::Rx); + REQUIRE(test_equiv_val(op2->get_params()[0], 0.2)); + REQUIRE(op3->get_type() == OpType::Barrier); + } } SCENARIO("Test depth_by_type method") { @@ -3117,6 +3137,185 @@ SCENARIO("check edge type in rewire function") { REQUIRE_THROWS_AS(c.rewire(v, ins, types), CircuitInvalidity); } } +void check_conditional_circuit(Circuit& c) { + // Confirm that the DAG is constructed appropriately by checking + // in and out edges of all vertices + std::vector vertices = c.all_vertices(); + // First check Quantum and Classical input and output vertices + // have expected number of out edges in interesting cases + REQUIRE(c.n_out_edges_of_type(vertices[8], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[8], EdgeType::Boolean) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[10], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[10], EdgeType::Boolean) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[12], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[12], EdgeType::Boolean) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[14], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[14], EdgeType::Boolean) == 2); + + // Conditional gate 0 + REQUIRE(c.n_in_edges_of_type(vertices[18], EdgeType::Quantum) == 1); + REQUIRE(c.n_in_edges_of_type(vertices[18], EdgeType::Classical) == 0); + REQUIRE(c.n_in_edges_of_type(vertices[18], EdgeType::Boolean) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[18], EdgeType::Quantum) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[18], EdgeType::Classical) == 0); + REQUIRE(c.n_out_edges_of_type(vertices[18], EdgeType::Boolean) == 0); + Op_ptr op_0 = c.get_Op_ptr_from_Vertex(vertices[18]); + const Conditional& con_0 = static_cast(*op_0); + REQUIRE(con_0.get_type() == OpType::Conditional); + Op_ptr barrier_0 = con_0.get_op(); + REQUIRE(barrier_0->get_type() == OpType::Barrier); + op_signature_t sig_0 = {EdgeType::Quantum}; + REQUIRE(barrier_0->get_signature() == sig_0); + + // Conditional gate 1 + REQUIRE(c.n_in_edges_of_type(vertices[19], EdgeType::Quantum) == 2); + REQUIRE(c.n_in_edges_of_type(vertices[19], EdgeType::Classical) == 1); + REQUIRE(c.n_in_edges_of_type(vertices[19], EdgeType::Boolean) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[19], EdgeType::Quantum) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[19], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[19], EdgeType::Boolean) == 0); + Op_ptr op_1 = c.get_Op_ptr_from_Vertex(vertices[19]); + const Conditional& con_1 = static_cast(*op_1); + REQUIRE(con_1.get_type() == OpType::Conditional); + Op_ptr barrier_1 = con_1.get_op(); + REQUIRE(barrier_1->get_type() == OpType::Barrier); + op_signature_t sig_1 = { + EdgeType::Quantum, EdgeType::Quantum, EdgeType::Classical}; + REQUIRE(barrier_1->get_signature() == sig_1); + + // Conditional gate 2 + REQUIRE(c.n_in_edges_of_type(vertices[20], EdgeType::Quantum) == 1); + REQUIRE(c.n_in_edges_of_type(vertices[20], EdgeType::Classical) == 1); + REQUIRE(c.n_in_edges_of_type(vertices[20], EdgeType::Boolean) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[20], EdgeType::Quantum) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[20], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[20], EdgeType::Boolean) == 0); + Op_ptr op_2 = c.get_Op_ptr_from_Vertex(vertices[20]); + const Conditional& con_2 = static_cast(*op_2); + REQUIRE(con_2.get_type() == OpType::Conditional); + Op_ptr barrier_2 = con_2.get_op(); + REQUIRE(barrier_2->get_type() == OpType::Barrier); + op_signature_t sig_2 = {EdgeType::Quantum, EdgeType::Classical}; + REQUIRE(barrier_2->get_signature() == sig_2); + + // Conditional gate 3 + REQUIRE(c.n_in_edges_of_type(vertices[21], EdgeType::Quantum) == 2); + REQUIRE(c.n_in_edges_of_type(vertices[21], EdgeType::Classical) == 3); + REQUIRE(c.n_in_edges_of_type(vertices[21], EdgeType::Boolean) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[21], EdgeType::Quantum) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[21], EdgeType::Classical) == 3); + REQUIRE(c.n_out_edges_of_type(vertices[21], EdgeType::Boolean) == 2); + Op_ptr op_3 = c.get_Op_ptr_from_Vertex(vertices[21]); + const Conditional& con_3 = static_cast(*op_3); + REQUIRE(con_3.get_type() == OpType::Conditional); + Op_ptr barrier_3 = con_3.get_op(); + REQUIRE(barrier_3->get_type() == OpType::Barrier); + op_signature_t sig_3 = { + EdgeType::Quantum, EdgeType::Quantum, EdgeType::Classical, + EdgeType::Classical, EdgeType::Classical}; + REQUIRE(barrier_3->get_signature() == sig_3); + + // Conditional gate 4 + REQUIRE(c.n_in_edges_of_type(vertices[22], EdgeType::Quantum) == 2); + REQUIRE(c.n_in_edges_of_type(vertices[22], EdgeType::Classical) == 1); + REQUIRE(c.n_in_edges_of_type(vertices[22], EdgeType::Boolean) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[22], EdgeType::Quantum) == 2); + REQUIRE(c.n_out_edges_of_type(vertices[22], EdgeType::Classical) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[22], EdgeType::Boolean) == 0); + Op_ptr op_4 = c.get_Op_ptr_from_Vertex(vertices[22]); + const Conditional& con_4 = static_cast(*op_4); + REQUIRE(con_4.get_type() == OpType::Conditional); + Op_ptr barrier_4 = con_4.get_op(); + REQUIRE(barrier_4->get_type() == OpType::Barrier); + op_signature_t sig_4 = { + EdgeType::Quantum, EdgeType::Quantum, EdgeType::Classical}; + REQUIRE(barrier_4->get_signature() == sig_4); + + // Conditional gate 5 + REQUIRE(c.n_in_edges_of_type(vertices[23], EdgeType::Quantum) == 4); + REQUIRE(c.n_in_edges_of_type(vertices[23], EdgeType::Classical) == 3); + REQUIRE(c.n_in_edges_of_type(vertices[23], EdgeType::Boolean) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[23], EdgeType::Quantum) == 4); + REQUIRE(c.n_out_edges_of_type(vertices[23], EdgeType::Classical) == 3); + REQUIRE(c.n_out_edges_of_type(vertices[23], EdgeType::Boolean) == 0); + Op_ptr op_5 = c.get_Op_ptr_from_Vertex(vertices[23]); + const Conditional& con_5 = static_cast(*op_5); + REQUIRE(con_5.get_type() == OpType::Conditional); + Op_ptr barrier_5 = con_5.get_op(); + REQUIRE(barrier_5->get_type() == OpType::Barrier); + op_signature_t sig_5 = {EdgeType::Quantum, EdgeType::Quantum, + EdgeType::Quantum, EdgeType::Quantum, + EdgeType::Classical, EdgeType::Classical, + EdgeType::Classical}; + REQUIRE(barrier_5->get_signature() == sig_5); + + // Conditional gate 6 + REQUIRE(c.n_in_edges_of_type(vertices[25], EdgeType::Quantum) == 4); + REQUIRE(c.n_in_edges_of_type(vertices[25], EdgeType::Classical) == 3); + REQUIRE(c.n_in_edges_of_type(vertices[25], EdgeType::Boolean) == 1); + REQUIRE(c.n_out_edges_of_type(vertices[25], EdgeType::Quantum) == 4); + REQUIRE(c.n_out_edges_of_type(vertices[25], EdgeType::Classical) == 3); + REQUIRE(c.n_out_edges_of_type(vertices[25], EdgeType::Boolean) == 0); + Op_ptr op_6 = c.get_Op_ptr_from_Vertex(vertices[25]); + const Conditional& con_6 = static_cast(*op_6); + REQUIRE(con_6.get_type() == OpType::Conditional); + Op_ptr barrier_6 = con_6.get_op(); + REQUIRE(barrier_6->get_type() == OpType::Barrier); + op_signature_t sig_6 = {EdgeType::Quantum, EdgeType::Quantum, + EdgeType::Quantum, EdgeType::Quantum, + EdgeType::Classical, EdgeType::Classical, + EdgeType::Classical}; + REQUIRE(barrier_6->get_signature() == sig_6); +} + +SCENARIO("Check Circuit::add_conditional_barrier.") { + GIVEN( + "Add various forms of valid conditional barrier using the unsigned " + "constructor.") { + Circuit c(4, 5); + std::vector c_empty = {}; + std::vector c_0 = {0}; + std::vector c_3 = {3}; + std::vector c_4 = {4}; + std::vector c_01 = {0, 1}; + std::vector c_02 = {0, 2}; + std::vector c_12 = {1, 2}; + std::vector c_13 = {1, 3}; + std::vector c_012 = {0, 1, 2}; + std::vector c_0123 = {0, 1, 2, 3}; + + c.add_conditional_barrier(c_0, c_empty, c_0, 0, ""); + c.add_conditional_barrier(c_01, c_0, c_12, 1, ""); + c.add_conditional_barrier(c_0, c_0, c_12, 1, ""); + c.add_conditional_barrier(c_13, c_012, c_3, 0, "test"); + c.add_conditional_barrier(c_02, c_0, c_12, 1, "test1"); + c.add_conditional_barrier(c_0123, c_012, c_3, 0, "test1"); + c.add_measure(3, 4); + c.add_conditional_barrier(c_0123, c_012, c_4, 0, "test2"); + check_conditional_circuit(c); + } + GIVEN( + "Add various forms of valid conditional barrier using the unitid " + "constructor.") { + Circuit c(4, 5); + c.add_conditional_barrier({Qubit(0)}, {Bit(0)}, 0, ""); + c.add_conditional_barrier( + {Qubit(0), Qubit(1), Bit(0)}, {Bit(1), Bit(2)}, 1, ""); + c.add_conditional_barrier({Qubit(0), Bit(0)}, {Bit(1), Bit(2)}, 1, ""); + c.add_conditional_barrier( + {Qubit(1), Qubit(3), Bit(0), Bit(1), Bit(2)}, {Bit(3)}, 0, "test"); + c.add_conditional_barrier( + {Qubit(0), Qubit(2), Bit(0)}, {Bit(1), Bit(2)}, 1, "test1"); + c.add_conditional_barrier( + {Qubit(0), Qubit(1), Qubit(2), Qubit(3), Bit(0), Bit(1), Bit(2)}, + {Bit(3)}, 0, "test1"); + c.add_measure(Qubit(3), Bit(4)); + c.add_conditional_barrier( + {Qubit(0), Qubit(1), Qubit(2), Qubit(3), Bit(0), Bit(1), Bit(2)}, + {Bit(4)}, 0, "test2"); + check_conditional_circuit(c); + } +} } // namespace test_Circ } // namespace tket diff --git a/tket/test/src/Circuit/test_ThreeQubitConversion.cpp b/tket/test/src/Circuit/test_ThreeQubitConversion.cpp index beec896801..c0ffb89087 100644 --- a/tket/test/src/Circuit/test_ThreeQubitConversion.cpp +++ b/tket/test/src/Circuit/test_ThreeQubitConversion.cpp @@ -480,6 +480,7 @@ SCENARIO("Three-qubit squash") { c.add_op(OpType::CX, {i % 3, (i + 1) % 3}); c.add_op(OpType::Rz, 0.25, {(i + 1) % 3}); } + c.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); c.add_conditional_gate(OpType::X, {}, {0}, {0}, 1); for (unsigned i = 11; i < 22; i++) { c.add_op(OpType::H, {i % 3}); @@ -489,13 +490,14 @@ SCENARIO("Three-qubit squash") { CHECK_FALSE(Transforms::three_qubit_squash().apply(c)); } GIVEN("A circuit with a barrier") { - Circuit c(3); + Circuit c(3, 1); for (unsigned i = 0; i < 11; i++) { c.add_op(OpType::H, {i % 3}); c.add_op(OpType::CX, {i % 3, (i + 1) % 3}); c.add_op(OpType::Rz, 0.25, {(i + 1) % 3}); } c.add_barrier({0, 1, 2}); + c.add_conditional_barrier({0, 2, 1}, {}, {0}, 1, ""); for (unsigned i = 11; i < 22; i++) { c.add_op(OpType::H, {i % 3}); c.add_op(OpType::CX, {i % 3, (i + 1) % 3}); diff --git a/tket/test/src/Passes/test_SynthesiseTket.cpp b/tket/test/src/Passes/test_SynthesiseTket.cpp index fab5f011e8..07250936da 100644 --- a/tket/test/src/Passes/test_SynthesiseTket.cpp +++ b/tket/test/src/Passes/test_SynthesiseTket.cpp @@ -27,10 +27,11 @@ SCENARIO("SynthesiseTket with conditionals") { c.add_c_register("c", 1); c.add_conditional_gate(OpType::CnRy, {0.25}, {0, 1}, {0}, 0); c.add_conditional_gate(OpType::Ry, {0.125}, {1}, {0}, 0); + c.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); CompilationUnit cu(c); SynthesiseTket()->apply(cu); Circuit c1 = cu.get_circ_ref(); - REQUIRE(c1.n_gates() == 5); + REQUIRE(c1.n_gates() == 6); } } // namespace test_SynthesiseTket diff --git a/tket/test/src/Simulation/test_CircuitSimulator.cpp b/tket/test/src/Simulation/test_CircuitSimulator.cpp index f16820b173..3fc2ef4108 100644 --- a/tket/test/src/Simulation/test_CircuitSimulator.cpp +++ b/tket/test/src/Simulation/test_CircuitSimulator.cpp @@ -176,6 +176,13 @@ SCENARIO("Simulate circuit with unsupported operations") { tket_sim::get_unitary(circ), Unsupported, MessageContains("Unsupported OpType Conditional")); } + GIVEN("Circuit with conditional barrier") { + Circuit circ(2, 2); + circ.add_conditional_barrier({0, 1}, {1}, {0}, 1, ""); + REQUIRE_THROWS_MATCHES( + tket_sim::get_unitary(circ), Unsupported, + MessageContains("Unsupported OpType Conditional")); + } } SCENARIO("Ignored op types don't affect get unitary") { diff --git a/tket/test/src/ZX/test_ZXConverters.cpp b/tket/test/src/ZX/test_ZXConverters.cpp index 23e7a8ff6e..65ce361b04 100644 --- a/tket/test/src/ZX/test_ZXConverters.cpp +++ b/tket/test/src/ZX/test_ZXConverters.cpp @@ -266,6 +266,13 @@ SCENARIO("Check converting gates to spiders") { REQUIRE(zx.n_wires() == 2); REQUIRE_NOTHROW(zx.check_validity()); } + GIVEN("Conditional Barrier") { + Circuit circ(2, 2); + circ.add_conditional_barrier( + std::vector{0, 1}, std::vector{0}, + std::vector{1}, 1, ""); + REQUIRE_THROWS_AS(circuit_to_zx(circ), Unsupported); + } GIVEN("noop") { Circuit circ(1); circ.add_op(OpType::noop, {0}); diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index b37e89b86a..80c5f8b957 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -356,15 +356,16 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { circ.add_conditional_gate(OpType::Rz, {0.142}, {0}, {0}, 0); circ.add_conditional_gate(OpType::Rz, {0.143}, {0}, {0}, 0); circ.add_conditional_gate(OpType::Rx, {0.528}, {1}, {0}, 0); + circ.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); CompilationUnit cu(circ); squash->apply(cu); const Circuit& c = cu.get_circ_ref(); c.assert_valid(); - REQUIRE(c.n_gates() == 3); + REQUIRE(c.n_gates() == 4); std::vector expected_optypes{ OpType::Conditional, // qubit 0 before CX OpType::Conditional, // qubit 1 before CX - OpType::CX}; + OpType::CX, OpType::Conditional}; check_command_types(c, expected_optypes); auto cmds = c.get_commands(); diff --git a/tket/test/src/test_LexiRoute.cpp b/tket/test/src/test_LexiRoute.cpp index 83b4442c73..004c2ebf86 100644 --- a/tket/test/src/test_LexiRoute.cpp +++ b/tket/test/src/test_LexiRoute.cpp @@ -459,6 +459,7 @@ SCENARIO("Test LexiRoute::solve and LexiRoute::solve_labelling") { circ.add_op(OpType::Measure, {qubits[1], Bit(0)}); circ.add_op(OpType::CX, {qubits[4], qubits[5]}); circ.add_op(OpType::Measure, {qubits[3], Bit(0)}); + circ.add_conditional_barrier({0, 1, 2}, {}, {0}, 1, ""); // n0 -- n1 -- n2 -- n3 -- n4 // | | // n5 n7 @@ -473,7 +474,7 @@ SCENARIO("Test LexiRoute::solve and LexiRoute::solve_labelling") { lr.solve(4); std::vector commands = mf->circuit_.get_commands(); - REQUIRE(commands.size() == 7); + REQUIRE(commands.size() == 8); Command swap_c = commands[1]; unit_vector_t uids = {nodes[1], nodes[2]}; REQUIRE(swap_c.get_args() == uids); diff --git a/tket/test/src/test_Rebase.cpp b/tket/test/src/test_Rebase.cpp index df0b02987c..e749f0a836 100644 --- a/tket/test/src/test_Rebase.cpp +++ b/tket/test/src/test_Rebase.cpp @@ -290,12 +290,14 @@ SCENARIO("Building rebases with rebase_factory") { Circuit circ(2, 1); circ.add_op(OpType::T, {0}); circ.add_conditional_gate(OpType::H, {}, {1}, {0}, 1); + circ.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); Transforms::rebase_tket().apply(circ); Circuit correct(2, 1); correct.add_op(OpType::TK1, {0, 0, 0.25}, {0}); correct.add_conditional_gate( OpType::TK1, {0.5, 0.5, 0.5}, {1}, {0}, 1); correct.add_conditional_gate(OpType::Phase, {0.5}, {}, {0}, 1); + correct.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); correct.add_phase(0.125); REQUIRE(circ == correct); } diff --git a/tket/test/src/test_RoutingPasses.cpp b/tket/test/src/test_RoutingPasses.cpp index 4148efa1ac..635428220c 100644 --- a/tket/test/src/test_RoutingPasses.cpp +++ b/tket/test/src/test_RoutingPasses.cpp @@ -364,6 +364,7 @@ SCENARIO( circ.add_conditional_gate(OpType::CX, {}, {2, 1}, {0, 1}, 1); circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0, 1}, 2); circ.add_conditional_gate(OpType::CX, {}, {2, 1}, {1, 0}, 3); + circ.add_conditional_barrier({1, 2}, {1}, {0}, 1, ""); circ.add_conditional_gate(OpType::CX, {}, {0, 2}, {0, 1}, 0); MappingManager mm(std::make_shared(test_arc)); REQUIRE(mm.route_circuit( @@ -382,6 +383,7 @@ SCENARIO( Circuit circ(5, 1); circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 1); add_2qb_gates(circ, OpType::CX, {{0, 1}, {1, 2}, {1, 3}, {1, 4}, {0, 1}}); + circ.add_conditional_barrier({0, 1, 2}, {}, {0}, 1, ""); MappingManager mm(std::make_shared(arc)); REQUIRE(mm.route_circuit( diff --git a/tket/test/src/test_Synthesis.cpp b/tket/test/src/test_Synthesis.cpp index da5652503b..a095e2191f 100644 --- a/tket/test/src/test_Synthesis.cpp +++ b/tket/test/src/test_Synthesis.cpp @@ -26,7 +26,7 @@ #include "tket/Gate/Rotation.hpp" #include "tket/OpType/OpType.hpp" #include "tket/OpType/OpTypeFunctions.hpp" -#include "tket/Ops/MetaOp.hpp" +#include "tket/Ops/BarrierOp.hpp" #include "tket/Predicates/CompilationUnit.hpp" #include "tket/Predicates/CompilerPass.hpp" #include "tket/Predicates/PassLibrary.hpp" @@ -1754,8 +1754,8 @@ SCENARIO("Test barrier blocks transforms successfully") { REQUIRE(circ.depth_by_type(OpType::Barrier) == 1); REQUIRE(circ.n_gates() == 3); // both CXs removed Circuit rep(4); - const Op_ptr bar = std::make_shared( - OpType::Barrier, op_signature_t(4, EdgeType::Quantum)); + const Op_ptr bar = + std::make_shared(op_signature_t(4, EdgeType::Quantum)); REQUIRE(circ.substitute_all(rep, bar)); REQUIRE(Transforms::remove_redundancies().apply(circ)); REQUIRE(verify_n_qubits_for_ops(circ)); diff --git a/tket/test/src/test_TwoQubitCanonical.cpp b/tket/test/src/test_TwoQubitCanonical.cpp index e0f3a6aa0e..19ea9b22e6 100644 --- a/tket/test/src/test_TwoQubitCanonical.cpp +++ b/tket/test/src/test_TwoQubitCanonical.cpp @@ -689,8 +689,9 @@ SCENARIO("two_qubit_squash with classical ops") { circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 1); circ.add_op(tket::OpType::CX, {0, 1}); circ.add_op(tket::OpType::CX, {0, 1}); + circ.add_conditional_barrier({0, 1}, {}, {0}, 1, ""); REQUIRE(Transforms::two_qubit_squash(OpType::CX).apply(circ)); - REQUIRE(circ.n_gates() == 1); + REQUIRE(circ.n_gates() == 2); REQUIRE(circ.get_commands()[0].get_vertex() == v); } GIVEN("Circuit with conditional gates") { diff --git a/tket/test/src/test_json.cpp b/tket/test/src/test_json.cpp index d52eeeb665..62fd76052e 100644 --- a/tket/test/src/test_json.cpp +++ b/tket/test/src/test_json.cpp @@ -71,10 +71,10 @@ bool check_circuit(const Circuit& c) { SCENARIO("Test Op serialization") { GIVEN("OpType") { - const OpTypeSet metaops = {OpType::Input, OpType::Output, - OpType::ClInput, OpType::ClOutput, - OpType::WASMInput, OpType::WASMOutput, - OpType::Barrier}; + const OpTypeSet meta_barrier_ops = {OpType::Input, OpType::Output, + OpType::ClInput, OpType::ClOutput, + OpType::WASMInput, OpType::WASMOutput, + OpType::Barrier}; const OpTypeSet boxes = { OpType::CircBox, OpType::Unitary1qBox, OpType::Unitary2qBox, OpType::Unitary3qBox, @@ -86,7 +86,7 @@ SCENARIO("Test Op serialization") { std::set type_names; for (auto type : - boost::join(all_gate_types(), boost::join(metaops, boxes))) { + boost::join(all_gate_types(), boost::join(meta_barrier_ops, boxes))) { bool success_insert = type_names.insert(optypeinfo().at(type).name).second; // check all optype names are unique @@ -226,6 +226,8 @@ SCENARIO("Test Circuit serialization") { c.add_conditional_gate(OpType::Ry, {-0.75}, {0}, {0, 1}, 1); c.add_conditional_gate(OpType::CX, {}, {0, 1}, {0, 1}, 1); c.add_conditional_gate(OpType::Measure, {}, {0, 2}, {0, 1}, 1); + c.add_conditional_barrier({0, 1}, {1, 2}, {0}, 0, ""); + c.add_conditional_barrier({0}, {2}, {0, 1}, 1, "test"); nlohmann::json j_box = c; const Circuit new_c = j_box.get();