From bee1b401a5c0cba30a123fa6158a7e7b087569cd Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Thu, 8 Jan 2026 16:24:45 -0500 Subject: [PATCH 01/11] Add initial definitions of logical, QEC and physical qubit types --- mlir/include/Quantum/IR/CMakeLists.txt | 4 +++ mlir/include/Quantum/IR/QuantumDialect.h | 5 ++++ mlir/include/Quantum/IR/QuantumDialect.td | 28 ++++++++++++++++++ .../Quantum/IR/QuantumTypeInterfaces.h | 21 ++++++++++++++ .../Quantum/IR/QuantumTypeInterfaces.td | 29 +++++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 mlir/include/Quantum/IR/QuantumTypeInterfaces.h create mode 100644 mlir/include/Quantum/IR/QuantumTypeInterfaces.td diff --git a/mlir/include/Quantum/IR/CMakeLists.txt b/mlir/include/Quantum/IR/CMakeLists.txt index 58401de302..a88e66142b 100644 --- a/mlir/include/Quantum/IR/CMakeLists.txt +++ b/mlir/include/Quantum/IR/CMakeLists.txt @@ -4,6 +4,10 @@ add_mlir_doc(QuantumDialect QuantumDialect Quantum/ -gen-dialect-doc) add_mlir_doc(QuantumOps QuantumOps Quantum/ -gen-op-doc) add_mlir_doc(QuantumInterfaces QuantumInterfaces Quantum/ -gen-op-interface-docs) +set(LLVM_TARGET_DEFINITIONS QuantumTypeInterfaces.td) +mlir_tablegen(QuantumTypeInterfaces.h.inc -gen-type-interface-decls) +mlir_tablegen(QuantumTypeInterfaces.cpp.inc -gen-type-interface-defs) + set(LLVM_TARGET_DEFINITIONS QuantumAttrDefs.td) mlir_tablegen(QuantumEnums.h.inc -gen-enum-decls) mlir_tablegen(QuantumEnums.cpp.inc -gen-enum-defs) diff --git a/mlir/include/Quantum/IR/QuantumDialect.h b/mlir/include/Quantum/IR/QuantumDialect.h index e322f853c8..34f2b96020 100644 --- a/mlir/include/Quantum/IR/QuantumDialect.h +++ b/mlir/include/Quantum/IR/QuantumDialect.h @@ -31,6 +31,11 @@ // Quantum resource abstractions declarations. //===----------------------------------------------------------------------===// +#include "Quantum/IR/QuantumTypeInterfaces.h.inc" + +#define GET_TYPEDEF_CLASSES +#include "Quantum/IR/QuantumOpsTypes.h.inc" + class QuantumMemory : public mlir::SideEffects::Resource::Base { llvm::StringRef getName() final { return "QuantumMemory"; } }; diff --git a/mlir/include/Quantum/IR/QuantumDialect.td b/mlir/include/Quantum/IR/QuantumDialect.td index 64c98859a9..fc13551815 100644 --- a/mlir/include/Quantum/IR/QuantumDialect.td +++ b/mlir/include/Quantum/IR/QuantumDialect.td @@ -18,6 +18,8 @@ include "mlir/IR/DialectBase.td" include "mlir/Interfaces/SideEffectInterfaces.td" +include "Quantum/IR/QuantumTypeInterfaces.td" + //===----------------------------------------------------------------------===// // Quantum dialect. //===----------------------------------------------------------------------===// @@ -57,6 +59,32 @@ def QuantumDialect : Dialect { def Unitary : NativeOpTrait<"UnitaryTrait">; def Hermitian : NativeOpTrait<"HermitianTrait">; +// Hierarchical qubit types for FTQC workloads +def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical", [QubitTypeInterface]> { + let summary = "Logical qubit"; + let description = [{ + Values with logical qubit type represent qubits at the logical layer of program + representation. In other words, they represent qubit values allocated and acted on by the + user program, and are agnostic to any specific QEC or hardware architecture. + }]; +} + +def QECQubitType : Quantum_Type<"QECQubit", "bit.qec", [QubitTypeInterface]> { + let summary = "QEC qubit"; + let description = [{ + Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of + program representation. + }]; +} + +def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical", [QubitTypeInterface]> { + let summary = "Physical qubit"; + let description = [{ + Values with physical qubit type represent qubits at the physical layer of program + representation. + }]; +} + //===----------------------------------------------------------------------===// // Quantum resource abstractions. //===----------------------------------------------------------------------===// diff --git a/mlir/include/Quantum/IR/QuantumTypeInterfaces.h b/mlir/include/Quantum/IR/QuantumTypeInterfaces.h new file mode 100644 index 0000000000..28c5ca12af --- /dev/null +++ b/mlir/include/Quantum/IR/QuantumTypeInterfaces.h @@ -0,0 +1,21 @@ +// Copyright 2026 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +//===----------------------------------------------------------------------===// +// Quantum type interface declarations. +//===----------------------------------------------------------------------===// + +#include "Quantum/IR/QuantumTypeInterfaces.h.inc" diff --git a/mlir/include/Quantum/IR/QuantumTypeInterfaces.td b/mlir/include/Quantum/IR/QuantumTypeInterfaces.td new file mode 100644 index 0000000000..a289b19bea --- /dev/null +++ b/mlir/include/Quantum/IR/QuantumTypeInterfaces.td @@ -0,0 +1,29 @@ +// Copyright 2026 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef QUANTUM_TYPE_INTERFACES +#define QUANTUM_TYPE_INTERFACES + +include "mlir/IR/Interfaces.td" + +//===----------------------------------------------------------------------===// +// Quantum type interfaces. +//===----------------------------------------------------------------------===// + +def QubitTypeInterface : TypeInterface<"QubitTypeInterface"> { + let description = "Interface for all qubit-like types."; + let cppNamespace = "::catalyst::quantum"; +} + +#endif // QUANTUM_TYPE_INTERFACES From 8ce764f2bcda9e7f176569d7584ac08b31fef780 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Thu, 15 Jan 2026 09:28:54 -0500 Subject: [PATCH 02/11] Remove QuantumTypeInterfaces and refactor --- mlir/include/Quantum/IR/CMakeLists.txt | 4 --- mlir/include/Quantum/IR/QuantumDialect.h | 5 ---- mlir/include/Quantum/IR/QuantumDialect.td | 28 ------------------ .../Quantum/IR/QuantumTypeInterfaces.h | 21 -------------- .../Quantum/IR/QuantumTypeInterfaces.td | 29 ------------------- mlir/include/Quantum/IR/QuantumTypes.td | 26 +++++++++++++++++ 6 files changed, 26 insertions(+), 87 deletions(-) delete mode 100644 mlir/include/Quantum/IR/QuantumTypeInterfaces.h delete mode 100644 mlir/include/Quantum/IR/QuantumTypeInterfaces.td diff --git a/mlir/include/Quantum/IR/CMakeLists.txt b/mlir/include/Quantum/IR/CMakeLists.txt index a88e66142b..58401de302 100644 --- a/mlir/include/Quantum/IR/CMakeLists.txt +++ b/mlir/include/Quantum/IR/CMakeLists.txt @@ -4,10 +4,6 @@ add_mlir_doc(QuantumDialect QuantumDialect Quantum/ -gen-dialect-doc) add_mlir_doc(QuantumOps QuantumOps Quantum/ -gen-op-doc) add_mlir_doc(QuantumInterfaces QuantumInterfaces Quantum/ -gen-op-interface-docs) -set(LLVM_TARGET_DEFINITIONS QuantumTypeInterfaces.td) -mlir_tablegen(QuantumTypeInterfaces.h.inc -gen-type-interface-decls) -mlir_tablegen(QuantumTypeInterfaces.cpp.inc -gen-type-interface-defs) - set(LLVM_TARGET_DEFINITIONS QuantumAttrDefs.td) mlir_tablegen(QuantumEnums.h.inc -gen-enum-decls) mlir_tablegen(QuantumEnums.cpp.inc -gen-enum-defs) diff --git a/mlir/include/Quantum/IR/QuantumDialect.h b/mlir/include/Quantum/IR/QuantumDialect.h index 34f2b96020..e322f853c8 100644 --- a/mlir/include/Quantum/IR/QuantumDialect.h +++ b/mlir/include/Quantum/IR/QuantumDialect.h @@ -31,11 +31,6 @@ // Quantum resource abstractions declarations. //===----------------------------------------------------------------------===// -#include "Quantum/IR/QuantumTypeInterfaces.h.inc" - -#define GET_TYPEDEF_CLASSES -#include "Quantum/IR/QuantumOpsTypes.h.inc" - class QuantumMemory : public mlir::SideEffects::Resource::Base { llvm::StringRef getName() final { return "QuantumMemory"; } }; diff --git a/mlir/include/Quantum/IR/QuantumDialect.td b/mlir/include/Quantum/IR/QuantumDialect.td index fc13551815..64c98859a9 100644 --- a/mlir/include/Quantum/IR/QuantumDialect.td +++ b/mlir/include/Quantum/IR/QuantumDialect.td @@ -18,8 +18,6 @@ include "mlir/IR/DialectBase.td" include "mlir/Interfaces/SideEffectInterfaces.td" -include "Quantum/IR/QuantumTypeInterfaces.td" - //===----------------------------------------------------------------------===// // Quantum dialect. //===----------------------------------------------------------------------===// @@ -59,32 +57,6 @@ def QuantumDialect : Dialect { def Unitary : NativeOpTrait<"UnitaryTrait">; def Hermitian : NativeOpTrait<"HermitianTrait">; -// Hierarchical qubit types for FTQC workloads -def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical", [QubitTypeInterface]> { - let summary = "Logical qubit"; - let description = [{ - Values with logical qubit type represent qubits at the logical layer of program - representation. In other words, they represent qubit values allocated and acted on by the - user program, and are agnostic to any specific QEC or hardware architecture. - }]; -} - -def QECQubitType : Quantum_Type<"QECQubit", "bit.qec", [QubitTypeInterface]> { - let summary = "QEC qubit"; - let description = [{ - Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of - program representation. - }]; -} - -def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical", [QubitTypeInterface]> { - let summary = "Physical qubit"; - let description = [{ - Values with physical qubit type represent qubits at the physical layer of program - representation. - }]; -} - //===----------------------------------------------------------------------===// // Quantum resource abstractions. //===----------------------------------------------------------------------===// diff --git a/mlir/include/Quantum/IR/QuantumTypeInterfaces.h b/mlir/include/Quantum/IR/QuantumTypeInterfaces.h deleted file mode 100644 index 28c5ca12af..0000000000 --- a/mlir/include/Quantum/IR/QuantumTypeInterfaces.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2026 Xanadu Quantum Technologies Inc. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -//===----------------------------------------------------------------------===// -// Quantum type interface declarations. -//===----------------------------------------------------------------------===// - -#include "Quantum/IR/QuantumTypeInterfaces.h.inc" diff --git a/mlir/include/Quantum/IR/QuantumTypeInterfaces.td b/mlir/include/Quantum/IR/QuantumTypeInterfaces.td deleted file mode 100644 index a289b19bea..0000000000 --- a/mlir/include/Quantum/IR/QuantumTypeInterfaces.td +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2026 Xanadu Quantum Technologies Inc. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef QUANTUM_TYPE_INTERFACES -#define QUANTUM_TYPE_INTERFACES - -include "mlir/IR/Interfaces.td" - -//===----------------------------------------------------------------------===// -// Quantum type interfaces. -//===----------------------------------------------------------------------===// - -def QubitTypeInterface : TypeInterface<"QubitTypeInterface"> { - let description = "Interface for all qubit-like types."; - let cppNamespace = "::catalyst::quantum"; -} - -#endif // QUANTUM_TYPE_INTERFACES diff --git a/mlir/include/Quantum/IR/QuantumTypes.td b/mlir/include/Quantum/IR/QuantumTypes.td index 77a2c02923..4d953f10be 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.td +++ b/mlir/include/Quantum/IR/QuantumTypes.td @@ -41,4 +41,30 @@ def ResultType : Quantum_Type<"Result", "res"> { let summary = "A quantum measurement result."; } +// Hierarchical qubit types for FTQC workloads +def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical"> { + let summary = "Logical qubit"; + let description = [{ + Values with logical qubit type represent qubits at the logical layer of program + representation. In other words, they represent qubit values allocated and acted on by the + user program, and are agnostic to any specific QEC or hardware architecture. + }]; +} + +def QECQubitType : Quantum_Type<"QECQubit", "bit.qec"> { + let summary = "QEC qubit"; + let description = [{ + Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of + program representation. + }]; +} + +def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical"> { + let summary = "Physical qubit"; + let description = [{ + Values with physical qubit type represent qubits at the physical layer of program + representation. + }]; +} + #endif // QUANTUM_TYPES From 66a9eb7dc5131d7add1195d639db4035854e3498 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Thu, 15 Jan 2026 17:07:23 -0500 Subject: [PATCH 03/11] [WIP] Add qubit-type levels Options are: - `!quantum.bit` - `!quantum.bit` - `!quantum.bit` - `!quantum.bit` The default qubit type is still `!quantum.bit`, which is now equivalent to `!quantum.bit`. --- mlir/include/Quantum/IR/QuantumAttrDefs.td | 12 +++++ mlir/include/Quantum/IR/QuantumOps.td | 4 +- mlir/include/Quantum/IR/QuantumTypes.h | 7 +++ mlir/include/Quantum/IR/QuantumTypes.td | 63 ++++++++++++++-------- mlir/test/Quantum/VerifierTest.mlir | 7 +++ 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/mlir/include/Quantum/IR/QuantumAttrDefs.td b/mlir/include/Quantum/IR/QuantumAttrDefs.td index 133462bae1..0a0757b9ba 100644 --- a/mlir/include/Quantum/IR/QuantumAttrDefs.td +++ b/mlir/include/Quantum/IR/QuantumAttrDefs.td @@ -38,5 +38,17 @@ def NamedObservable : I32EnumAttr<"NamedObservable", def NamedObservableAttr : EnumAttr; +def QubitLevel : I32EnumAttr<"QubitLevel", + "Qubit levels in the hierarchical qubit representation", + [ + I32EnumAttrCase<"Abstract", 0, "abstract">, + I32EnumAttrCase<"Logical", 1, "logical">, + I32EnumAttrCase<"QEC", 2, "qec">, + I32EnumAttrCase<"Physical", 3, "physical">, + ]> { + let cppNamespace = "catalyst::quantum"; + let genSpecializedAttr = 0; +} + #endif // QUANTUM_ATTR_DEFS diff --git a/mlir/include/Quantum/IR/QuantumOps.td b/mlir/include/Quantum/IR/QuantumOps.td index 165aa880d1..5ae6560654 100644 --- a/mlir/include/Quantum/IR/QuantumOps.td +++ b/mlir/include/Quantum/IR/QuantumOps.td @@ -410,7 +410,9 @@ def SetBasisStateOp : Gate_Op<"set_basis_state"> { } def CustomOp : UnitaryGate_Op<"custom", [DifferentiableGate, NoMemoryEffect, - AttrSizedOperandSegments, AttrSizedResultSegments]> { + AttrSizedOperandSegments, AttrSizedResultSegments, + AllTypesMatch<["in_qubits", "out_qubits"]>, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "A generic quantum gate on n qubits with m floating point parameters."; let description = [{ }]; diff --git a/mlir/include/Quantum/IR/QuantumTypes.h b/mlir/include/Quantum/IR/QuantumTypes.h index b860bae969..5d3e908d7c 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.h +++ b/mlir/include/Quantum/IR/QuantumTypes.h @@ -18,7 +18,14 @@ // Quantum type declarations. //===----------------------------------------------------------------------===// +#include "mlir/IR/OpImplementation.h" #include "mlir/IR/Types.h" +#include "Quantum/IR/QuantumAttrDefs.h" + #define GET_TYPEDEF_CLASSES #include "Quantum/IR/QuantumOpsTypes.h.inc" + +void printQubitLevel(mlir::AsmPrinter &printer, catalyst::quantum::QubitLevel level); + +mlir::ParseResult parseQubitLevel(mlir::AsmParser &parser, catalyst::quantum::QubitLevel &level); diff --git a/mlir/include/Quantum/IR/QuantumTypes.td b/mlir/include/Quantum/IR/QuantumTypes.td index 4d953f10be..a8e4e7daab 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.td +++ b/mlir/include/Quantum/IR/QuantumTypes.td @@ -30,6 +30,23 @@ class Quantum_Type traits = []> def QubitType : Quantum_Type<"Qubit", "bit"> { let summary = "A value-semantic qubit (state)."; + + let parameters = (ins + DefaultValuedParameter<"QubitLevel", "QubitLevel::Abstract">:$level + ); + + let assemblyFormat = "(`<` $level^ `>`)?"; + + // This allows the ODS generator to "build" the type automatically + // using the Abstract qubit level when it needs a default. + let builderCall = "$_builder.getType(quantum::QubitLevel::Abstract)"; + + let extraClassDeclaration = [{ + // Get an instance of the QubitType with the default 'Abstract' level. + static QubitType get(::mlir::MLIRContext *context) { + return get(context, quantum::QubitLevel::Abstract); + } + }]; } def QuregType : Quantum_Type<"Qureg", "reg"> { let summary = "An array of value-semantic qubits (i.e. quantum register)."; @@ -41,30 +58,30 @@ def ResultType : Quantum_Type<"Result", "res"> { let summary = "A quantum measurement result."; } -// Hierarchical qubit types for FTQC workloads -def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical"> { - let summary = "Logical qubit"; - let description = [{ - Values with logical qubit type represent qubits at the logical layer of program - representation. In other words, they represent qubit values allocated and acted on by the - user program, and are agnostic to any specific QEC or hardware architecture. - }]; -} +// // Hierarchical qubit types for FTQC workloads +// def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical"> { +// let summary = "Logical qubit"; +// let description = [{ +// Values with logical qubit type represent qubits at the logical layer of program +// representation. In other words, they represent qubit values allocated and acted on by the +// user program, and are agnostic to any specific QEC or hardware architecture. +// }]; +// } -def QECQubitType : Quantum_Type<"QECQubit", "bit.qec"> { - let summary = "QEC qubit"; - let description = [{ - Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of - program representation. - }]; -} +// def QECQubitType : Quantum_Type<"QECQubit", "bit.qec"> { +// let summary = "QEC qubit"; +// let description = [{ +// Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of +// program representation. +// }]; +// } -def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical"> { - let summary = "Physical qubit"; - let description = [{ - Values with physical qubit type represent qubits at the physical layer of program - representation. - }]; -} +// def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical"> { +// let summary = "Physical qubit"; +// let description = [{ +// Values with physical qubit type represent qubits at the physical layer of program +// representation. +// }]; +// } #endif // QUANTUM_TYPES diff --git a/mlir/test/Quantum/VerifierTest.mlir b/mlir/test/Quantum/VerifierTest.mlir index f6fbb90ac1..dc03b9865d 100644 --- a/mlir/test/Quantum/VerifierTest.mlir +++ b/mlir/test/Quantum/VerifierTest.mlir @@ -456,3 +456,10 @@ func.func @state_good(%q0 : !quantum.bit, %q1 : !quantum.bit, %c : i64, %in_stat quantum.state %obs shape %c : tensor> return } + +// ----- + +func.func @qubit_type_abstract(%q0 : !quantum.bit) { + %q3 = quantum.custom "PauliZ"() %q0 : !quantum.bit + return +} From 19928cd8b3787403fcf4720514e10098a5a0ca9a Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 09:11:15 -0500 Subject: [PATCH 04/11] Clean up --- mlir/include/Quantum/IR/QuantumTypes.td | 26 ------------- mlir/test/Quantum/QubitTypesTest.mlir | 50 +++++++++++++++++++++++++ mlir/test/Quantum/VerifierTest.mlir | 7 ---- 3 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 mlir/test/Quantum/QubitTypesTest.mlir diff --git a/mlir/include/Quantum/IR/QuantumTypes.td b/mlir/include/Quantum/IR/QuantumTypes.td index a8e4e7daab..1a655557cd 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.td +++ b/mlir/include/Quantum/IR/QuantumTypes.td @@ -58,30 +58,4 @@ def ResultType : Quantum_Type<"Result", "res"> { let summary = "A quantum measurement result."; } -// // Hierarchical qubit types for FTQC workloads -// def LogicalQubitType : Quantum_Type<"LogicalQubit", "bit.logical"> { -// let summary = "Logical qubit"; -// let description = [{ -// Values with logical qubit type represent qubits at the logical layer of program -// representation. In other words, they represent qubit values allocated and acted on by the -// user program, and are agnostic to any specific QEC or hardware architecture. -// }]; -// } - -// def QECQubitType : Quantum_Type<"QECQubit", "bit.qec"> { -// let summary = "QEC qubit"; -// let description = [{ -// Values with QEC qubit type represent qubits at the quantum-error-corrected (QEC) layer of -// program representation. -// }]; -// } - -// def PhysicalQubitType : Quantum_Type<"PhysicalQubit", "bit.physical"> { -// let summary = "Physical qubit"; -// let description = [{ -// Values with physical qubit type represent qubits at the physical layer of program -// representation. -// }]; -// } - #endif // QUANTUM_TYPES diff --git a/mlir/test/Quantum/QubitTypesTest.mlir b/mlir/test/Quantum/QubitTypesTest.mlir new file mode 100644 index 0000000000..aa7ba2f86a --- /dev/null +++ b/mlir/test/Quantum/QubitTypesTest.mlir @@ -0,0 +1,50 @@ +// Copyright 2026 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// RUN: quantum-opt %s --split-input-file --verify-diagnostics + +func.func @test_default_type(%q0 : !quantum.bit) { + %q1 = quantum.custom ""() %q0 : !quantum.bit + return +} + +// ----- + +func.func @test_abstract_type(%q0 : !quantum.bit) { + %q1 = quantum.custom ""() %q0 : !quantum.bit + return +} + +// ----- + +func.func @test_logical_type(%q0 : !quantum.bit) { + %q1 = quantum.custom ""() %q0 : !quantum.bit + return +} + +// ----- + +func.func @test_qec_type(%q0 : !quantum.bit) { + %q1 = quantum.custom ""() %q0 : !quantum.bit + return +} + +// ----- + +func.func @test_physical_type(%q0 : !quantum.bit) { + %q1 = quantum.custom ""() %q0 : !quantum.bit + return +} + +// ----- \ No newline at end of file diff --git a/mlir/test/Quantum/VerifierTest.mlir b/mlir/test/Quantum/VerifierTest.mlir index dc03b9865d..f6fbb90ac1 100644 --- a/mlir/test/Quantum/VerifierTest.mlir +++ b/mlir/test/Quantum/VerifierTest.mlir @@ -456,10 +456,3 @@ func.func @state_good(%q0 : !quantum.bit, %q1 : !quantum.bit, %c : i64, %in_stat quantum.state %obs shape %c : tensor> return } - -// ----- - -func.func @qubit_type_abstract(%q0 : !quantum.bit) { - %q3 = quantum.custom "PauliZ"() %q0 : !quantum.bit - return -} From a3b8a75e9516985f1a09d0ddb32cda419985b201 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 13:34:41 -0500 Subject: [PATCH 05/11] Add verifier and tests --- mlir/include/Quantum/IR/QuantumInterfaces.td | 21 ++- mlir/test/Quantum/QubitTypesTest.mlir | 173 +++++++++++++++++-- 2 files changed, 180 insertions(+), 14 deletions(-) diff --git a/mlir/include/Quantum/IR/QuantumInterfaces.td b/mlir/include/Quantum/IR/QuantumInterfaces.td index 1a6f5cd16b..09255206bf 100644 --- a/mlir/include/Quantum/IR/QuantumInterfaces.td +++ b/mlir/include/Quantum/IR/QuantumInterfaces.td @@ -79,11 +79,26 @@ def QuantumOperation : OpInterface<"QuantumOperation"> { let verify = [{ auto gate = mlir::cast($_op); + auto operands = gate.getQubitOperands(); + auto results = gate.getQubitResults(); - if (gate.getQubitOperands().size() != gate.getQubitResults().size()) + if (operands.size() != results.size()) { return $_op->emitError() << - "number of qubits in input (" << gate.getQubitOperands().size() << ") " << - "and output (" << gate.getQubitResults().size() << ") must be the same"; + "number of qubits in input (" << operands.size() << ") " << + "and output (" << results.size() << ") must be the same"; + } + + if (!operands.empty()) { + mlir::Type refType = operands[0].getType(); + + // Check if all operands and results have the same type as refType + auto allMatch = [&](auto value) { return value.getType() == refType; }; + + if (!llvm::all_of(operands, allMatch) || !llvm::all_of(results, allMatch)) { + return $_op->emitOpError() + << "requires all qubit operands and results to have the same type"; + } + } return mlir::success(); }]; diff --git a/mlir/test/Quantum/QubitTypesTest.mlir b/mlir/test/Quantum/QubitTypesTest.mlir index aa7ba2f86a..5bd8fff62a 100644 --- a/mlir/test/Quantum/QubitTypesTest.mlir +++ b/mlir/test/Quantum/QubitTypesTest.mlir @@ -14,37 +14,188 @@ // RUN: quantum-opt %s --split-input-file --verify-diagnostics -func.func @test_default_type(%q0 : !quantum.bit) { - %q1 = quantum.custom ""() %q0 : !quantum.bit +//===----------------------------------------------------------------------===// +// Smoke tests: Can we use each qubit type? +//===----------------------------------------------------------------------===// + +func.func @test_default_type(%0 : !quantum.bit, %1 : !quantum.bit) { + %2 = quantum.custom ""() %0 : !quantum.bit + %3, %4 = quantum.custom ""() %1, %2 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_abstract_type(%0 : !quantum.bit, %1 : !quantum.bit) { + %2 = quantum.custom ""() %0 : !quantum.bit + %3, %4 = quantum.custom ""() %1, %2 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_logical_type(%0 : !quantum.bit, %1 : !quantum.bit) { + %2 = quantum.custom ""() %0 : !quantum.bit + %3, %4 = quantum.custom ""() %1, %2 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_qec_type(%0 : !quantum.bit, %1 : !quantum.bit) { + %2 = quantum.custom ""() %0 : !quantum.bit + %3, %4 = quantum.custom ""() %1, %2 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_physical_type(%0 : !quantum.bit, %1 : !quantum.bit) { + %2 = quantum.custom ""() %0 : !quantum.bit + %3, %4 = quantum.custom ""() %1, %2 : !quantum.bit, !quantum.bit return } // ----- -func.func @test_abstract_type(%q0 : !quantum.bit) { - %q1 = quantum.custom ""() %q0 : !quantum.bit +func.func @test_ctrl_default(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit return } // ----- -func.func @test_logical_type(%q0 : !quantum.bit) { - %q1 = quantum.custom ""() %q0 : !quantum.bit +func.func @test_ctrl_abstract(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit return } // ----- -func.func @test_qec_type(%q0 : !quantum.bit) { - %q1 = quantum.custom ""() %q0 : !quantum.bit +func.func @test_ctrl_logical(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit return } // ----- -func.func @test_physical_type(%q0 : !quantum.bit) { - %q1 = quantum.custom ""() %q0 : !quantum.bit +func.func @test_ctrl_qec(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit return } -// ----- \ No newline at end of file +// ----- + +func.func @test_ctrl_physical(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit + return +} + +// ----- + + +//===----------------------------------------------------------------------===// +// Verifier tests: Does incorrect usage result in expected errors? +//===----------------------------------------------------------------------===// + +// COM: In the tests below, note that we can safely "convert" between `!quantum.bit` +// COM: and `!quantum.bit` since they are equivalent. + +func.func @test_mix_types_abs_log(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_mix_types_abs_qec(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_mix_types_abs_phy(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_mix_types_log_qec(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_mix_types_log_phy(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_mix_types_qec_phy(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit // expected-note {{prior use here}} + // expected-error @below {{expects different type than prior uses}} + %2 = quantum.custom ""() %1 : !quantum.bit + return +} + +// ----- + +func.func @test_2q_mix_input_types_default_log(%0 : !quantum.bit, %1 : !quantum.bit) { + // expected-error @below {{requires all qubit operands and results to have the same type}} + %2, %3 = quantum.custom ""() %0, %1 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_2q_mix_input_types_abs_log(%0 : !quantum.bit, %1 : !quantum.bit) { + // expected-error @below {{requires all qubit operands and results to have the same type}} + %2, %3 = quantum.custom ""() %0, %1 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_2q_mix_input_types_abs_phy(%0 : !quantum.bit, %1 : !quantum.bit) { + // expected-error @below {{requires all qubit operands and results to have the same type}} + %2, %3 = quantum.custom ""() %0, %1 : !quantum.bit, !quantum.bit + return +} + +// ----- + +func.func @test_2q_mix_input_ctrl_types_abs_log(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + // expected-error @below {{requires all qubit operands and results to have the same type}} + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit + return +} + +// ----- + +func.func @test_2q_mix_input_ctrl_types_abs_phy(%0 : !quantum.bit, %1 : !quantum.bit) { + %true = llvm.mlir.constant (1 : i1) : i1 + // expected-error @below {{requires all qubit operands and results to have the same type}} + %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit + return +} From 6b6d0a2b831f64e9895390f7d73b8221c84c06cd Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 13:52:48 -0500 Subject: [PATCH 06/11] Add AllTypesMatch to all quantum ops with in/out qubits Also update verifier tests --- mlir/include/Quantum/IR/QuantumOps.td | 31 +++++++++++++++++---------- mlir/test/Quantum/VerifierTest.mlir | 12 +++++------ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/mlir/include/Quantum/IR/QuantumOps.td b/mlir/include/Quantum/IR/QuantumOps.td index 5ae6560654..8c1544b3cf 100644 --- a/mlir/include/Quantum/IR/QuantumOps.td +++ b/mlir/include/Quantum/IR/QuantumOps.td @@ -346,7 +346,7 @@ class UnitaryGate_Op traits = []> : let extraClassDeclaration = extraBaseClassDeclaration; } -def SetStateOp : Gate_Op<"set_state"> { +def SetStateOp : Gate_Op<"set_state", [AllTypesMatch<["in_qubits", "out_qubits"]>]> { let summary = "Set state to a complex vector."; let description = [{ This operation is useful for simulators implementing state preparation. @@ -377,7 +377,7 @@ def SetStateOp : Gate_Op<"set_state"> { } -def SetBasisStateOp : Gate_Op<"set_basis_state"> { +def SetBasisStateOp : Gate_Op<"set_basis_state", [AllTypesMatch<["in_qubits", "out_qubits"]>]> { let summary = "Set basis state."; let description = [{ This operation is useful for simulators implementing set basis state. @@ -539,14 +539,16 @@ def CustomOp : UnitaryGate_Op<"custom", [DifferentiableGate, NoMemoryEffect, def PauliWord : TypedArrayAttrBase; def PauliRotOp : UnitaryGate_Op<"paulirot", [DifferentiableGate, NoMemoryEffect, - AttrSizedOperandSegments, AttrSizedResultSegments]> { + AttrSizedOperandSegments, AttrSizedResultSegments, + AllTypesMatch<["in_qubits", "out_qubits"]>, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "Apply a Pauli Product Rotation"; let description = [{ The `quantum.paulirot` operation applies a rotation around a Pauli product - operator to the state-vector. + operator to the state-vector. The arguments are the rotation angle `angle`, a string representing the Pauli product operator, and a set of qubits the operation acts on. - Note that this operation is currently not excutable. There isn't a valid + Note that this operation is currently not excutable. There isn't a valid lowering path to the LLVM IR. }]; @@ -567,7 +569,7 @@ def PauliRotOp : UnitaryGate_Op<"paulirot", [DifferentiableGate, NoMemoryEffect, let assemblyFormat = [{ $pauli_product `(` $angle `)` $in_qubits (`adj` $adjoint^)? attr-dict ( `ctrls` `(` $in_ctrl_qubits^ `)` )? ( `ctrlvals` `(` $in_ctrl_values^ `)` )? `:` type($out_qubits) (`ctrls` type($out_ctrl_qubits)^ )? }]; - + let extraClassDeclaration = extraBaseClassDeclaration # [{ mlir::ValueRange getAllParams() { return getODSOperands(getParamOperandIdx()); @@ -575,7 +577,8 @@ def PauliRotOp : UnitaryGate_Op<"paulirot", [DifferentiableGate, NoMemoryEffect, }]; } -def GlobalPhaseOp : UnitaryGate_Op<"gphase", [DifferentiableGate, AttrSizedOperandSegments]> { +def GlobalPhaseOp : UnitaryGate_Op<"gphase", [DifferentiableGate, AttrSizedOperandSegments, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "Global Phase."; let description = [{ @@ -616,7 +619,9 @@ def GlobalPhaseOp : UnitaryGate_Op<"gphase", [DifferentiableGate, AttrSizedOpera } def MultiRZOp : UnitaryGate_Op<"multirz", [DifferentiableGate, NoMemoryEffect, - AttrSizedOperandSegments, AttrSizedResultSegments]> { + AttrSizedOperandSegments, AttrSizedResultSegments, + AllTypesMatch<["in_qubits", "out_qubits"]>, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "Apply an arbitrary multi Z rotation"; let description = [{ The `quantum.multirz` operation applies an arbitrary multi Z rotation to the state-vector. @@ -655,7 +660,9 @@ def MultiRZOp : UnitaryGate_Op<"multirz", [DifferentiableGate, NoMemoryEffect, } def PCPhaseOp : UnitaryGate_Op<"pcphase", [DifferentiableGate, NoMemoryEffect, - AttrSizedOperandSegments, AttrSizedResultSegments]> { + AttrSizedOperandSegments, AttrSizedResultSegments, + AllTypesMatch<["in_qubits", "out_qubits"]>, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "Apply a projector-controlled phase gate"; let description = [{ This gate is built from simpler gates like `PhaseShift` and `PauliX` and acts on a group @@ -705,7 +712,9 @@ def PCPhaseOp : UnitaryGate_Op<"pcphase", [DifferentiableGate, NoMemoryEffect, def QubitUnitaryOp : UnitaryGate_Op<"unitary", [ParametrizedGate, NoMemoryEffect, - AttrSizedOperandSegments, AttrSizedResultSegments]> { + AttrSizedOperandSegments, AttrSizedResultSegments, + AllTypesMatch<["in_qubits", "out_qubits"]>, + AllTypesMatch<["in_ctrl_qubits", "out_ctrl_qubits"]>]> { let summary = "Apply an arbitrary fixed unitary matrix"; let description = [{ The `quantum.unitary` operation applies an arbitrary fixed unitary matrix to the @@ -960,7 +969,7 @@ def HamiltonianOp : Observable_Op<"hamiltonian"> { class Measurement_Op traits = []> : Quantum_Op; -def MeasureOp : Quantum_Op<"measure"> { +def MeasureOp : Quantum_Op<"measure", [AllTypesMatch<["in_qubit", "out_qubit"]>]> { let summary = "A single-qubit projective measurement in the computational basis."; let description = [{ }]; diff --git a/mlir/test/Quantum/VerifierTest.mlir b/mlir/test/Quantum/VerifierTest.mlir index f6fbb90ac1..f194356063 100644 --- a/mlir/test/Quantum/VerifierTest.mlir +++ b/mlir/test/Quantum/VerifierTest.mlir @@ -59,7 +59,7 @@ func.func @custom(%f : f64, %q1 : !quantum.bit, %q2 : !quantum.bit) { %q4 = quantum.custom "RZ"(%f) %q1 : !quantum.bit %q5, %q6 = quantum.custom "CNOT"() %q1, %q2 : !quantum.bit, !quantum.bit - // expected-error@+1 {{number of qubits in input (2) and output (1) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %err = quantum.custom "CNOT"() %q1, %q2 : !quantum.bit return @@ -68,7 +68,7 @@ func.func @custom(%f : f64, %q1 : !quantum.bit, %q2 : !quantum.bit) { // ----- func.func @multirz2(%q0 : !quantum.bit, %q1 : !quantum.bit, %theta : f64) { - // expected-error@+1 {{number of qubits in input (2) and output (1) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %err = quantum.multirz(%theta) %q0, %q1 : !quantum.bit return @@ -77,7 +77,7 @@ func.func @multirz2(%q0 : !quantum.bit, %q1 : !quantum.bit, %theta : f64) { // ----- func.func @multirz3(%q0 : !quantum.bit, %theta : f64) { - // expected-error@+1 {{number of qubits in input (1) and output (2) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %err:2 = quantum.multirz(%theta) %q0 : !quantum.bit, !quantum.bit return @@ -86,7 +86,7 @@ func.func @multirz3(%q0 : !quantum.bit, %theta : f64) { // ----- func.func @unitary2(%q0 : !quantum.bit, %q1 : !quantum.bit, %m : tensor<4x4xcomplex>) { - // expected-error@+1 {{number of qubits in input (2) and output (1) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %err = quantum.unitary(%m: tensor<4x4xcomplex>) %q0, %q1 : !quantum.bit return @@ -110,7 +110,7 @@ func.func @controlled1(%1 : !quantum.bit, %2 : !quantum.bit, %3 : !quantum.bit) %cst = llvm.mlir.constant (6.000000e-01 : f64) : f64 %cst_0 = llvm.mlir.constant (9.000000e-01 : f64) : f64 %cst_1 = llvm.mlir.constant (3.000000e-01 : f64) : f64 - // expected-error@+1 {{number of controlling qubits in input (1) and output (0) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %out_qubits:2 = quantum.custom "Rot"(%cst, %cst_1, %cst_0) %2 ctrls (%3) ctrlvals (%true) : !quantum.bit, !quantum.bit return } @@ -122,7 +122,7 @@ func.func @controlled2(%1 : !quantum.bit, %2 : !quantum.bit, %3 : !quantum.bit) %cst = llvm.mlir.constant (6.000000e-01 : f64) : f64 %cst_0 = llvm.mlir.constant (9.000000e-01 : f64) : f64 %cst_1 = llvm.mlir.constant (3.000000e-01 : f64) : f64 - // expected-error@+1 {{number of controlling qubits in input (2) and controlling values (1) must be the same}} + // expected-error@+1 {{number of operands and types do not match}} %out_qubits:3 = quantum.custom "Rot"(%cst, %cst_1, %cst_0) %2 ctrls (%3, %3) ctrlvals (%true) : !quantum.bit, !quantum.bit, !quantum.bit return } From d2e3e97818e6457235ca1b8223b6e7dc9010f44b Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 13:54:49 -0500 Subject: [PATCH 07/11] Add alloc_qb and dealloc_qb tests --- mlir/test/Quantum/QubitTypesTest.mlir | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/mlir/test/Quantum/QubitTypesTest.mlir b/mlir/test/Quantum/QubitTypesTest.mlir index 5bd8fff62a..3c0318ca5c 100644 --- a/mlir/test/Quantum/QubitTypesTest.mlir +++ b/mlir/test/Quantum/QubitTypesTest.mlir @@ -98,6 +98,62 @@ func.func @test_ctrl_physical(%0 : !quantum.bit, %1 : !quantum.bit + return +} + +// ----- + +func.func @test_alloc_qb_log() { + %0 = quantum.alloc_qb : !quantum.bit + return +} + +// ----- + +func.func @test_alloc_qb_phy() { + %0 = quantum.alloc_qb : !quantum.bit + return +} + +// ----- + +func.func @test_dealloc_qb_default(%0 : !quantum.bit) { + quantum.dealloc_qb %0 : !quantum.bit + return +} + +// ----- + +func.func @test_dealloc_qb_abs(%0 : !quantum.bit) { + quantum.dealloc_qb %0 : !quantum.bit + return +} + +// ----- + +func.func @test_dealloc_qb_log(%0 : !quantum.bit) { + quantum.dealloc_qb %0 : !quantum.bit + return +} + +// ----- + +func.func @test_dealloc_qb_phy(%0 : !quantum.bit) { + quantum.dealloc_qb %0 : !quantum.bit + return +} + +// ----- + //===----------------------------------------------------------------------===// // Verifier tests: Does incorrect usage result in expected errors? From 2bd8c4624e4f8363a80b940ed358fbedd78a69af Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 15:23:10 -0500 Subject: [PATCH 08/11] Add 'role' param to qubit type The role parameter is restricted to QEC and physical qubits, and can be one of 'data', 'xcheck', 'zcheck' or 'null' (the default). For abstract and logical qubits, only the 'null' role is permitted. --- mlir/include/Quantum/IR/QuantumAttrDefs.td | 11 +++ mlir/include/Quantum/IR/QuantumTypes.td | 20 ++++-- mlir/lib/Quantum/IR/CMakeLists.txt | 1 + mlir/lib/Quantum/IR/QuantumTypes.cpp | 34 +++++++++ mlir/test/Quantum/QubitTypesTest.mlir | 83 ++++++++++++++++++++++ 5 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 mlir/lib/Quantum/IR/QuantumTypes.cpp diff --git a/mlir/include/Quantum/IR/QuantumAttrDefs.td b/mlir/include/Quantum/IR/QuantumAttrDefs.td index 0a0757b9ba..69854dcd3b 100644 --- a/mlir/include/Quantum/IR/QuantumAttrDefs.td +++ b/mlir/include/Quantum/IR/QuantumAttrDefs.td @@ -50,5 +50,16 @@ def QubitLevel : I32EnumAttr<"QubitLevel", let genSpecializedAttr = 0; } +def QubitRole : I32EnumAttr<"QubitRole", + "Qubit roles for further specialization in the hierarchical qubit representation", + [ + I32EnumAttrCase<"Null", 0, "null">, + I32EnumAttrCase<"Data", 1, "data">, + I32EnumAttrCase<"XCheck", 2, "xcheck">, + I32EnumAttrCase<"ZCheck", 3, "zcheck">, + ]> { + let cppNamespace = "catalyst::quantum"; + let genSpecializedAttr = 0; +} #endif // QUANTUM_ATTR_DEFS diff --git a/mlir/include/Quantum/IR/QuantumTypes.td b/mlir/include/Quantum/IR/QuantumTypes.td index 1a655557cd..01f4909ba1 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.td +++ b/mlir/include/Quantum/IR/QuantumTypes.td @@ -32,21 +32,27 @@ def QubitType : Quantum_Type<"Qubit", "bit"> { let summary = "A value-semantic qubit (state)."; let parameters = (ins - DefaultValuedParameter<"QubitLevel", "QubitLevel::Abstract">:$level + DefaultValuedParameter<"QubitLevel", "QubitLevel::Abstract">:$level, + DefaultValuedParameter<"QubitRole", "QubitRole::Null">:$role ); - let assemblyFormat = "(`<` $level^ `>`)?"; + let assemblyFormat = "(`<` $level^ (`,` $role^ )? `>`)?"; - // This allows the ODS generator to "build" the type automatically - // using the Abstract qubit level when it needs a default. - let builderCall = "$_builder.getType(quantum::QubitLevel::Abstract)"; + // This allows the ODS generator to "build" the type automatically using the + // 'Abstract' qubit level and 'Null' qubit role and when it needs a default. + let builderCall = [{ + $_builder.getType( + quantum::QubitLevel::Abstract, quantum::QubitRole::Null) + }]; let extraClassDeclaration = [{ - // Get an instance of the QubitType with the default 'Abstract' level. + // Get an instance of the QubitType with the default 'Abstract' level and 'Null' role. static QubitType get(::mlir::MLIRContext *context) { - return get(context, quantum::QubitLevel::Abstract); + return get(context, quantum::QubitLevel::Abstract, quantum::QubitRole::Null); } }]; + + let genVerifyDecl = 1; } def QuregType : Quantum_Type<"Qureg", "reg"> { let summary = "An array of value-semantic qubits (i.e. quantum register)."; diff --git a/mlir/lib/Quantum/IR/CMakeLists.txt b/mlir/lib/Quantum/IR/CMakeLists.txt index 976c35cfc1..e229182e4f 100644 --- a/mlir/lib/Quantum/IR/CMakeLists.txt +++ b/mlir/lib/Quantum/IR/CMakeLists.txt @@ -2,6 +2,7 @@ add_mlir_library(MLIRQuantum QuantumDialect.cpp QuantumInterfaces.cpp QuantumOps.cpp + QuantumTypes.cpp ADDITIONAL_HEADER_DIRS ${PROJECT_SOURCE_DIR}/include/Quantum diff --git a/mlir/lib/Quantum/IR/QuantumTypes.cpp b/mlir/lib/Quantum/IR/QuantumTypes.cpp new file mode 100644 index 0000000000..7a8b5ebf6b --- /dev/null +++ b/mlir/lib/Quantum/IR/QuantumTypes.cpp @@ -0,0 +1,34 @@ +// Copyright 2026 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mlir/Support/LLVM.h" + +#include "Quantum/IR/QuantumAttrDefs.h" +#include "Quantum/IR/QuantumTypes.h" + +using namespace mlir; +using namespace catalyst::quantum; + +LogicalResult QubitType::verify(function_ref emitError, QubitLevel level, + QubitRole role) +{ + // If qubit level is not QEC or Physical, role must be Null + // In other words, abstract and logical qubits cannot specify a role + if ((level != QubitLevel::QEC && level != QubitLevel::Physical) && role != QubitRole::Null) { + return emitError() << "qubit role '" << stringifyQubitRole(role) + << "' is only allowed for qec or physical qubits; " + << "found level '" << stringifyQubitLevel(level) << "'"; + } + return success(); +} diff --git a/mlir/test/Quantum/QubitTypesTest.mlir b/mlir/test/Quantum/QubitTypesTest.mlir index 3c0318ca5c..803bec1a49 100644 --- a/mlir/test/Quantum/QubitTypesTest.mlir +++ b/mlir/test/Quantum/QubitTypesTest.mlir @@ -154,6 +154,47 @@ func.func @test_dealloc_qb_phy(%0 : !quantum.bit) { // ----- +func.func @test_qec_data_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_qec_xcheck_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_qec_zcheck_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_phy_data_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_phy_xcheck_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_phy_zcheck_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- //===----------------------------------------------------------------------===// // Verifier tests: Does incorrect usage result in expected errors? @@ -255,3 +296,45 @@ func.func @test_2q_mix_input_ctrl_types_abs_phy(%0 : !quantum.bit, %1 %2, %3 = quantum.custom ""() %0 ctrls (%1) ctrlvals (%true) : !quantum.bit ctrls !quantum.bit return } + +// ----- + +func.func @test_abs_data_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'data' is only allowed for qec or physical qubits}} + return +} + +// ----- + +func.func @test_abs_xcheck_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'xcheck' is only allowed for qec or physical qubits}} + return +} + +// ----- + +func.func @test_abs_zcheck_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'zcheck' is only allowed for qec or physical qubits}} + return +} + +// ----- + +func.func @test_abs_data_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'data' is only allowed for qec or physical qubits}} + return +} + +// ----- + +func.func @test_abs_xcheck_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'xcheck' is only allowed for qec or physical qubits}} + return +} + +// ----- + +func.func @test_abs_zcheck_type(%0 : !quantum.bit) { + // expected-error @above {{qubit role 'zcheck' is only allowed for qec or physical qubits}} + return +} From ca46d9c5492dfaf2da0824d6dfaf9364f477c1f9 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 15:37:53 -0500 Subject: [PATCH 09/11] Remove unused print/parse declarations --- mlir/include/Quantum/IR/QuantumTypes.h | 7 ------- mlir/lib/Quantum/IR/QuantumTypes.cpp | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/mlir/include/Quantum/IR/QuantumTypes.h b/mlir/include/Quantum/IR/QuantumTypes.h index 5d3e908d7c..b860bae969 100644 --- a/mlir/include/Quantum/IR/QuantumTypes.h +++ b/mlir/include/Quantum/IR/QuantumTypes.h @@ -18,14 +18,7 @@ // Quantum type declarations. //===----------------------------------------------------------------------===// -#include "mlir/IR/OpImplementation.h" #include "mlir/IR/Types.h" -#include "Quantum/IR/QuantumAttrDefs.h" - #define GET_TYPEDEF_CLASSES #include "Quantum/IR/QuantumOpsTypes.h.inc" - -void printQubitLevel(mlir::AsmPrinter &printer, catalyst::quantum::QubitLevel level); - -mlir::ParseResult parseQubitLevel(mlir::AsmParser &parser, catalyst::quantum::QubitLevel &level); diff --git a/mlir/lib/Quantum/IR/QuantumTypes.cpp b/mlir/lib/Quantum/IR/QuantumTypes.cpp index 7a8b5ebf6b..d5ed01bb94 100644 --- a/mlir/lib/Quantum/IR/QuantumTypes.cpp +++ b/mlir/lib/Quantum/IR/QuantumTypes.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "mlir/IR/Diagnostics.h" #include "mlir/Support/LLVM.h" #include "Quantum/IR/QuantumAttrDefs.h" From 59ee3a32b33726e201c6fef5ff4bcd926a835f98 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 15:41:32 -0500 Subject: [PATCH 10/11] Improve qubit verifier error message --- mlir/lib/Quantum/IR/QuantumTypes.cpp | 2 +- mlir/test/Quantum/QubitTypesTest.mlir | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Quantum/IR/QuantumTypes.cpp b/mlir/lib/Quantum/IR/QuantumTypes.cpp index d5ed01bb94..38841be223 100644 --- a/mlir/lib/Quantum/IR/QuantumTypes.cpp +++ b/mlir/lib/Quantum/IR/QuantumTypes.cpp @@ -28,7 +28,7 @@ LogicalResult QubitType::verify(function_ref emitError, Qu // In other words, abstract and logical qubits cannot specify a role if ((level != QubitLevel::QEC && level != QubitLevel::Physical) && role != QubitRole::Null) { return emitError() << "qubit role '" << stringifyQubitRole(role) - << "' is only allowed for qec or physical qubits; " + << "' is only permitted for qec and physical qubits; " << "found level '" << stringifyQubitLevel(level) << "'"; } return success(); diff --git a/mlir/test/Quantum/QubitTypesTest.mlir b/mlir/test/Quantum/QubitTypesTest.mlir index 803bec1a49..021287098c 100644 --- a/mlir/test/Quantum/QubitTypesTest.mlir +++ b/mlir/test/Quantum/QubitTypesTest.mlir @@ -154,6 +154,20 @@ func.func @test_dealloc_qb_phy(%0 : !quantum.bit) { // ----- +func.func @test_abs_null_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + +func.func @test_log_null_type(%0 : !quantum.bit) { + %1 = quantum.custom ""() %0 : !quantum.bit + return +} + +// ----- + func.func @test_qec_data_type(%0 : !quantum.bit) { %1 = quantum.custom ""() %0 : !quantum.bit return @@ -300,41 +314,41 @@ func.func @test_2q_mix_input_ctrl_types_abs_phy(%0 : !quantum.bit, %1 // ----- func.func @test_abs_data_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'data' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'data' is only permitted for qec and physical qubits}} return } // ----- func.func @test_abs_xcheck_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'xcheck' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'xcheck' is only permitted for qec and physical qubits}} return } // ----- func.func @test_abs_zcheck_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'zcheck' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'zcheck' is only permitted for qec and physical qubits}} return } // ----- func.func @test_abs_data_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'data' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'data' is only permitted for qec and physical qubits}} return } // ----- func.func @test_abs_xcheck_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'xcheck' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'xcheck' is only permitted for qec and physical qubits}} return } // ----- func.func @test_abs_zcheck_type(%0 : !quantum.bit) { - // expected-error @above {{qubit role 'zcheck' is only allowed for qec or physical qubits}} + // expected-error @above {{qubit role 'zcheck' is only permitted for qec and physical qubits}} return } From 8a60958c09f3ef2a9711748ec1cc6c4910d7c8f5 Mon Sep 17 00:00:00 2001 From: Joey Carter Date: Fri, 16 Jan 2026 15:46:33 -0500 Subject: [PATCH 11/11] Add changelog entry --- doc/releases/changelog-dev.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index b082fef31f..ba4365cbcc 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -39,11 +39,17 @@ into normal PPR and PPMs with SCF dialect to support runtime execution. [(#2390)](https://github.com/PennyLaneAI/catalyst/pull/2390) +* New qubit-type specializations have been added Catalyst's MLIR type system. These new qubit types + include `!quantum.bit`, `!quantum.bit` and `!quantum.bit`. The original + `!quantum.bit` type continues to be supported and used as the default qubit type. + [(#2369)](https://github.com/PennyLaneAI/catalyst/pull/2369) +

Documentation 📝

Contributors ✍️

This release contains contributions from (in alphabetical order): -Ali Asadi +Ali Asadi, +Joey Carter, Sengthai Heng, Jeffrey Kam.