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 compile a circuit directly.

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

mqss::mqssci::MQSSCompiler is the simplest way to compile a circuit: one header, one class, one call. It hides dialect registration, MLIR context setup, and pipeline assembly behind a single compile call.

#include "MQSSCIInterfaces/MQSSCompiler.h"

mqss::mqssci::MQSSCompiler compiler;

mqss::mqssci::CompilerOptions opts;
opts.optimization_level = mqss::mqssci::OptLevel::O1;        // O1, O2, or O3 — selects the preset pipeline
opts.result_format = mqss::mqssci::ResultFormat::OPENQASM2;  // or QIR, QIRBASE, QIRADAPTIVE, QIRFULL

std::optional<std::string> qasm = compiler.compile("path/to/circuit.qke", "planqc", opts); // Use compileSource() to parse source string
if (!qasm) {
  // Compilation failed. MQSSCompiler has already reported a diagnostic through
  // MLIR's diagnostic engine — it never throws or aborts the process.
  return 1;
}

llvm::outs() << *qasm;

Walking through it:

  1. #include "MQSSCIInterfaces/MQSSCompiler.h" is the only header you need for this path.

  2. mqss::mqssci::CompilerOptions configures the run: optimization_level selects the O1/O2/O3 preset pipeline (see Pass Pipelines for what each includes), and result_format selects the output format — one of the mqss::mqssci::ResultFormat enumerators: OPENQASM2, QIR, QIRBASE, QIRADAPTIVE, or QIRFULL.

  3. The second argument to compile ("planqc" above) is a known backend name that selects a built-in native-gate set for decomposition. See Choosing a Backend below for the alternatives.

  4. compile returns std::optional<std::string>, not the compiled circuit directly. On success, it holds the compiled output; on failure it’s std::nullopt, and the reason has already been reported via mlir::emitError — MQSSCI is built without C++ exceptions, so a failed compile never throws or crashes your program, it just returns an empty optional.

Choosing a Backend

compile has overloads for three ways to select the native-gate set used for decomposition: by a known backend name, by an explicit gate list, or not at all:

// By backend name: iqm, planqc, and wmi are recognized out of the box.
compiler.compile("path/to/circuit.qke", "iqm", opts);

// By an explicit native-gate set, when your target isn't one of the built-in backends.
compiler.compile("path/to/circuit.qke", {"rz", "rx", "cz"}, opts);

// Neither: skip native-gate decomposition entirely.
compiler.compile("path/to/circuit.qke", opts);

backend_name

Native-gate set

"iqm"

phased_rx, cz

"planqc"

rx, cz, rz

"wmi"

cz, x, y, rz

The full signature (used in the examples above via its three shorthand overloads) also accepts a qubit_connectivity map alongside the native-gate set, for targets with restricted qubit connectivity. See MQSSCIInterfaces/MQSSCompiler.h for all four compile overloads.

Discovering Supported Input Formats

MQSSCompiler::getSupportedInputFormats() is a static method that returns the display name of every input format MQSSCI’s mqss::mqssci::InputFormat enum defines:

for (std::string_view name : mqss::mqssci::MQSSCompiler::getSupportedInputFormats()) {
  llvm::outs() << name << "\n";
}

It returns a std::vector<std::string_view> (currently {"cudaq-quake", "catalyst-quantum"}) — useful for validating a user-supplied format name or listing the options in a CLI’s --help output. Note that compile/compileSource currently only accept the cudaq-quake dialect regardless of what this list reports; see the note at the top of MQSSCompiler.cpp.

Advanced: Building a Custom Pipeline Yourself

MQSSCompiler covers the common case: a preset optimization level, a fixed native-gate set, and one of two output formats. If you need to run individual passes, pass pass-specific options (e.g. CommonGateCancellationPassOptions), or assemble a pipeline MQSSCompiler doesn’t support, drop down to the same mlir::OpPassManager-based API MQSSCompiler itself is built on:

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

// 1. Get an MLIRContext with every dialect MQSSCI's passes need already loaded.
std::unique_ptr<mlir::MLIRContext> context = mqss::mqssci::opt::createMQSSContext();

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

// 3. Build a pass manager and add passes to the pipeline
mlir::PassManager pm(context.get());
CommonGateCancellationPassOptions CancelOpts;
CommonCommutePassOptions CommuteOpts;
CancelOpts.mode = "CancelGate";
CommuteOpts.mode = "CX-RX";

pm.addPass(CommonGateCancellationPass(CancelOpts));
pm.addPass(CommonCommutePass(CommuteOpts));

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

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

mqss::mqssci::opt::createMQSSContext() is a convenience that registers every MQSSCI dialect and returns a ready-to-use MLIRContext in one call — equivalent to building a DialectRegistry with registerMQSSDialects, constructing an MLIRContext from it, and calling loadAllAvailableDialects() yourself.

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::mqssci::codegen::QuakeToQASM2Pass(os));

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

Testing the Interface

MQSSCompiler has its own testing framework at tests/unittests/MQSSCIInterfaces/test_Interface.cpp. It builds automatically as part of the normal build (via scripts/build.sh / make target) — run it with:

make test-interfaces

For what the interfaces testing framework covers and how to extend it, see Testing the Interface in the developer guide.