Skip to content

[MLIR] [RFC] Upstream Support For Linear Typing? #241831

Open

Description

@MatthiasReumann

Description

TL;DR Many quantum compilers implement linear typing. Could there be LinearTypeInterface and LinearOpInterface in upstream MLIR?

Motivation

The majority of MLIR-based quantum compilers that perform circuit (or program) optimization use linear typing to model quantum data-flow graphs. As all projects are built upon MLIR, their design converges and an implicit set of best practices already emerges. In other words, all projects implement exactly the same to some degree1.

Project OpInterface Linear Type
Catalyst QuantumOperation !quantum.bit
CUDA-Q OperatorInterface !quake.wire
MQT Compiler Collection UnitaryOpInterface !qco.qubit

Proposal

Because I primarily work on quantum compilation, the following proposal will have a focus on that. Nonetheless, I think this ideas could be useful elsewhere also.

A good starting point would be a type interface. Quantum compilers could implement this interface for their respective qubit types.

def LinearTypeInterface : TypeInterface<"LinearTypeInterface"> {
  let description = [{
    Implemented by types whose values are linear resources: they must be
    consumed exactly once and can be neither copied nor discarded
    implicitly.
  }];
   ...
}

Consequently, we could use the following function to determine if a generic value is linear.

bool isLinearValue(Value v) { return isa<LinearTypeInterface>(v.getType()); }

An operation interface provides the necessary methods to traverse the linear data-flow graph and verifies if each result is used exactly once.

def LinearOpInterface : OpInterface<"LinearOpInterface"> {
  let description = [{
    The interface declares the op's *linear data-flow*: 
        consumed ->(op)-> produced
  }];
  let methods = [
    InterfaceMethod<
      "Returns the linear operands consumed by this operation. Default: All linear types.",
      "::mlir::SmallVector<::mlir::Value>", "getConsumedValues",
      (ins),
      /*methodBody=*/[{ ... }]
    >,
    InterfaceMethod<
      "Returns the linear results produced by this operation. Default: All linear types.",
      "::mlir::SmallVector<::mlir::Value>", "getProducedValues",
      (ins),
      /*methodBody=*/[{ ... }]
    >,
    InterfaceMethod<
      "Given a produced value, returns the consumed value it continues, or
       nullptr if the produced value is a fresh resource. Default: i-th consumed for i-th produced.",
      "::mlir::Value", "getConsumedForProduced",
      (ins "::mlir::Value":"produced")
    >,
    InterfaceMethod<
      "Given a consumed value, returns the produced value that continues
       it, or nullptr if this op drops the resource. Default: i-th produced for i-th consumed.",
      "::mlir::Value", "getProducedForConsumed",
      (ins "::mlir::Value":"consumed")
    >,
  ];

  let verify = [{
    /// Each result: hasOneUse. 
    return ::mlir::linear::impl::verifyLinearOpInterface($_op);
  }];
}

Additionally, we could implement a bidirectional iterator iterating over a chain of linear SSA values.

Footnotes

  1. This is also one of the reasons why I think in the long run the MLIR framework could provide a built-in QPU dialect-the analog to the GPU dialect. However, I think quantum computing is not mature enough, yet. ↩

Activity

  1. llvmorg-github-actions commented on Nov 12, 2026

    @llvmorg-github-actions

    @llvm/issue-subscribers-mlir

    Author: matthias (MatthiasReumann)

    Details # Description

    TL;DR Many quantum compilers implement linear typing. Could there be LinearTypeInterface and LinearOpInterface in upstream MLIR?

    Motivation

    The majority of MLIR-based quantum compilers that perform circuit (or program) optimization use linear typing to model quantum data-flow graphs. As all projects are built upon MLIR, their design converges and an implicit set of best practices already emerges. In other words, all projects implement exactly the same to some degree1.

    Project OpInterface Linear Type
    Catalyst QuantumOperation !quantum.bit
    CUDA-Q OperatorInterface !quake.wire
    MQT Compiler Collection UnitaryOpInterface !qco.qubit

    Proposal

    > Because I primarily work on quantum compilation, the following proposal will have a focus on that. Nonetheless, I think this ideas could be useful elsewhere also.

    A good starting point would be a type interface. Quantum compilers could implement this interface for their respective qubit types.

    def LinearTypeInterface : TypeInterface&lt;"LinearTypeInterface"&gt; {
      let description = [{
        Implemented by types whose values are linear resources: they must be
        consumed exactly once and can be neither copied nor discarded
        implicitly.
      }];
       ...
    }
    

    Consequently, we could use the following function to determine if a generic value is linear.

    bool isLinearValue(Value v) { return isa&lt;LinearTypeInterface&gt;(v.getType()); }

    An operation interface provides the necessary methods to traverse the linear data-flow graph and verifies if each result is used exactly once.

    def LinearOpInterface : OpInterface&lt;"LinearOpInterface"&gt; {
      let description = [{
        The interface declares the op's *linear data-flow*: 
            consumed -&gt;(op)-&gt; produced
      }];
      let methods = [
        InterfaceMethod&lt;
          "Returns the linear operands consumed by this operation. Default: All linear types.",
          "::mlir::SmallVector&lt;::mlir::Value&gt;", "getConsumedValues",
          (ins),
          /*methodBody=*/[{ ... }]
        &gt;,
        InterfaceMethod&lt;
          "Returns the linear results produced by this operation. Default: All linear types.",
          "::mlir::SmallVector&lt;::mlir::Value&gt;", "getProducedValues",
          (ins),
          /*methodBody=*/[{ ... }]
        &gt;,
        InterfaceMethod&lt;
          "Given a produced value, returns the consumed value it continues, or
           nullptr if the produced value is a fresh resource. Default: i-th consumed for i-th produced.",
          "::mlir::Value", "getConsumedForProduced",
          (ins "::mlir::Value":"produced")
        &gt;,
        InterfaceMethod&lt;
          "Given a consumed value, returns the produced value that continues
           it, or nullptr if this op drops the resource. Default: i-th produced for i-th consumed.",
          "::mlir::Value", "getProducedForConsumed",
          (ins "::mlir::Value":"consumed")
        &gt;,
      ];
    
      let verify = [{
        /// Each result: hasOneUse. 
        return ::mlir::linear::impl::verifyLinearOpInterface($_op);
      }];
    }
    

    Additionally, we could implement a bidirectional iterator iterating over a chain of linear SSA values.

    Footnotes

    1. This is also one of the reasons why I think in the long run the MLIR framework could provide a built-in QPU dialect-the analog to the GPU dialect. However, I think quantum computing is not mature enough, yet. ↩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions