Using MQSSCI as a Library

The Passes page shows how to run passes through the mqss-opt/mqss-cc command-line tools. This page is for the case where you don’t want a separate command-line step at all — you want your own C++ program to load an MLIR module and run MQSSCI passes on it directly.

The two are not different features: mqss-opt itself is just a small C++ program that does exactly what’s shown below. Calling it from your own code gives you the same passes, without shelling out to a separate binary.

Before You Start

Your project needs to link against the MQSSCI library first. That’s a one-time CMake setup step — see Integrating the MQSS Quantum Compilation Suite within your project for how to fetch MQSSCI with FetchContent and link your target against mqss-ci::mqss-ci. The rest of this page assumes that step is done and focuses purely on the C++ side.

A Minimal Example

Here’s the smallest useful program: load an MLIR file, run the O1 preset pipeline on it, then lower the result to OpenQASM 2.

#include "Passes/Transforms/Dialects.h"
#include "Passes/Transforms/Pipelines.h"

// 1. Tell MLIR about the dialects MQSSCI's passes need (Quake, Catalyst-quantum, etc.)
mlir::DialectRegistry registry;
mqss::opt::registerMQSSDialects(registry);
mlir::MLIRContext context(registry);
context.loadAllAvailableDialects();

// 2. Parse your MLIR file into a module
auto module = mlir::parseSourceFile<mlir::ModuleOp>(src_path, &context);
if (!module) {
  llvm::errs() << "failed to parse MLIR file\n";
}

// 3. Build a pass manager and add a preset optimization pipeline
mlir::PassManager pm(&context);
mqss::opt::O1(pm);

if (mlir::failed(pm.run(*module))) {
  llvm::errs() << "Compiler: Pipeline failed\n";
}

// 4. Lower the optimized module to OpenQASM 2
pm.addPass(mqss::opt::QuakeToQASM2Pass());
if (mlir::failed(pm.run(*module))) {
  llvm::errs() << "Compiler: Conversion of Quake to QASM2 failed\n";
}

Walking through it:

  1. registerMQSSDialects fills in a DialectRegistry with every MLIR dialect the passes operate on or lower into. Do this before parsing anything — the parser needs to recognize the dialect operations in your input.

  2. mlir::parseSourceFile reads your MLIR/Quake/Catalyst-quantum source file into an in-memory module.

  3. mqss::opt::O1(pm) appends the whole O1 preset pipeline to the pass manager in one call. O2 and O3 work the same way. See Pass Pipelines for what each preset includes.

  4. mqss::opt::QuakeToQASM2Pass() is a code-generation pass — it’s added and run separately from the optimization pipeline, same as chaining --O1 --quake-to-qasm2 on the mqss-opt command line.

If you’d rather capture the OpenQASM output in a string instead of printing it, QuakeToQASM2Pass also accepts an llvm::raw_ostream&:

std::string qasm;
llvm::raw_string_ostream os(qasm);
pm.addPass(mqss::opt::QuakeToQASM2Pass(os));

Invoking Passes: Same Rules as the Developer Guide

Everything beyond this minimal example — running individual passes instead of a preset, passing options to a pass (e.g. CommonGateCancellationPassOptions), or assembling a fully custom pipeline — works exactly as described in Integrating the MQSS Quantum Compilation Suite within your project. There’s no separate API for library users: whether you’re writing a pass yourself or just consuming the library, you build the same mlir::OpPassManager and call the same factory functions from Passes/Transforms/Transforms.h.

For the full list of available passes, their options, and valid option values, see Passes.