Skip to content

Commit

Permalink
update remove blank wires to keep classical bits (#1435)
Browse files Browse the repository at this point in the history
* update remove blank wires

* add testcase

* update changelog

* update tket version

* update testcases

* update regen stubs

* fix typo

* add param to remove_classical_wire in python

* regen stubs

* Update pytket/docs/changelog.rst

Co-authored-by: Alec Edgington <[email protected]>

* rename parameter to keep_blank_classical_wires

* update tket version

* fix param

* Update pytket/binders/circuit/Circuit/main.cpp

Co-authored-by: Alec Edgington <[email protected]>

* Update pytket/binders/circuit/Circuit/main.cpp

Co-authored-by: Alec Edgington <[email protected]>

* Update pytket/pytket/_tket/circuit.pyi

Co-authored-by: Alec Edgington <[email protected]>

* update testcases to use bits and qubits

* regen stubs

---------

Co-authored-by: Alec Edgington <[email protected]>
  • Loading branch information
cqc-melf and cqc-alec authored Jun 6, 2024
1 parent dc910ad commit 02ddc13
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 22 deletions.
4 changes: 3 additions & 1 deletion pytket/binders/circuit/Circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,9 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &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 "
Expand Down
2 changes: 1 addition & 1 deletion pytket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 2 additions & 1 deletion pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
-----------------

Expand Down
4 changes: 3 additions & 1 deletion pytket/pytket/_tket/circuit.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down
2 changes: 1 addition & 1 deletion tket/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

class TketConan(ConanFile):
name = "tket"
version = "1.3.3"
version = "1.3.4"
package_type = "library"
license = "Apache 2"
homepage = "https://github.com/CQCL/tket"
Expand Down
9 changes: 7 additions & 2 deletions tket/include/tket/Circuit/Circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 12 additions & 10 deletions tket/src/Circuit/basic_circ_manip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<TagID>()) {
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) {
Expand Down
2 changes: 1 addition & 1 deletion tket/src/Predicates/PassGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ PassPtr gen_flatten_relabel_registers_pass(const std::string& label) {
Transform t =
Transform([=](Circuit& circuit, std::shared_ptr<unit_bimaps_t> 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<Qubit, Qubit> relabelling_map;
std::vector<Qubit> all_qubits = circuit.all_qubits();
Expand Down
31 changes: 27 additions & 4 deletions tket/test/src/Circuit/test_Circ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned>(OpType::CX, {0, 1});
test.add_op<unsigned>(OpType::Z, {0});

WHEN("Check Commands work correctly") {
std::vector<Command> 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();
}

Expand Down

0 comments on commit 02ddc13

Please sign in to comment.