diff --git a/pytket/binders/circuit/Circuit/main.cpp b/pytket/binders/circuit/Circuit/main.cpp index 69f5eae586..7c764a2ccc 100644 --- a/pytket/binders/circuit/Circuit/main.cpp +++ b/pytket/binders/circuit/Circuit/main.cpp @@ -431,7 +431,9 @@ void def_circuit(py::class_> &pyCircuit) { "from the circuit. This may occur when optimisations " "recognise that the operations on a qubit reduce to the " "identity, or when routing adds wires to \"fill out\" the " - "architecture.") + "architecture.\n\n:param keep_blank_classical_wires: select if " + "empty classical wires should not be removed", + py::arg("keep_blank_classical_wires") = false) .def( "add_blank_wires", &Circuit::add_blank_wires, "Adds a number of new qubits to the circuit. These will be " diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 261e824730..1e0037a0b5 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.3@tket/stable") + self.requires("tket/1.3.4@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 74822745b6..c312fe7148 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -9,13 +9,14 @@ Features: * Add ``OpType.CnRx`` and ``OpType.CnRz``. * Add ``AutoRebase`` and ``AutoSquash`` passes. Deprecate ``auto_rebase_pass`` and ``auto_squash_pass``. +* Add new parameter to `remove_blank_wires` to allow to keep empty classical bits Fixes: * Allow barriers when dagger or transpose a circuit. +* Keep blank classical wires when running `FlattenRelabelRegistersPass` * Handle Clifford-angle ``NPhasedX`` gates in Clifford resynthesis. - 1.28.0 (May 2024) ----------------- diff --git a/pytket/pytket/_tket/circuit.pyi b/pytket/pytket/_tket/circuit.pyi index eb0e7f8de3..a1955deded 100644 --- a/pytket/pytket/_tket/circuit.pyi +++ b/pytket/pytket/_tket/circuit.pyi @@ -2263,9 +2263,11 @@ class Circuit: """ Query whether a qubit has its final state discarded """ - def remove_blank_wires(self) -> None: + def remove_blank_wires(self, keep_blank_classical_wires: bool = False) -> None: """ Removes any Input-Output pairs in the DAG with no intervening operations, i.e. removes untouched qubits/bits from the circuit. This may occur when optimisations recognise that the operations on a qubit reduce to the identity, or when routing adds wires to "fill out" the architecture. + + :param keep_blank_classical_wires: select if empty classical wires should not be removed """ def rename_units(self, map: dict[pytket._tket.unit_id.UnitID | pytket._tket.unit_id.Qubit | pytket._tket.unit_id.Bit, pytket._tket.unit_id.UnitID | pytket._tket.unit_id.Qubit | pytket._tket.unit_id.Bit]) -> bool: """ diff --git a/tket/conanfile.py b/tket/conanfile.py index 6a48958fde..d6995cac8b 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.3" + version = "1.3.4" 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 04cbb56cc0..38f80e7931 100644 --- a/tket/include/tket/Circuit/Circuit.hpp +++ b/tket/include/tket/Circuit/Circuit.hpp @@ -762,8 +762,13 @@ class Circuit { // Basic Circuit Manipulation// ////////////////////////////// - // O(V), `V` the number of vertices - void remove_blank_wires(); + /** + * O(V), `V` the number of vertices + * removes all blank wires + * @param keep_blank_classical_wires option to choose if empty classical wire + * should be removed as well + */ + void remove_blank_wires(bool keep_blank_classical_wires = false); /** * Remove all gates in the circuits that are identities diff --git a/tket/src/Circuit/basic_circ_manip.cpp b/tket/src/Circuit/basic_circ_manip.cpp index 49749eb4a1..0f200d2283 100644 --- a/tket/src/Circuit/basic_circ_manip.cpp +++ b/tket/src/Circuit/basic_circ_manip.cpp @@ -32,20 +32,22 @@ namespace tket { // if there are any blank wires in the circuit, // this method removes them and removes the vertices // from boundaries -void Circuit::remove_blank_wires() { +void Circuit::remove_blank_wires(bool keep_blank_classical_wires) { VertexList bin; unit_vector_t unused_units; const Op_ptr noop = get_op_ptr(OpType::noop); for (const BoundaryElement& el : boundary.get()) { - Vertex in = el.in_; - Vertex out = el.out_; - VertexVec succs = get_successors(in); - if (succs.size() == 1 && succs.front() == out) { - dag[in].op = noop; - bin.push_back(in); - dag[out].op = noop; - bin.push_back(out); - unused_units.push_back(el.id_); + if (!keep_blank_classical_wires || el.type() == UnitType::Qubit) { + Vertex in = el.in_; + Vertex out = el.out_; + VertexVec succs = get_successors(in); + if (succs.size() == 1 && succs.front() == out) { + dag[in].op = noop; + bin.push_back(in); + dag[out].op = noop; + bin.push_back(out); + unused_units.push_back(el.id_); + } } } for (const UnitID& u : unused_units) { diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 3fe9057098..0a6c3f316a 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -357,7 +357,7 @@ PassPtr gen_flatten_relabel_registers_pass(const std::string& label) { Transform t = Transform([=](Circuit& circuit, std::shared_ptr maps) { unsigned n_qubits = circuit.n_qubits(); - circuit.remove_blank_wires(); + circuit.remove_blank_wires(false); bool changed = circuit.n_qubits() < n_qubits; std::map relabelling_map; std::vector all_qubits = circuit.all_qubits(); diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index d1724bdd13..270e63b38c 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -1012,11 +1012,34 @@ SCENARIO( } test.add_blank_wires(8); - int n = test.n_vertices(); + REQUIRE(test.n_bits() == 0); + REQUIRE(test.n_qubits() == 10); test.remove_blank_wires(); - int m = test.n_vertices(); - REQUIRE(n == 22); - REQUIRE(m == 6); + REQUIRE(test.n_bits() == 0); + REQUIRE(test.n_qubits() == 2); + test.assert_valid(); +} + +SCENARIO( + "Test a circuit with blank wires can have the blank wires removed keeping " + "classical", + "[blank_wires]") { + Circuit test(4, 2); + test.add_op(OpType::CX, {0, 1}); + test.add_op(OpType::Z, {0}); + + WHEN("Check Commands work correctly") { + std::vector coms = test.get_commands(); + REQUIRE(*coms[0].get_op_ptr() == *get_op_ptr(OpType::CX)); + REQUIRE(*coms[1].get_op_ptr() == *get_op_ptr(OpType::Z)); + } + + test.add_blank_wires(8); + REQUIRE(test.n_bits() == 2); + REQUIRE(test.n_qubits() == 12); + test.remove_blank_wires(true); + REQUIRE(test.n_bits() == 2); + REQUIRE(test.n_qubits() == 2); test.assert_valid(); }