diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index a2f6c2f23f..33dfb4c517 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -54,7 +54,6 @@ #include #include #include -#include #include #include #include @@ -151,7 +150,6 @@ struct MappingPass : impl::MappingPassBase { /// Statistics collected while routing. struct Statistics { size_t nswaps{0}; - DenseSet touchedPrograms; }; /// Parameters influencing the behavior of the A* search algorithm. @@ -366,26 +364,7 @@ struct MappingPass : impl::MappingPassBase { return; } - SmallVector materializedPrograms(wires.size()); - std::iota(materializedPrograms.begin(), materializedPrograms.end(), 0); - if (comp->hasTwoQubitOperations) { - RoutingBundle preview{.wires = wires, .infos = infos, .layout = *layout}; - Statistics previewStats; - if (failed(route(preview, previewStats))) { - func.emitError() << "failed to plan target routing"; - signalPassFailure(); - return; - } - for (const auto prog : previewStats.touchedPrograms) { - if (prog >= wires.size()) { - materializedPrograms.emplace_back(prog); - } - } - std::ranges::sort(materializedPrograms); - } - - std::tie(wires, infos) = std::move( - place(body, *layout, *target, materializedPrograms, *comp, rewriter)); + std::tie(wires, infos) = std::move(place(body, *layout, *comp, rewriter)); Statistics stats; RoutingBundle bundle{.wires = std::move(wires), @@ -600,6 +579,7 @@ struct MappingPass : impl::MappingPassBase { "function body"; return WalkResult::interrupt(); }); + if (discovery.wasInterrupted()) { return failure(); } @@ -647,19 +627,18 @@ struct MappingPass : impl::MappingPassBase { /// /// Analogously to the discoverComputation function, the i-th extract /// operation defines the i-th program qubit. - static std::pair - place(Region& body, const Layout& layout, - const CompilerTarget& compilerTarget, - const ArrayRef materializedPrograms, Computation& computation, - IRRewriter& rewriter) { - SmallVector staticQubits(layout.nqubits()); + std::pair place(Region& body, const Layout& layout, + Computation& computation, + IRRewriter& rewriter) { + SmallVector staticQubits; + staticQubits.reserve(target->numQubits()); + // Create and save static qubit operations. rewriter.setInsertionPointToStart(&body.front()); - for (const auto prog : materializedPrograms) { - const auto hw = layout.getHardwareIndex(prog); - const auto site = compilerTarget.siteForVertex(hw); + for (size_t hw = 0; hw < layout.nqubits(); ++hw) { + const auto site = target->siteForVertex(hw); auto op = StaticOp::create(rewriter, body.getLoc(), site); - staticQubits[prog] = op.getQubit(); + staticQubits.emplace_back(op.getQubit()); rewriter.setInsertionPointAfter(op); } @@ -668,8 +647,8 @@ struct MappingPass : impl::MappingPassBase { for (auto alloc : computation.scalarAllocations) { const auto prog = wires.size(); - const auto qubit = staticQubits[prog]; - assert(qubit && "expected program qubit to be materialized"); + const auto hw = layout.getHardwareIndex(prog); + const auto qubit = staticQubits[hw]; rewriter.replaceAllUsesWith(alloc.getResult(), qubit); rewriter.eraseOp(alloc); @@ -683,8 +662,8 @@ struct MappingPass : impl::MappingPassBase { TypeSwitch(operation) .Case([&](auto op) { const auto prog = wires.size(); - const auto qubit = staticQubits[prog]; - assert(qubit && "expected program qubit to be materialized"); + const auto hw = layout.getHardwareIndex(prog); + const auto qubit = staticQubits[hw]; rewriter.replaceAllUsesWith(op.getResult(), qubit); rewriter.replaceAllUsesWith(op.getOutTensor(), op.getTensor()); @@ -705,20 +684,24 @@ struct MappingPass : impl::MappingPassBase { rewriter.eraseOp(tensor.allocation); } - const auto numProgramQubits = wires.size(); + // Create sinks for remaining, unused, static qubits. + rewriter.setInsertionPoint(body.back().getTerminator()); - for (const auto prog : materializedPrograms) { - if (prog < numProgramQubits) { - continue; - } - const auto qubit = staticQubits[prog]; + for (size_t prog = wires.size(); prog < layout.nqubits(); ++prog) { + const auto hw = layout.getHardwareIndex(prog); + const auto site = target->siteForVertex(hw); + const auto qubit = staticQubits[site]; wires.emplace_back(qubit); - infos.insertOrUpdate(wires.size() - 1, prog); + infos.insertOrUpdate(prog, prog); SinkOp::create(rewriter, body.getLoc(), qubit); } + // Finally, update the SCF operations such that they take all static qubits + // as input. To handle recursively nested SCF operations, use a stack of + // (region, mapping) pairs. + SmallVector>> stack; stack.emplace_back(body, DenseSet{}); @@ -736,7 +719,7 @@ struct MappingPass : impl::MappingPassBase { } }) .Case([&](scf::ForOp forOp) { - assert(qubits.size() == materializedPrograms.size()); + assert(qubits.size() == layout.nqubits()); llvm::for_each(getQubitValues(forOp.getInits()), [&](Value v) { qubits.erase(v); }); @@ -757,7 +740,7 @@ struct MappingPass : impl::MappingPassBase { DenseSet(regionQubits.begin(), regionQubits.end())); }) .Case([&](scf::WhileOp whileOp) { - assert(qubits.size() == materializedPrograms.size()); + assert(qubits.size() == layout.nqubits()); llvm::for_each(getQubitValues(whileOp.getInits()), [&](Value v) { qubits.erase(v); }); @@ -783,7 +766,7 @@ struct MappingPass : impl::MappingPassBase { DenseSet(afterArgs.begin(), afterArgs.end())); }) .Case([&](IfOp ifOp) { - assert(qubits.size() == materializedPrograms.size()); + assert(qubits.size() == layout.nqubits()); llvm::for_each(ifOp.getQubits(), [&](Value v) { qubits.erase(v); }); @@ -806,7 +789,7 @@ struct MappingPass : impl::MappingPassBase { DenseSet(elseArgs.begin(), elseArgs.end())); }) .Case([&](IndexSwitchOp switchOp) { - assert(qubits.size() == materializedPrograms.size()); + assert(qubits.size() == layout.nqubits()); llvm::for_each(switchOp.getTargets(), [&](Value value) { qubits.erase(value); }); @@ -1202,8 +1185,6 @@ struct MappingPass : impl::MappingPassBase { auto& [wires, infos, layout] = bundle; for (const auto& [hw0, hw1] : swaps) { const auto [prog0, prog1] = layout.getProgramIndices(hw0, hw1); - stats.touchedPrograms.insert(prog0); - stats.touchedPrograms.insert(prog1); if constexpr (Mode == RoutingMode::Hot) { assert(infos.containsProgram(prog0) && infos.containsProgram(prog1) && diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp index fa7d68f75d..347ae4a635 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include @@ -215,20 +217,26 @@ static bool isExecutable(func::FuncOp entry, const CompilerTarget& target) { return isExecutable(entry.getFunctionBody(), m, target); } -/// Return a 3x3 square-grid compiler target. -static CompilerTarget getNineQubitSquareGrid() { - return CompilerTarget(9, std::vector{{0, 1}, - {0, 3}, - {1, 2}, - {1, 4}, - {2, 5}, - {3, 4}, - {3, 6}, - {4, 5}, - {4, 7}, - {5, 8}, - {6, 7}, - {7, 8}}); +/// Return a nxn square-grid compiler target. +static CompilerTarget getSquareGridTarget(const size_t n) { + const auto numTarget = n * n; + + std::vector couplings; + couplings.reserve(n * n); + + for (size_t r = 0; r < n; ++r) { + for (size_t c = 0; c < n; ++c) { + const auto i = (r * n) + c; + if (c + 1 < n) { + couplings.emplace_back(i, i + 1); + } + if (r + 1 < n) { + couplings.emplace_back(i, i + n); + } + } + } + + return CompilerTarget(numTarget, std::move(couplings)); } /// Creates an N-qubit GHZ state, where N = `qubits.size()` using @@ -272,10 +280,19 @@ static void loopGHZ(QCOProgramBuilder& builder, Value& tensor, .front(); } +/// Creates an N-qubit CX/CZ circuit. +static void cxcz(QCOProgramBuilder& builder, SmallVector& qubits) { + for (size_t i = 0; i + 1 < qubits.size(); ++i) { + std::tie(qubits[i], qubits[i + 1]) = builder.cx(qubits[i], qubits[i + 1]); + } + for (size_t i = 0; i + 2 < qubits.size(); ++i) { + std::tie(qubits[i], qubits[i + 2]) = builder.cz(qubits[i], qubits[i + 2]); + } +} + namespace { -class MappingPassTest : public testing::Test, - public testing::WithParamInterface { +class MappingPassFixture : public testing::Test { protected: void SetUp() override { DialectRegistry registry; @@ -290,14 +307,146 @@ class MappingPassTest : public testing::Test, const MappingPassOptions& options) { PassManager pm(m->getContext()); pm.addPass(createMappingPass(target, options)); - return pm.run(m); + if (failed(pm.run(m))) { + return failure(); + } + + RewritePatternSet patterns(m.getContext()); + SinkOp::getCanonicalizationPatterns(patterns, m.getContext()); + return applyPatternsGreedily(m, std::move(patterns)); } std::unique_ptr context; }; +class MappingPassTest : public MappingPassFixture, + public testing::WithParamInterface {}; + }; // namespace +TEST_F(MappingPassFixture, MapTopologyOnlyWithEmptyOperationSet) { + constexpr int64_t size = 3; + + const CompilerTarget target( + 3, std::vector{{0, 1}, {1, 2}}, + std::vector{}); + + QCOProgramBuilder builder(context.get()); + builder.initialize(SmallVector(size, builder.getI1Type())); + + SmallVector qubits(size); + SmallVector bits(size); + + for (int64_t i = 0; i < size; ++i) { + qubits[i] = builder.allocQubit(); + } + + qubits[0] = builder.x(qubits[0]); + std::tie(qubits[0], qubits[1]) = builder.rxx(0.25, qubits[0], qubits[1]); + std::tie(qubits[1], qubits[2]) = builder.rzx(0.5, qubits[1], qubits[2]); + std::tie(qubits[0], qubits[2]) = builder.cx(qubits[0], qubits[2]); + + for (int64_t i = 0; i < qubits.size(); ++i) { + std::tie(qubits[i], bits[i]) = builder.measure(qubits[i]); + builder.sink(qubits[i]); + } + + auto m = builder.finalize(bits); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); + + size_t numSwaps = 0; + m->walk([&](SWAPOp) { ++numSwaps; }); + EXPECT_GT(numSwaps, 0); +} + +TEST_F(MappingPassFixture, PreserveNoncontiguousTargetSiteIds) { + constexpr int64_t size = 3; + + std::vector sites; + sites.emplace_back(7); + sites.emplace_back(19); + sites.emplace_back(42); + + const CompilerTarget target( + std::move(sites), + std::vector{{7, 19}, {19, 42}}, + std::vector{}); + + QCOProgramBuilder builder(context.get()); + builder.initialize(SmallVector(size, builder.getI1Type())); + + SmallVector qubits(size); + SmallVector bits(size); + + for (int64_t i = 0; i < size; ++i) { + qubits[i] = builder.allocQubit(); + } + + std::tie(qubits[0], qubits[1]) = builder.cx(qubits[0], qubits[1]); + std::tie(qubits[1], qubits[2]) = builder.cz(qubits[1], qubits[2]); + std::tie(qubits[0], qubits[2]) = builder.cx(qubits[0], qubits[2]); + for (int64_t i = 0; i < qubits.size(); ++i) { + std::tie(qubits[i], bits[i]) = builder.measure(qubits[i]); + builder.sink(qubits[i]); + } + + auto m = builder.finalize(bits); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); + + const DenseSet expectedSites{7, 19, 42}; + size_t numStatics = 0; + m->walk([&](StaticOp op) { + ++numStatics; + EXPECT_TRUE(expectedSites.contains(op.getIndex())); + }); + EXPECT_EQ(numStatics, 3); +} + +TEST_F(MappingPassFixture, KeepWorkspaceSparseOnLargeTarget) { + constexpr size_t numTargetQubits = 64; + std::vector couplings; + couplings.reserve(numTargetQubits - 1); + for (size_t site = 1; site < numTargetQubits; ++site) { + couplings.emplace_back(0, static_cast(site)); + } + + const CompilerTarget target(numTargetQubits, std::move(couplings)); + + QCOProgramBuilder builder(context.get()); + builder.initialize(SmallVector(2, builder.getI1Type())); + + SmallVector bits(2); + Value q0 = builder.allocQubit(); + Value q1 = builder.allocQubit(); + std::tie(q0, q1) = builder.cx(q0, q1); + std::tie(q0, bits[0]) = builder.measure(q0); + std::tie(q1, bits[1]) = builder.measure(q1); + builder.sink(q0); + builder.sink(q1); + + auto m = builder.finalize(bits); + ASSERT_TRUE(runPass(m.get(), target, + MappingPassOptions{.niterations = 1, .ntrials = 1}) + .succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); + + size_t numStatics = 0; + size_t numSinks = 0; + m->walk([&](StaticOp) { ++numStatics; }); + m->walk([&](SinkOp) { ++numSinks; }); + EXPECT_GE(numStatics, 2); + EXPECT_LE(numStatics, 3); + EXPECT_LT(numStatics, numTargetQubits); + EXPECT_EQ(numSinks, numStatics); +} + TEST_P(MappingPassTest, FailNoEntryPoint) { const auto& target = GetParam(); @@ -357,16 +506,16 @@ TEST_P(MappingPassTest, MapMixedScalarAndTensorAllocations) { tensor = builder.qtensorInsert(tensorQubit1, tensor, 1); builder.qtensorDealloc(tensor); - auto module = builder.finalize(); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + auto m = builder.finalize(); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); size_t numScalarAllocations = 0; size_t numTensorAllocations = 0; - module->walk([&](AllocOp) { ++numScalarAllocations; }); - module->walk([&](qtensor::AllocOp) { ++numTensorAllocations; }); + m->walk([&](AllocOp) { ++numScalarAllocations; }); + m->walk([&](qtensor::AllocOp) { ++numTensorAllocations; }); EXPECT_EQ(numScalarAllocations, 0); EXPECT_EQ(numTensorAllocations, 0); } @@ -389,19 +538,20 @@ TEST_P(MappingPassTest, MapProgramAfterQubitReuse) { std::tie(q1, bit1) = builder.measure(q1); builder.sink(q1); - auto module = builder.finalize({bit0, bit1}); + auto m = builder.finalize({bit0, bit1}); PassManager pm(context.get()); pm.addPass(createReuseQubits()); pm.addPass(createCanonicalizerPass()); pm.addPass(createMappingPass(target, MappingPassOptions{.ntrials = 1})); - ASSERT_TRUE(pm.run(module.get()).succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + pm.addPass(createCanonicalizerPass()); + ASSERT_TRUE(pm.run(m.get()).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); size_t numStatics = 0; size_t numResets = 0; - module->walk([&](StaticOp) { ++numStatics; }); - module->walk([&](ResetOp) { ++numResets; }); + m->walk([&](StaticOp) { ++numStatics; }); + m->walk([&](ResetOp) { ++numResets; }); EXPECT_EQ(numStatics, 1); EXPECT_EQ(numResets, 1); } @@ -426,16 +576,16 @@ TEST_P(MappingPassTest, FailNestedScalarAllocation) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); std::string diagnostics; ScopedDiagnosticHandler handler(context.get(), [&](Diagnostic& diagnostic) { diagnostics += diagnostic.str(); return success(); }); - EXPECT_TRUE(failed(runPass(module.get(), target, MappingPassOptions{}))); + EXPECT_TRUE(failed(runPass(m.get(), target, MappingPassOptions{}))); EXPECT_TRUE( StringRef(diagnostics) .contains( @@ -465,16 +615,16 @@ TEST_P(MappingPassTest, FailNestedTensorAllocation) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); std::string diagnostics; ScopedDiagnosticHandler handler(context.get(), [&](Diagnostic& diagnostic) { diagnostics += diagnostic.str(); return success(); }); - EXPECT_TRUE(failed(runPass(module.get(), target, MappingPassOptions{}))); + EXPECT_TRUE(failed(runPass(m.get(), target, MappingPassOptions{}))); EXPECT_TRUE( StringRef(diagnostics) .contains( @@ -501,13 +651,13 @@ TEST_P(MappingPassTest, FailNestedHigherArityUnitary) { builder.sink(qubit); } - auto module = builder.finalize(); + auto m = builder.finalize(); std::string diagnostics; ScopedDiagnosticHandler handler(context.get(), [&](Diagnostic& diagnostic) { diagnostics += diagnostic.str(); return success(); }); - EXPECT_TRUE(failed(runPass(module.get(), target, MappingPassOptions{}))); + EXPECT_TRUE(failed(runPass(m.get(), target, MappingPassOptions{}))); EXPECT_TRUE( StringRef(diagnostics) .contains("decompose it to one- and two-qubit operations first")) @@ -515,8 +665,8 @@ TEST_P(MappingPassTest, FailNestedHigherArityUnitary) { size_t numAllocations = 0; size_t numStatics = 0; - module->walk([&](AllocOp) { ++numAllocations; }); - module->walk([&](StaticOp) { ++numStatics; }); + m->walk([&](AllocOp) { ++numAllocations; }); + m->walk([&](StaticOp) { ++numStatics; }); EXPECT_EQ(numAllocations, 3); EXPECT_EQ(numStatics, 0); } @@ -578,105 +728,6 @@ TEST_P(MappingPassTest, FailTooManyQubitsForArch) { ASSERT_TRUE(res.failed()); } -TEST_P(MappingPassTest, MapTopologyOnlyWithEmptyOperationSet) { - const CompilerTarget target( - 3, std::vector{{0, 1}, {1, 2}}, - std::vector{}); - - QCOProgramBuilder builder(context.get()); - builder.initialize(); - SmallVector qubits{builder.allocQubit(), builder.allocQubit(), - builder.allocQubit()}; - - qubits[0] = builder.x(qubits[0]); - std::tie(qubits[0], qubits[1]) = builder.rxx(0.25, qubits[0], qubits[1]); - std::tie(qubits[1], qubits[2]) = builder.rzx(0.5, qubits[1], qubits[2]); - std::tie(qubits[0], qubits[2]) = builder.cx(qubits[0], qubits[2]); - for (const auto qubit : qubits) { - builder.sink(qubit); - } - - auto module = builder.finalize(); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); - - size_t numSwaps = 0; - module->walk([&](SWAPOp) { ++numSwaps; }); - EXPECT_GT(numSwaps, 0); -} - -TEST_P(MappingPassTest, PreserveNoncontiguousTargetSiteIds) { - std::vector sites; - sites.emplace_back(7); - sites.emplace_back(19); - sites.emplace_back(42); - const CompilerTarget target( - std::move(sites), - std::vector{{7, 19}, {19, 42}}, - std::vector{}); - - QCOProgramBuilder builder(context.get()); - builder.initialize(); - SmallVector qubits{builder.allocQubit(), builder.allocQubit(), - builder.allocQubit()}; - std::tie(qubits[0], qubits[1]) = builder.cx(qubits[0], qubits[1]); - std::tie(qubits[1], qubits[2]) = builder.cz(qubits[1], qubits[2]); - std::tie(qubits[0], qubits[2]) = builder.cx(qubits[0], qubits[2]); - for (const auto qubit : qubits) { - builder.sink(qubit); - } - - auto module = builder.finalize(); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); - - const DenseSet expectedSites{7, 19, 42}; - size_t numStatics = 0; - module->walk([&](StaticOp op) { - ++numStatics; - EXPECT_TRUE(expectedSites.contains(op.getIndex())); - }); - EXPECT_EQ(numStatics, 3); -} - -TEST_P(MappingPassTest, KeepWorkspaceSparseOnLargeTarget) { - constexpr size_t numTargetQubits = 64; - std::vector couplings; - couplings.reserve(numTargetQubits - 1); - for (size_t site = 1; site < numTargetQubits; ++site) { - couplings.emplace_back(0, static_cast(site)); - } - const CompilerTarget target(numTargetQubits, std::move(couplings)); - - QCOProgramBuilder builder(context.get()); - builder.initialize(); - Value q0 = builder.allocQubit(); - Value q1 = builder.allocQubit(); - std::tie(q0, q1) = builder.cx(q0, q1); - builder.sink(q0); - builder.sink(q1); - - auto module = builder.finalize(); - ASSERT_TRUE(runPass(module.get(), target, - MappingPassOptions{.niterations = 1, .ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); - - size_t numStatics = 0; - size_t numSinks = 0; - module->walk([&](StaticOp) { ++numStatics; }); - module->walk([&](SinkOp) { ++numSinks; }); - EXPECT_GE(numStatics, 2); - EXPECT_LE(numStatics, 3); - EXPECT_LT(numStatics, numTargetQubits); - EXPECT_EQ(numSinks, numStatics); -} - TEST_P(MappingPassTest, MapFlatGHZ) { const auto& target = GetParam(); const int64_t size = 3; @@ -707,11 +758,10 @@ TEST_P(MappingPassTest, MapFlatGHZ) { builder.qtensorDealloc(tensor); auto m = builder.finalize(bits); - auto res = runPass(m.get(), target, MappingPassOptions{}); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapLoopBasedGHZByUnrolling) { @@ -751,11 +801,9 @@ TEST_P(MappingPassTest, MapLoopBasedGHZByUnrolling) { builder.qtensorDealloc(tensor); auto m = builder.finalize(bits); - auto res = pm.run(m.get()); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE(pm.run(m.get()).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapGroverLike) { @@ -765,11 +813,8 @@ TEST_P(MappingPassTest, MapGroverLike) { SmallVector qubits(size); SmallVector bits(size); - PassManager pm(context.get()); - pm.addPass(createMappingPass(target, MappingPassOptions{})); - QCOProgramBuilder builder(context.get()); - builder.initialize(SmallVector(5, builder.getI1Type())); + builder.initialize(SmallVector(size, builder.getI1Type())); Value tensor = builder.qtensorAlloc(4); Value flagTensor = builder.qtensorAlloc(1); @@ -817,11 +862,10 @@ TEST_P(MappingPassTest, MapGroverLike) { builder.qtensorDealloc(flagTensor); auto m = builder.finalize(bits); - auto res = pm.run(m.get()); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapParallelLoops) { @@ -831,9 +875,6 @@ TEST_P(MappingPassTest, MapParallelLoops) { SmallVector qubits(size); SmallVector bits(size); - PassManager pm(context.get()); - pm.addPass(createMappingPass(target, MappingPassOptions{})); - QCOProgramBuilder builder(context.get()); builder.initialize(SmallVector(size, builder.getI1Type())); @@ -896,11 +937,10 @@ TEST_P(MappingPassTest, MapParallelLoops) { builder.qtensorDealloc(tensor); auto m = builder.finalize(bits); - auto res = pm.run(m.get()); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapForWithClassicalIterArg) { @@ -938,14 +978,13 @@ TEST_P(MappingPassTest, MapForWithClassicalIterArg) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(verify(*module).succeeded()); - - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - EXPECT_TRUE(verify(*module).succeeded()); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(verify(*m).succeeded()); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + EXPECT_TRUE(verify(*m).succeeded()); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapTypeChangingWhileWithClassicalState) { @@ -989,14 +1028,14 @@ TEST_P(MappingPassTest, MapTypeChangingWhileWithClassicalState) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(verify(*module).succeeded()); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(verify(*m).succeeded()); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - EXPECT_TRUE(verify(*module).succeeded()); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + EXPECT_TRUE(verify(*m).succeeded()); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapIfWithClassicalResult) { @@ -1037,17 +1076,17 @@ TEST_P(MappingPassTest, MapIfWithClassicalResult) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); IfOp ifOp; - module->walk([&](IfOp candidate) { ifOp = candidate; }); + m->walk([&](IfOp candidate) { ifOp = candidate; }); ASSERT_TRUE(ifOp); ASSERT_EQ(ifOp.getClassicalResults().size(), 1); EXPECT_TRUE(ifOp.getClassicalResults().front().getType().isInteger(64)); @@ -1102,17 +1141,17 @@ TEST_P(MappingPassTest, MapIndexSwitchWithClassicalResult) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); - EXPECT_TRUE(isExecutable(getEntryPoint(module.get()), target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); IndexSwitchOp switchOp; - module->walk([&](IndexSwitchOp candidate) { switchOp = candidate; }); + m->walk([&](IndexSwitchOp candidate) { switchOp = candidate; }); ASSERT_TRUE(switchOp); ASSERT_EQ(switchOp.getClassicalResults().size(), 1); EXPECT_TRUE(switchOp.getClassicalResults().front().getType().isInteger(64)); @@ -1123,7 +1162,7 @@ TEST_P(MappingPassTest, MapIndexSwitchWithClassicalResult) { } } -TEST_P(MappingPassTest, RouteIndexSwitchRegions) { +TEST_P(MappingPassTest, MapIndexSwitchRegions) { const auto& target = GetParam(); constexpr StringLiteral source = R"mlir( module { @@ -1172,20 +1211,20 @@ TEST_P(MappingPassTest, RouteIndexSwitchRegions) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - ASSERT_TRUE(succeeded(verify(*module))); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); size_t numSwaps = 0; - module->walk([&](SWAPOp) { ++numSwaps; }); + m->walk([&](SWAPOp) { ++numSwaps; }); EXPECT_GT(numSwaps, 3); } -TEST_P(MappingPassTest, RouteNestedOperationOnceWhileIndependentWiresAdvance) { +TEST_P(MappingPassTest, MapNestedOperationOnceWhileIndependentWiresAdvance) { const auto& target = GetParam(); constexpr StringLiteral source = R"mlir( module { @@ -1233,16 +1272,16 @@ TEST_P(MappingPassTest, RouteNestedOperationOnceWhileIndependentWiresAdvance) { } )mlir"; - auto module = parseSourceString(source, context.get()); - ASSERT_TRUE(module); - ASSERT_TRUE(succeeded(verify(*module))); + auto m = parseSourceString(source, context.get()); + ASSERT_TRUE(m); + ASSERT_TRUE(succeeded(verify(*m))); - ASSERT_TRUE(runPass(module.get(), target, MappingPassOptions{.ntrials = 1}) - .succeeded()); - EXPECT_TRUE(succeeded(verify(*module))); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + EXPECT_TRUE(succeeded(verify(*m))); size_t numIndexSwitches = 0; - module->walk([&](IndexSwitchOp) { ++numIndexSwitches; }); + m->walk([&](IndexSwitchOp) { ++numIndexSwitches; }); EXPECT_EQ(numIndexSwitches, 1); } @@ -1311,11 +1350,10 @@ TEST_P(MappingPassTest, MapSABRECircuit) { builder.qtensorDealloc(tensorDown); auto m = builder.finalize(bits); - auto res = runPass(m.get(), target, MappingPassOptions{}); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapBranchingGHZ) { @@ -1364,11 +1402,10 @@ TEST_P(MappingPassTest, MapBranchingGHZ) { builder.qtensorDealloc(tensor); auto m = builder.finalize(bits); - auto res = runPass(m.get(), target, MappingPassOptions{.ntrials = 1}); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapDoUntil) { @@ -1426,11 +1463,10 @@ TEST_P(MappingPassTest, MapDoUntil) { builder.qtensorDealloc(tensor); auto m = builder.finalize(); - auto res = runPass(m.get(), target, MappingPassOptions{.ntrials = 1}); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapNestedForSwitch) { @@ -1520,11 +1556,10 @@ TEST_P(MappingPassTest, MapNestedForSwitch) { builder.qtensorDealloc(tensor); auto m = builder.finalize(); - auto res = runPass(m.get(), target, MappingPassOptions{}); - auto entry = getEntryPoint(m.get()); - - ASSERT_TRUE(res.succeeded()); - EXPECT_TRUE(isExecutable(entry, target)); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); } TEST_P(MappingPassTest, MapIndexSwitchUsesVotedLayout) { @@ -1569,5 +1604,35 @@ TEST_P(MappingPassTest, MapIndexSwitchUsesVotedLayout) { EXPECT_EQ(numSwaps, 4UL); } -INSTANTIATE_TEST_SUITE_P(NineQubitSquareGrid, MappingPassTest, - testing::Values(getNineQubitSquareGrid())); +TEST_P(MappingPassTest, MapPaddedCXCZGrid) { + const auto& target = GetParam(); + const auto size = (target.numQubits() + 1) / 2; + + SmallVector qubits(size); + SmallVector bits(size); + + QCOProgramBuilder builder(context.get()); + builder.initialize(SmallVector(size, builder.getI1Type())); + + for (size_t i = 0; i < size; ++i) { + qubits[i] = builder.allocQubit(); + } + cxcz(builder, qubits); + for (int64_t i = 0; i < qubits.size(); ++i) { + std::tie(qubits[i], bits[i]) = builder.measure(qubits[i]); + builder.sink(qubits[i]); + } + + auto m = builder.finalize(bits); + ASSERT_TRUE( + runPass(m.get(), target, MappingPassOptions{.ntrials = 1}).succeeded()); + ASSERT_TRUE(succeeded(verify(*m))); + EXPECT_TRUE(isExecutable(getEntryPoint(m.get()), target)); +} + +INSTANTIATE_TEST_SUITE_P(ThreeByThreeSquareGrid, MappingPassTest, + testing::Values(getSquareGridTarget(3))); +INSTANTIATE_TEST_SUITE_P(FourByFourSquareGrid, MappingPassTest, + testing::Values(getSquareGridTarget(4))); +INSTANTIATE_TEST_SUITE_P(TenByTenSquareGrid, MappingPassTest, + testing::Values(getSquareGridTarget(10)));