Skip to content

Commit 2d03cce

Browse files
Fix segfault in LexiRoute lookahead on classical vertices (#2248)
1 parent d2bb207 commit 2d03cce

5 files changed

Lines changed: 58 additions & 4 deletions

File tree

‎TKET_VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.1.101
1+
2.1.102

‎pytket/docs/changelog.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
Fixes:
6+
7+
- Fix segfault in `LexiRouteRoutingMethod` (and so `DefaultMappingPass`) when
8+
a `ClExprOp` is reached while looking ahead to the next two-qubit slice.
9+
310
## 2.18.4 (September 2026)
411

512
Fixes:

‎pytket/tests/mapping_test.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from pytket import Circuit, OpType
1919
from pytket.architecture import Architecture
20-
from pytket.circuit import CircBox, Node, PhasePolyBox, Qubit, UnitID
20+
from pytket.circuit import Bit, CircBox, Node, PhasePolyBox, Qubit, UnitID
2121
from pytket.circuit.named_types import UnitIdMap
2222
from pytket.mapping import (
2323
AASLabellingMethod,
@@ -439,3 +439,20 @@ def test_BoxDecompositionRoutingMethod() -> None:
439439
)
440440
assert circ.valid_connectivity(arc, directed=False)
441441
assert len(circ.get_commands()) == 4
442+
443+
444+
def test_LexiRoute_with_clexpr_in_lookahead() -> None:
445+
# https://github.com/Quantinuum/tket/issues/2180
446+
circ = Circuit(3, 3)
447+
circ.CX(0, 1)
448+
circ.CX(2, 1)
449+
circ.H(2, condition_bits=[2])
450+
circ.add_clexpr_from_logicexp(Bit(1) | Bit(2), [Bit(0)])
451+
circ.add_clexpr_from_logicexp(Bit(1) & Bit(0), [Bit(2)])
452+
circ.CX(0, 2)
453+
circ.CX(0, 2)
454+
arc = Architecture([(0, 1), (1, 2)])
455+
MappingManager(arc).route_circuit(
456+
circ, [LexiLabellingMethod(), LexiRouteRoutingMethod()]
457+
)
458+
assert circ.valid_connectivity(arc, directed=False)

‎tket/src/Mapping/MappingFrontier.cpp‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,10 @@ void MappingFrontier::advance_next_2qb_slice(unsigned max_advance) {
253253
}
254254
EdgeVec in_edges =
255255
this->circuit_.get_in_edges_of_type(vert, EdgeType::Quantum);
256-
// More than 1 edge means we want to keep edges, so continue
257-
if (in_edges.size() > 1) {
256+
// More than 1 edge means we want to keep edges, so continue. No edge
257+
// means a purely classical vertex (e.g. ClExprOp), which has no quantum
258+
// boundary to advance.
259+
if (in_edges.size() != 1) {
258260
continue;
259261
}
260262
// can guarantee that we update now as non-updating cases have been

‎tket/test/src/test_LexiRoute.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "tket/Mapping/LexiRouteRoutingMethod.hpp"
2222
#include "tket/Mapping/MappingManager.hpp"
2323
#include "tket/Mapping/Verification.hpp"
24+
#include "tket/Ops/ClExpr.hpp"
2425
#include "tket/Ops/ClassicalOps.hpp"
2526
#include "tket/Placement/Placement.hpp"
2627
#include "tket/Predicates/CompilationUnit.hpp"
@@ -2387,4 +2388,31 @@ SCENARIO("Lexi route produces incorrect bimaps") {
23872388
REQUIRE(mm.route_circuit_with_maps(circ, config, maps));
23882389
REQUIRE(check_permutation(circ, maps));
23892390
}
2391+
SCENARIO("Lexi route with ClExprOps in the lookahead slice") {
2392+
// segfault Github #2180: advance_next_2qb_slice read the quantum in-edge of
2393+
// a purely classical vertex, which has none.
2394+
Circuit circ(3, 3);
2395+
circ.add_op<unsigned>(OpType::CX, {0, 1});
2396+
circ.add_op<unsigned>(OpType::CX, {2, 1});
2397+
circ.add_conditional_gate<unsigned>(OpType::H, {}, {2}, {2}, 1);
2398+
ClExpr bit_or(ClOp::BitOr, {ClBitVar{0}, ClBitVar{1}});
2399+
circ.add_op<unsigned>(
2400+
std::make_shared<const ClExprOp>(
2401+
WiredClExpr(bit_or, {{0, 0}, {1, 1}}, {}, {2})),
2402+
{1, 2, 0});
2403+
ClExpr bit_and(ClOp::BitAnd, {ClBitVar{0}, ClBitVar{1}});
2404+
circ.add_op<unsigned>(
2405+
std::make_shared<const ClExprOp>(
2406+
WiredClExpr(bit_and, {{0, 0}, {1, 1}}, {}, {2})),
2407+
{1, 0, 2});
2408+
circ.add_op<unsigned>(OpType::CX, {0, 2});
2409+
circ.add_op<unsigned>(OpType::CX, {0, 2});
2410+
Architecture arc({{Node(0), Node(1)}, {Node(1), Node(2)}});
2411+
MappingManager mm(std::make_shared<Architecture>(arc));
2412+
std::vector<RoutingMethodPtr> config = {
2413+
std::make_shared<LexiLabellingMethod>(),
2414+
std::make_shared<LexiRouteRoutingMethod>()};
2415+
REQUIRE(mm.route_circuit(circ, config));
2416+
REQUIRE(respects_connectivity_constraints(circ, arc, false));
2417+
}
23902418
} // namespace tket

0 commit comments

Comments
 (0)