From c8e72483f291d2d8fe31cdba08d2919845f82604 Mon Sep 17 00:00:00 2001 From: Kit Barton Date: Tue, 26 Mar 2024 18:06:56 -0400 Subject: [PATCH 1/2] Update getPhaseValue to return an mlir::Value. (#304) This PR changes the return value of the Pulse utility method getPhaseValue() to return an mlir::Value instead of a double. This is necessary when using parameters, as the phase offset can be specified as a parameter value in addition to a compile time constant. By returning the mlir::Value, the caller of phaseOffset has the ability to differentiate between the two scenarios and act accordingly. --- include/Dialect/Pulse/Utils/Utils.h | 5 ++--- lib/Dialect/Pulse/Utils/Utils.cpp | 17 ++++++----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/include/Dialect/Pulse/Utils/Utils.h b/include/Dialect/Pulse/Utils/Utils.h index f93bddd59..2cdea100c 100644 --- a/include/Dialect/Pulse/Utils/Utils.h +++ b/include/Dialect/Pulse/Utils/Utils.h @@ -41,9 +41,8 @@ Waveform_CreateOp getWaveformOp(PlayOp pulsePlayOp, Waveform_CreateOp getWaveformOp(PlayOp pulsePlayOp, CallSequenceStack_t &callSequenceOpStack); - -double getPhaseValue(ShiftPhaseOp shiftPhaseOp, - CallSequenceStack_t &callSequenceOpStack); +mlir::Value getPhaseValue(ShiftPhaseOp shiftPhaseOp, + CallSequenceStack_t &callSequenceOpStack); /// this function goes over all the blocks of the input pulse sequence, and for /// each block, it sorts the pulse ops within the block according to their diff --git a/lib/Dialect/Pulse/Utils/Utils.cpp b/lib/Dialect/Pulse/Utils/Utils.cpp index eb7fb113a..02cfe20ef 100644 --- a/lib/Dialect/Pulse/Utils/Utils.cpp +++ b/lib/Dialect/Pulse/Utils/Utils.cpp @@ -64,25 +64,20 @@ Waveform_CreateOp getWaveformOp(PlayOp pulsePlayOp, return waveformOp; } -double getPhaseValue(ShiftPhaseOp shiftPhaseOp, - CallSequenceStack_t &callSequenceOpStack) { +mlir::Value getPhaseValue(ShiftPhaseOp shiftPhaseOp, + CallSequenceStack_t &callSequenceOpStack) { auto phaseOffsetIndex = 0; mlir::Value phaseOffset = shiftPhaseOp.getPhaseOffset(); for (auto it = callSequenceOpStack.rbegin(); it != callSequenceOpStack.rend(); ++it) { - if (phaseOffset.isa()) { - phaseOffsetIndex = phaseOffset.dyn_cast().getArgNumber(); - phaseOffset = it->getOperand(phaseOffsetIndex); - } else + if (auto blockArg = dyn_cast(phaseOffset)) + phaseOffset = it->getOperand(blockArg.getArgNumber()); + else break; } - auto phaseOffsetOp = - dyn_cast(phaseOffset.getDefiningOp()); - if (!phaseOffsetOp) - phaseOffsetOp->emitError() << "Phase offset is not a ConstantFloatOp."; - return phaseOffsetOp.value().convertToDouble(); + return phaseOffset; } void sortOpsByTimepoint(SequenceOp &sequenceOp) { From d09f48cc4ac1ca908650dd9c20dfe73c98461da6 Mon Sep 17 00:00:00 2001 From: Yilun Zhao <40353317+Zhaoyilunnn@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:52:06 +0800 Subject: [PATCH 2/2] fix: support ~ operation, close #278 (#303) Hi Team, I've added rules to parse "~" symbol in qe-qasm: https://github.com/openqasm/qe-qasm/pull/29, at the side of qe-compiler, we need to generate a MLIR op for `ASTOpTypeBitNot` in `lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp`. After searching, I find that MLIR does not have arith operations directly related to "BitNot". In the context of OQ3, I believe "~cbit" and "!cbit" are equivalent in a "if" condition? So I just simply reused the processing logic of `ASTOpTypeLogicalNot`, which checks whether the target is a `I1Type` and also works for `ASTOpTypeBitNot` operation. It will also create an `CmpIPredicate::ne` operation. Willing to update my pr if you have better solutions Thanks, Yilun ## Changes 1. Update the commit id of qe-qasm project 2. Reuse the processing logic of `ASTOpTypeLogicalNot` for `ASTOpTypeBitNot`. ## Sample Given the following source file ```bash OPENQASM 3.0; qubit $0; gate x q {} bit[4] qc0_c0; if ((qc0_c0[0] & qc0_c0[1] | qc0_c0[0] & qc0_c0[2] | qc0_c0[1] & qc0_c0[2]) & ~(qc0_c0[0] & qc0_c0[1] & qc0_c0[2])) { x $0; } qc0_c0[3] = measure $0; ``` The compiler generates following MLIR ````bash module { oq3.declare_variable @qc0_c0 : !quir.cbit<4> func.func @x(%arg0: !quir.qubit<1>) { return } func.func @main() -> i32 { qcs.init %c0 = arith.constant 0 : index %c1000 = arith.constant 1000 : index %c1 = arith.constant 1 : index scf.for %arg0 = %c0 to %c1000 step %c1 { %dur = quir.constant #quir.duration<1.000000e+00> : !quir.duration quir.delay %dur, () : !quir.duration, () -> () qcs.shot_init {qcs.num_shots = 1000 : i32} %0 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> %c0_i4 = arith.constant 0 : i4 %1 = "oq3.cast"(%c0_i4) : (i4) -> !quir.cbit<4> oq3.variable_assign @qc0_c0 : !quir.cbit<4> = %1 %2 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %3 = oq3.cbit_extractbit(%2 : !quir.cbit<4>) [0] : i1 %4 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %5 = oq3.cbit_extractbit(%4 : !quir.cbit<4>) [1] : i1 %6 = oq3.cbit_and %3, %5 : i1 %7 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %8 = oq3.cbit_extractbit(%7 : !quir.cbit<4>) [0] : i1 %9 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %10 = oq3.cbit_extractbit(%9 : !quir.cbit<4>) [2] : i1 %11 = oq3.cbit_and %8, %10 : i1 %12 = oq3.cbit_or %6, %11 : i1 %13 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %14 = oq3.cbit_extractbit(%13 : !quir.cbit<4>) [1] : i1 %15 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %16 = oq3.cbit_extractbit(%15 : !quir.cbit<4>) [2] : i1 %17 = oq3.cbit_and %14, %16 : i1 %18 = oq3.cbit_or %12, %17 : i1 %19 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %20 = oq3.cbit_extractbit(%19 : !quir.cbit<4>) [0] : i1 %21 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %22 = oq3.cbit_extractbit(%21 : !quir.cbit<4>) [1] : i1 %23 = oq3.cbit_and %20, %22 : i1 %24 = oq3.variable_load @qc0_c0 : !quir.cbit<4> %25 = oq3.cbit_extractbit(%24 : !quir.cbit<4>) [2] : i1 %26 = oq3.cbit_and %23, %25 : i1 %true = arith.constant true %27 = arith.cmpi ne, %26, %true : i1 %28 = oq3.cbit_and %18, %27 : i1 scf.if %28 { quir.call_gate @x(%0) : (!quir.qubit<1>) -> () %false = arith.constant false } %29 = quir.measure(%0) : (!quir.qubit<1>) -> i1 oq3.cbit_assign_bit @qc0_c0<4> [3] : i1 = %29 } {qcs.shot_loop} qcs.finalize %c0_i32 = arith.constant 0 : i32 return %c0_i32 : i32 } } ``` --------- Co-authored-by: Thomas Alexander --- conan/qasm/conandata.yml | 2 +- conan/qasm/conanfile.py | 2 +- conandata.yml | 2 +- lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp | 1 + .../notes/add-bitnot-support-7ad288e5a87b7302.yaml | 6 ++++++ test/Frontend/OpenQASM3/bitops.qasm | 8 ++++++++ 6 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/add-bitnot-support-7ad288e5a87b7302.yaml diff --git a/conan/qasm/conandata.yml b/conan/qasm/conandata.yml index 5489a0b03..22ef11038 100644 --- a/conan/qasm/conandata.yml +++ b/conan/qasm/conandata.yml @@ -1,5 +1,5 @@ sources: - hash: "d59248cbb4cf8840c9720462f2569907b9506323" + hash: "a9cf9fa3599b2045941d154dc91aba5a45beabb7" requirements: - "gmp/6.2.1" - "mpfr/4.1.0" diff --git a/conan/qasm/conanfile.py b/conan/qasm/conanfile.py index fa6731c35..7df3c82af 100644 --- a/conan/qasm/conanfile.py +++ b/conan/qasm/conanfile.py @@ -17,7 +17,7 @@ class QasmConan(ConanFile): name = "qasm" - version = "0.3.0" + version = "0.3.1" url = "https://github.com/openqasm/qe-qasm.git" settings = "os", "compiler", "build_type", "arch" options = {"shared": [True, False], "examples": [True, False]} diff --git a/conandata.yml b/conandata.yml index 692cbc5c3..8a64801ba 100644 --- a/conandata.yml +++ b/conandata.yml @@ -7,4 +7,4 @@ requirements: - pybind11/2.11.1 - clang-tools-extra/17.0.5-0@ - llvm/17.0.5-0@ - - qasm/0.3.0@qss/stable + - qasm/0.3.1@qss/stable diff --git a/lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp b/lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp index 0320da550..308697878 100644 --- a/lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp +++ b/lib/Frontend/OpenQASM3/QUIRGenQASM3Visitor.cpp @@ -1847,6 +1847,7 @@ ExpressionValueType QUIRGenQASM3Visitor::visit_(const ASTUnaryOpNode *node) { } switch (node->GetOpType()) { + case ASTOpTypeBitNot: case ASTOpTypeLogicalNot: { const auto boolType = builder.getI1Type(); diff --git a/releasenotes/notes/add-bitnot-support-7ad288e5a87b7302.yaml b/releasenotes/notes/add-bitnot-support-7ad288e5a87b7302.yaml new file mode 100644 index 000000000..576cff9c4 --- /dev/null +++ b/releasenotes/notes/add-bitnot-support-7ad288e5a87b7302.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes failure to parse expressions with bitnot operation. Refer to + `#278 ` for more + details. diff --git a/test/Frontend/OpenQASM3/bitops.qasm b/test/Frontend/OpenQASM3/bitops.qasm index 8b10a663a..90368625c 100644 --- a/test/Frontend/OpenQASM3/bitops.qasm +++ b/test/Frontend/OpenQASM3/bitops.qasm @@ -107,3 +107,11 @@ f = e | d; // MLIR: [[NOT_CBIT:%.*]] = "oq3.cast"([[NOT]]) : (i1) -> !quir.cbit<1> // MLIR: oq3.variable_assign @f : !quir.cbit<1> = [[NOT_CBIT]] f = !f; + +// MLIR: [[F:%.*]] = oq3.variable_load @f : !quir.cbit<1> +// MLIR: [[BOOL_F:%.*]] = "oq3.cast"([[F]]) : (!quir.cbit<1>) -> i1 +// MLIR: [[TRUE:%.*]] = arith.constant true +// MLIR: [[NOT:%.*]] = arith.cmpi ne, [[BOOL_F]], [[TRUE]] : i1 +// MLIR: [[NOT_CBIT:%.*]] = "oq3.cast"([[NOT]]) : (i1) -> !quir.cbit<1> +// MLIR: oq3.variable_assign @f : !quir.cbit<1> = [[NOT_CBIT]] +f = ~f;