Skip to content

Commit

Permalink
Fix invalid registers returned by Circuit.q_registers and ``Circu…
Browse files Browse the repository at this point in the history
…it.c_registers``.
  • Loading branch information
yao-cqc committed Dec 19, 2023
1 parent 7f04263 commit 700d6a3
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 38 deletions.
77 changes: 39 additions & 38 deletions pytket/binders/circuit/Circuit/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,43 @@ UnitID to_cpp_unitid(const PyUnitID &py_unitid) {
return get<Bit>(py_unitid);
}

template <typename T>
std::vector<T> get_unit_registers(Circuit &circ) {
static_assert(
std::is_same<T, QubitRegister>::value ||
std::is_same<T, BitRegister>::value,
"T must be either QubitRegister or BitRegister");
using T2 = typename std::conditional<
std::is_same<T, QubitRegister>::value, Qubit, Bit>::type;
std::vector<T2> unitids;
if constexpr (std::is_same<T, QubitRegister>::value) {
unitids = circ.all_qubits();
} else if constexpr (std::is_same<T, BitRegister>::value) {
unitids = circ.all_bits();
}
// map from register name to unsigned indices
std::map<std::string, std::set<unsigned>> unit_map;
std::vector<T> regs;
for (const T2 &unitid : unitids) {
// UnitRegisters only describe registers with 1-d indices
if (unitid.reg_dim() > 1) continue;
auto it = unit_map.find(unitid.reg_name());
if (it == unit_map.end()) {
unit_map.insert({unitid.reg_name(), {unitid.index()[0]}});
} else {
it->second.insert(unitid.index()[0]);
}
}
regs.reserve(unit_map.size());
for (auto const &it : unit_map) {
// only return registers that are indexed consecutively from zero
if (*it.second.rbegin() == it.second.size() - 1) {
regs.emplace_back(it.first, it.second.size());
}
}
return regs;
}

void init_circuit_add_op(py::class_<Circuit, std::shared_ptr<Circuit>> &c);
void init_circuit_add_classical_op(
py::class_<Circuit, std::shared_ptr<Circuit>> &c);
Expand Down Expand Up @@ -252,25 +289,7 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
":py:class:`BitRegister`",
py::arg("name"))
.def_property_readonly(
"c_registers",
[](Circuit &circ) {
bit_vector_t all_bits = circ.all_bits();
std::map<std::string, unsigned> bits_map;
std::vector<BitRegister> b_regs;
for (const Bit &bit : all_bits) {
auto it = bits_map.find(bit.reg_name());
if (it == bits_map.end()) {
bits_map.insert({bit.reg_name(), 1});
} else {
it->second++;
}
}
b_regs.reserve(bits_map.size());
for (auto const &it : bits_map) {
b_regs.emplace_back(it.first, it.second);
}
return b_regs;
},
"c_registers", &get_unit_registers<BitRegister>,
"Get all classical registers.\n\n"
"This property is only valid if the bits in the circuit are "
"organized into registers (i.e. all bit indices are single numbers "
Expand All @@ -292,25 +311,7 @@ void def_circuit(py::class_<Circuit, std::shared_ptr<Circuit>> &pyCircuit) {
":py:class:`QubitRegister`",
py::arg("name"))
.def_property_readonly(
"q_registers",
[](Circuit &circ) {
qubit_vector_t all_qbs = circ.all_qubits();
std::map<std::string, unsigned> qbs_map;
std::vector<QubitRegister> q_regs;
for (const Qubit &qb : all_qbs) {
auto it = qbs_map.find(qb.reg_name());
if (it == qbs_map.end()) {
qbs_map.insert({qb.reg_name(), 1});
} else {
it->second++;
}
}
q_regs.reserve(qbs_map.size());
for (auto const &it : qbs_map) {
q_regs.emplace_back(it.first, it.second);
}
return q_regs;
},
"q_registers", &get_unit_registers<QubitRegister>,
"Get all quantum registers.\n\n"
"This property is only valid if the qubits in the circuit are "
"organized into registers (i.e. all qubit indices are single numbers "
Expand Down
1 change: 1 addition & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Fixes:
* Fix `PauliFrameRandomisation.sample_circuits`.
* For `Circuit` with no 2-qubit gates, `NoiseAwarePlacement` now assigns `Qubit` to `Node` in `Architecture`
with lowest reported error rates.
* Fix invalid registers returned by ``Circuit.q_registers`` and ``Circuit.c_registers``.


1.22.0 (November 2023)
Expand Down
19 changes: 19 additions & 0 deletions pytket/tests/circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,25 @@ def test_getting_registers() -> None:
assert q_regs[1] == QubitRegister("test_qr", 10)


def test_getting_registers_with_non_consective_indices() -> None:
# https://github.com/CQCL/tket/issues/1160
c = Circuit()
c.add_qubit(Qubit(3))
c.add_qubit(Qubit(2))
c.add_bit(Bit(3))
c.add_qubit(Qubit("a", 0))
c.add_qubit(Qubit("a", 1))
c.add_qubit(Qubit("a", 2))
c.add_bit(Bit("b", 0))
c.add_bit(Bit("b", 1))
c_regs = c.c_registers
assert len(c_regs) == 1
assert c_regs[0] == BitRegister("b", 2)
q_regs = c.q_registers
assert len(q_regs) == 1
assert q_regs[0] == QubitRegister("a", 3)


def test_measuring_registers() -> None:
c = Circuit()
with pytest.raises(RuntimeError) as e:
Expand Down

0 comments on commit 700d6a3

Please sign in to comment.