Loading mlir/parsers/qasm3/tests/test_alias_handler.cpp +102 −24 Original line number Diff line number Diff line Loading @@ -2,17 +2,15 @@ #include "gtest/gtest.h" TEST(qasm3VisitorTester, checkAlias) { { // Test 1: Alias by indices const std::string alias_by_indicies = R"#(OPENQASM 3; include "qelib1.inc"; qubit q[6]; // Test 1: Alias by indices // myreg[0,1,2] refers to the qubit q[1,3,5] let myreg = q[1, 3, 5]; // Apply x on qubits in the alias list for i in [0:3] { x myreg[i]; } // Use broadcast to make sure it work x myreg; // Measure all qubits bit m[6]; m = measure q; Loading @@ -26,39 +24,119 @@ QCOR_EXPECT_TRUE(m[2] == 0); QCOR_EXPECT_TRUE(m[3] == 1); QCOR_EXPECT_TRUE(m[4] == 0); QCOR_EXPECT_TRUE(m[5] == 1); )#"; auto mlir = qcor::mlir_compile("qasm3", alias_by_indicies, "test", qcor::OutputType::MLIR, true); std::cout << "MLIR:\n" << mlir << "\n"; EXPECT_FALSE(qcor::execute("qasm3", alias_by_indicies, "test")); } // Reset q to start next test reset q; bit m1[6]; m1 = measure q; QCOR_EXPECT_TRUE(m1[0] == 0); QCOR_EXPECT_TRUE(m1[1] == 0); QCOR_EXPECT_TRUE(m1[2] == 0); QCOR_EXPECT_TRUE(m1[3] == 0); QCOR_EXPECT_TRUE(m1[4] == 0); QCOR_EXPECT_TRUE(m1[5] == 0); // Test 2: Alias by slice: { const std::string alias_by_slice = R"#(OPENQASM 3; include "qelib1.inc"; qubit q[6]; // Range without step size // 0, 1, 2, 3 (inclusive) let myreg1 = q[0:3]; x myreg1; // Measure all qubits bit m2[6]; m2 = measure q; for j in [0:6] { print(m2[j]); } QCOR_EXPECT_TRUE(m2[0] == 1); QCOR_EXPECT_TRUE(m2[1] == 1); QCOR_EXPECT_TRUE(m2[2] == 1); QCOR_EXPECT_TRUE(m2[3] == 1); QCOR_EXPECT_TRUE(m2[4] == 0); QCOR_EXPECT_TRUE(m2[5] == 0); // Reset q to start next test reset q; // Range with step size (0, 2, 4) let myreg2 = q[0:2:5]; x myreg2; // Measure all qubits bit m3[6]; m3 = measure q; for k in [0:6] { print(m3[k]); } QCOR_EXPECT_TRUE(m3[0] == 1); QCOR_EXPECT_TRUE(m3[1] == 0); QCOR_EXPECT_TRUE(m3[2] == 1); QCOR_EXPECT_TRUE(m3[3] == 0); QCOR_EXPECT_TRUE(m3[4] == 1); QCOR_EXPECT_TRUE(m3[5] == 0); // Reset q to start next test reset q; // Range with negative step: // 4, 3, 2 let myreg3 = q[4:-1:2]; x myreg3; // Measure all qubits bit m4[6]; m4 = measure q; for i1 in [0:6] { print(m4[i1]); } QCOR_EXPECT_TRUE(m4[0] == 0); QCOR_EXPECT_TRUE(m4[1] == 0); QCOR_EXPECT_TRUE(m4[2] == 1); QCOR_EXPECT_TRUE(m4[3] == 1); QCOR_EXPECT_TRUE(m4[4] == 1); QCOR_EXPECT_TRUE(m4[5] == 0); // Reset q to start next test reset q; // Range with start = stop // This is q[5] let myreg4 = q[5:5]; x myreg4; // Measure all qubits bit m5[6]; m5 = measure q; for i2 in [0:6] { print(m5[i2]); } QCOR_EXPECT_TRUE(m5[0] == 0); QCOR_EXPECT_TRUE(m5[1] == 0); QCOR_EXPECT_TRUE(m5[2] == 0); QCOR_EXPECT_TRUE(m5[3] == 0); QCOR_EXPECT_TRUE(m5[4] == 0); QCOR_EXPECT_TRUE(m5[5] == 1); // Reset q to start next test reset q; // Range using negative indexing: // Last 3 qubits let myreg5 = q[-4:-1]; let myreg5 = q[-3:-1]; x myreg5; // Measure all qubits bit m6[6]; m6 = measure q; for i3 in [0:6] { print(m6[i3]); } QCOR_EXPECT_TRUE(m6[0] == 0); QCOR_EXPECT_TRUE(m6[1] == 0); QCOR_EXPECT_TRUE(m6[2] == 0); QCOR_EXPECT_TRUE(m6[3] == 1); QCOR_EXPECT_TRUE(m6[4] == 1); QCOR_EXPECT_TRUE(m6[5] == 1); )#"; auto mlir = qcor::mlir_compile("qasm3", alias_by_slice, "test", auto mlir = qcor::mlir_compile("qasm3", alias_by_indicies, "test", qcor::OutputType::MLIR, true); std::cout << "MLIR:\n" << mlir << "\n"; // EXPECT_FALSE(qcor::execute("qasm3", alias_by_slice, "test")); } EXPECT_FALSE(qcor::execute("qasm3", alias_by_indicies, "test")); } int main(int argc, char **argv) { Loading mlir/parsers/qasm3/visitor_handlers/alias_handler.cpp +56 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,35 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( // register auto alias = context->Identifier()->getText(); // Get the name and symbol Value of the original register // Get the name and symbol Value of the original register. // We need to determine the alias array size and cache it // for broadcast to work on the result array. auto allocated_variable = context->indexIdentifier()->Identifier()->getText(); auto allocated_symbol = symbol_table.get_symbol(allocated_variable); // Determine the first qreg size const auto get_qreg_size = [&](const std::string &qreg_name) { uint64_t nqubits; auto qreg_value = symbol_table.get_symbol(qreg_name); if (auto op = qreg_value.getDefiningOp<mlir::quantum::QallocOp>()) { nqubits = op.size().getLimitedValue(); } else { auto attributes = symbol_table.get_variable_attributes(qreg_name); if (!attributes.empty()) { try { nqubits = std::stoi(attributes[0]); } catch (...) { printErrorMessage("Could not infer qubit[] size from block argument.", context); } } else { printErrorMessage( "Could not infer qubit[] size from block argument. No size " "attribute for variable in symbol table.", context); } } return nqubits; }; // handle q[1, 3, 5] comma syntax if (context->indexIdentifier()->LBRACKET()) { Loading @@ -42,7 +68,8 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( builder.create<mlir::quantum::QaliasArrayAllocOp>( location, array_type, integer_attr, str_attr); // Add the alias register to the symbol table symbol_table.add_symbol(alias, alias_allocation); symbol_table.add_symbol(alias, alias_allocation, {std::to_string(n_expressions)}); auto counter = 0; for (auto expr : expressions) { Loading Loading @@ -103,6 +130,33 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( location, array_type, allocated_symbol, llvm::makeArrayRef(std::vector<mlir::Value>{ range_start_mlir_val, range_step_mlir_val, range_stop_mlir_val})); // Determine and cache the slice size for instruction broadcasting. const int64_t orig_size = get_qreg_size(allocated_variable); const auto slice_size_calc = [](int64_t orig_size, int64_t start, int64_t step, int64_t end) -> int64_t { // If step > 0 and lo > hi, or step < 0 and lo < hi, the range is empty. // Else for step > 0, if n values are in the range, the last one is // lo + (n-1)*step, which must be <= hi. Rearranging, // n <= (hi - lo)/step + 1, so taking the floor of the RHS gives // the proper value. assert(step != 0); // Convert to positive indices (if given as negative) const int64_t lo = start >= 0 ? start : orig_size + start; const int64_t hi = end >= 0 ? end : orig_size + end; if (lo == hi) { return 1; } if (step > 0 && lo < hi) { return 1 + (hi - lo) / step; } else if (step < 0 && lo > hi) { return 1 + (lo - hi) / (-step); } else { return 0; } }; const auto new_size = slice_size_calc(orig_size, range_start, range_step, range_stop); symbol_table.add_symbol(alias, array_slice, {std::to_string(new_size)}); } else { // handle concatenation } Loading mlir/transforms/quantum_to_llvm.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -864,7 +864,7 @@ class QarraySliceOpLowering : public ConversionPattern { variables.insert({name.str(), slice_array_call.getResult(0)}); // Remove the old QuantumDialect QarraySliceOp rewriter.eraseOp(op); rewriter.replaceOp(op, slice_array_call.getResult(0)); return success(); } Loading Loading
mlir/parsers/qasm3/tests/test_alias_handler.cpp +102 −24 Original line number Diff line number Diff line Loading @@ -2,17 +2,15 @@ #include "gtest/gtest.h" TEST(qasm3VisitorTester, checkAlias) { { // Test 1: Alias by indices const std::string alias_by_indicies = R"#(OPENQASM 3; include "qelib1.inc"; qubit q[6]; // Test 1: Alias by indices // myreg[0,1,2] refers to the qubit q[1,3,5] let myreg = q[1, 3, 5]; // Apply x on qubits in the alias list for i in [0:3] { x myreg[i]; } // Use broadcast to make sure it work x myreg; // Measure all qubits bit m[6]; m = measure q; Loading @@ -26,39 +24,119 @@ QCOR_EXPECT_TRUE(m[2] == 0); QCOR_EXPECT_TRUE(m[3] == 1); QCOR_EXPECT_TRUE(m[4] == 0); QCOR_EXPECT_TRUE(m[5] == 1); )#"; auto mlir = qcor::mlir_compile("qasm3", alias_by_indicies, "test", qcor::OutputType::MLIR, true); std::cout << "MLIR:\n" << mlir << "\n"; EXPECT_FALSE(qcor::execute("qasm3", alias_by_indicies, "test")); } // Reset q to start next test reset q; bit m1[6]; m1 = measure q; QCOR_EXPECT_TRUE(m1[0] == 0); QCOR_EXPECT_TRUE(m1[1] == 0); QCOR_EXPECT_TRUE(m1[2] == 0); QCOR_EXPECT_TRUE(m1[3] == 0); QCOR_EXPECT_TRUE(m1[4] == 0); QCOR_EXPECT_TRUE(m1[5] == 0); // Test 2: Alias by slice: { const std::string alias_by_slice = R"#(OPENQASM 3; include "qelib1.inc"; qubit q[6]; // Range without step size // 0, 1, 2, 3 (inclusive) let myreg1 = q[0:3]; x myreg1; // Measure all qubits bit m2[6]; m2 = measure q; for j in [0:6] { print(m2[j]); } QCOR_EXPECT_TRUE(m2[0] == 1); QCOR_EXPECT_TRUE(m2[1] == 1); QCOR_EXPECT_TRUE(m2[2] == 1); QCOR_EXPECT_TRUE(m2[3] == 1); QCOR_EXPECT_TRUE(m2[4] == 0); QCOR_EXPECT_TRUE(m2[5] == 0); // Reset q to start next test reset q; // Range with step size (0, 2, 4) let myreg2 = q[0:2:5]; x myreg2; // Measure all qubits bit m3[6]; m3 = measure q; for k in [0:6] { print(m3[k]); } QCOR_EXPECT_TRUE(m3[0] == 1); QCOR_EXPECT_TRUE(m3[1] == 0); QCOR_EXPECT_TRUE(m3[2] == 1); QCOR_EXPECT_TRUE(m3[3] == 0); QCOR_EXPECT_TRUE(m3[4] == 1); QCOR_EXPECT_TRUE(m3[5] == 0); // Reset q to start next test reset q; // Range with negative step: // 4, 3, 2 let myreg3 = q[4:-1:2]; x myreg3; // Measure all qubits bit m4[6]; m4 = measure q; for i1 in [0:6] { print(m4[i1]); } QCOR_EXPECT_TRUE(m4[0] == 0); QCOR_EXPECT_TRUE(m4[1] == 0); QCOR_EXPECT_TRUE(m4[2] == 1); QCOR_EXPECT_TRUE(m4[3] == 1); QCOR_EXPECT_TRUE(m4[4] == 1); QCOR_EXPECT_TRUE(m4[5] == 0); // Reset q to start next test reset q; // Range with start = stop // This is q[5] let myreg4 = q[5:5]; x myreg4; // Measure all qubits bit m5[6]; m5 = measure q; for i2 in [0:6] { print(m5[i2]); } QCOR_EXPECT_TRUE(m5[0] == 0); QCOR_EXPECT_TRUE(m5[1] == 0); QCOR_EXPECT_TRUE(m5[2] == 0); QCOR_EXPECT_TRUE(m5[3] == 0); QCOR_EXPECT_TRUE(m5[4] == 0); QCOR_EXPECT_TRUE(m5[5] == 1); // Reset q to start next test reset q; // Range using negative indexing: // Last 3 qubits let myreg5 = q[-4:-1]; let myreg5 = q[-3:-1]; x myreg5; // Measure all qubits bit m6[6]; m6 = measure q; for i3 in [0:6] { print(m6[i3]); } QCOR_EXPECT_TRUE(m6[0] == 0); QCOR_EXPECT_TRUE(m6[1] == 0); QCOR_EXPECT_TRUE(m6[2] == 0); QCOR_EXPECT_TRUE(m6[3] == 1); QCOR_EXPECT_TRUE(m6[4] == 1); QCOR_EXPECT_TRUE(m6[5] == 1); )#"; auto mlir = qcor::mlir_compile("qasm3", alias_by_slice, "test", auto mlir = qcor::mlir_compile("qasm3", alias_by_indicies, "test", qcor::OutputType::MLIR, true); std::cout << "MLIR:\n" << mlir << "\n"; // EXPECT_FALSE(qcor::execute("qasm3", alias_by_slice, "test")); } EXPECT_FALSE(qcor::execute("qasm3", alias_by_indicies, "test")); } int main(int argc, char **argv) { Loading
mlir/parsers/qasm3/visitor_handlers/alias_handler.cpp +56 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,35 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( // register auto alias = context->Identifier()->getText(); // Get the name and symbol Value of the original register // Get the name and symbol Value of the original register. // We need to determine the alias array size and cache it // for broadcast to work on the result array. auto allocated_variable = context->indexIdentifier()->Identifier()->getText(); auto allocated_symbol = symbol_table.get_symbol(allocated_variable); // Determine the first qreg size const auto get_qreg_size = [&](const std::string &qreg_name) { uint64_t nqubits; auto qreg_value = symbol_table.get_symbol(qreg_name); if (auto op = qreg_value.getDefiningOp<mlir::quantum::QallocOp>()) { nqubits = op.size().getLimitedValue(); } else { auto attributes = symbol_table.get_variable_attributes(qreg_name); if (!attributes.empty()) { try { nqubits = std::stoi(attributes[0]); } catch (...) { printErrorMessage("Could not infer qubit[] size from block argument.", context); } } else { printErrorMessage( "Could not infer qubit[] size from block argument. No size " "attribute for variable in symbol table.", context); } } return nqubits; }; // handle q[1, 3, 5] comma syntax if (context->indexIdentifier()->LBRACKET()) { Loading @@ -42,7 +68,8 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( builder.create<mlir::quantum::QaliasArrayAllocOp>( location, array_type, integer_attr, str_attr); // Add the alias register to the symbol table symbol_table.add_symbol(alias, alias_allocation); symbol_table.add_symbol(alias, alias_allocation, {std::to_string(n_expressions)}); auto counter = 0; for (auto expr : expressions) { Loading Loading @@ -103,6 +130,33 @@ antlrcpp::Any qasm3_visitor::visitAliasStatement( location, array_type, allocated_symbol, llvm::makeArrayRef(std::vector<mlir::Value>{ range_start_mlir_val, range_step_mlir_val, range_stop_mlir_val})); // Determine and cache the slice size for instruction broadcasting. const int64_t orig_size = get_qreg_size(allocated_variable); const auto slice_size_calc = [](int64_t orig_size, int64_t start, int64_t step, int64_t end) -> int64_t { // If step > 0 and lo > hi, or step < 0 and lo < hi, the range is empty. // Else for step > 0, if n values are in the range, the last one is // lo + (n-1)*step, which must be <= hi. Rearranging, // n <= (hi - lo)/step + 1, so taking the floor of the RHS gives // the proper value. assert(step != 0); // Convert to positive indices (if given as negative) const int64_t lo = start >= 0 ? start : orig_size + start; const int64_t hi = end >= 0 ? end : orig_size + end; if (lo == hi) { return 1; } if (step > 0 && lo < hi) { return 1 + (hi - lo) / step; } else if (step < 0 && lo > hi) { return 1 + (lo - hi) / (-step); } else { return 0; } }; const auto new_size = slice_size_calc(orig_size, range_start, range_step, range_stop); symbol_table.add_symbol(alias, array_slice, {std::to_string(new_size)}); } else { // handle concatenation } Loading
mlir/transforms/quantum_to_llvm.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -864,7 +864,7 @@ class QarraySliceOpLowering : public ConversionPattern { variables.insert({name.str(), slice_array_call.getResult(0)}); // Remove the old QuantumDialect QarraySliceOp rewriter.eraseOp(op); rewriter.replaceOp(op, slice_array_call.getResult(0)); return success(); } Loading