Skip to content

Commit 6f0d146

Browse files
author
Li-ming Bao
committed
add tests
1 parent fde8ae3 commit 6f0d146

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

mlir/unittests/Dialect/QCO/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ target_link_libraries(mqt-core-mlir-qco-dialect-test PRIVATE GTest::gtest_main M
1414
gtest_discover_tests(mqt-core-mlir-qco-dialect-test)
1515

1616
add_subdirectory(Modifiers)
17+
add_subdirectory(Scf)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
2+
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# Licensed under the MIT License
8+
9+
add_executable(mqt-core-mlir-dialect-qco-ir-scf-test test_qco_if.cpp)
10+
11+
target_link_libraries(
12+
mqt-core-mlir-dialect-qco-ir-scf-test
13+
PRIVATE GTest::gtest_main
14+
MQT::CoreIR
15+
MLIRQCOProgramBuilder
16+
MLIRPass
17+
MLIRTransforms
18+
MLIRIR
19+
MLIRParser
20+
MLIRSupport
21+
LLVMSupport)
22+
23+
gtest_discover_tests(mqt-core-mlir-dialect-qco-ir-scf-test)
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
3+
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
4+
* All rights reserved.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*
8+
* Licensed under the MIT License
9+
*/
10+
11+
#include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h"
12+
#include "mlir/Dialect/QCO/IR/QCODialect.h"
13+
14+
#include <gtest/gtest.h>
15+
#include <mlir/Dialect/Arith/IR/Arith.h>
16+
#include <mlir/Dialect/Func/IR/FuncOps.h>
17+
#include <mlir/IR/Builders.h>
18+
#include <mlir/IR/BuiltinOps.h>
19+
#include <mlir/IR/Diagnostics.h>
20+
#include <mlir/IR/MLIRContext.h>
21+
#include <mlir/IR/OwningOpRef.h>
22+
#include <mlir/IR/Value.h>
23+
#include <mlir/IR/ValueRange.h>
24+
#include <mlir/IR/Verifier.h>
25+
#include <mlir/Parser/Parser.h>
26+
#include <mlir/Pass/PassManager.h>
27+
#include <mlir/Support/LLVM.h>
28+
#include <mlir/Support/WalkResult.h>
29+
#include <mlir/Transforms/Passes.h>
30+
31+
using namespace mlir;
32+
using namespace mlir::qco;
33+
34+
class QCOIfOpTest : public ::testing::Test {
35+
protected:
36+
MLIRContext context;
37+
QCOProgramBuilder builder;
38+
OwningOpRef<ModuleOp> module;
39+
40+
QCOIfOpTest() : builder(&context) {}
41+
42+
void SetUp() override {
43+
context.loadDialect<QCODialect>();
44+
context.loadDialect<func::FuncDialect>();
45+
context.loadDialect<arith::ArithDialect>();
46+
47+
// Setup Builder
48+
builder.initialize();
49+
}
50+
51+
IfOp buildOp() {
52+
// Build a basic program with 2 qubits and an if operation
53+
const auto q = builder.allocQubitRegister(2);
54+
auto [qubit, measureResult] = builder.measure(q[0]);
55+
56+
builder.qcoIf(
57+
measureResult, {qubit, q[1]},
58+
[&](ValueRange args) -> SmallVector<Value> {
59+
auto q2 = builder.h(args[0]);
60+
return {q2, args[1]};
61+
},
62+
[&](ValueRange args) -> SmallVector<Value> { return args; });
63+
auto ifOp = cast<IfOp>(builder.getBlock()->getOperations().back());
64+
module = builder.finalize();
65+
return ifOp;
66+
}
67+
};
68+
69+
TEST_F(QCOIfOpTest, TestBuilder) {
70+
auto ifOp = buildOp();
71+
72+
// Verify the operation structure
73+
EXPECT_EQ(ifOp.getQubits().size(), 2);
74+
EXPECT_EQ(ifOp.getResults().size(), 2);
75+
EXPECT_EQ(ifOp.thenYield()->getNumOperands(), 2);
76+
EXPECT_EQ(ifOp.elseYield()->getNumOperands(), 2);
77+
EXPECT_EQ(ifOp.thenBlock()->getArgumentTypes(),
78+
ifOp.elseBlock()->getArgumentTypes());
79+
EXPECT_EQ(ifOp->getResultTypes(), ifOp.getQubits().getTypes());
80+
EXPECT_EQ(ifOp->getResultTypes(), ifOp.thenBlock()->getArgumentTypes());
81+
82+
// Verify operation
83+
ASSERT_TRUE(mlir::verify(ifOp).succeeded());
84+
}
85+
86+
TEST_F(QCOIfOpTest, TestWrongType) {
87+
auto ifOp = buildOp();
88+
// Change the block argument type to a non-qubit
89+
auto* block = ifOp.thenBlock();
90+
block->getArgument(0).setType(builder.getI1Type());
91+
// Verify operation
92+
ASSERT_TRUE(mlir::verify(ifOp).failed());
93+
}
94+
95+
TEST_F(QCOIfOpTest, TestSameNumberOfBlockArgs) {
96+
const auto q = builder.allocQubitRegister(2);
97+
auto [qubit, measureResult] = builder.measure(q[0]);
98+
99+
builder.qcoIf(
100+
measureResult, {qubit, q[1]},
101+
[&](ValueRange args) -> SmallVector<Value> {
102+
auto q2 = builder.h(args[0]);
103+
return {q2, args[1]};
104+
},
105+
[&](ValueRange args) -> SmallVector<Value> { return args; });
106+
auto ifOp = cast<IfOp>(builder.getBlock()->getOperations().back());
107+
module = builder.finalize();
108+
109+
// Add an additional block argument in the then block
110+
auto* block = ifOp.thenBlock();
111+
block->addArgument(QubitType::get(&context), builder.getUnknownLoc());
112+
// Verify operation
113+
ASSERT_TRUE(mlir::verify(ifOp).failed());
114+
}
115+
116+
TEST_F(QCOIfOpTest, TestSameNumberOfOperandQubitsAndResult) {
117+
auto ifOp = buildOp();
118+
119+
// Add an additional block argument in both blocks
120+
const auto qcoType = QubitType::get(&context);
121+
auto* thenBlock = ifOp.thenBlock();
122+
thenBlock->addArgument(qcoType, builder.getUnknownLoc());
123+
auto* elseBlock = ifOp.elseBlock();
124+
elseBlock->addArgument(qcoType, builder.getUnknownLoc());
125+
// Verify operation
126+
ASSERT_TRUE(mlir::verify(ifOp).failed());
127+
}
128+
129+
TEST_F(QCOIfOpTest, TestConstantCondition) {
130+
// Build a qco.if with a constant condition
131+
const auto q = builder.allocQubitRegister(2);
132+
133+
builder.qcoIf(
134+
arith::ConstantOp::create(builder, builder.getBoolAttr(true)), q,
135+
[&](ValueRange args) -> SmallVector<Value> {
136+
auto q2 = builder.h(args[0]);
137+
return {q2, args[1]};
138+
},
139+
[&](ValueRange args) -> SmallVector<Value> { return args; });
140+
141+
module = builder.finalize();
142+
143+
// Run canonicalizer
144+
PassManager pm(&context);
145+
auto* moduleOp = module->getOperation();
146+
pm.addPass(createCanonicalizerPass());
147+
if (pm.run(moduleOp).failed()) {
148+
llvm::errs() << "Failed to run canonicalization passes.\n";
149+
}
150+
151+
bool hasIf = false;
152+
bool hasHOp = false;
153+
// Check that the qco.if operation is removed and the qco.h operation is still
154+
// there
155+
moduleOp->walk([&](Operation* op) {
156+
if (isa<IfOp>(op)) {
157+
hasIf = true;
158+
return WalkResult::interrupt();
159+
}
160+
if (isa<HOp>(op)) {
161+
hasHOp = true;
162+
}
163+
return WalkResult::advance();
164+
});
165+
166+
EXPECT_FALSE(hasIf);
167+
EXPECT_TRUE(hasHOp);
168+
}

0 commit comments

Comments
 (0)