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:
#include "MQSSCIInterfaces/MQSSCompiler.h"is the only header you need for this path.mqss::mqssci::CompilerOptionsconfigures the run:optimization_levelselects theO1/O2/O3preset pipeline (see Pass Pipelines for what each includes), andresult_formatselects the output format — one of themqss::mqssci::ResultFormatenumerators:OPENQASM2,QIR,QIRBASE,QIRADAPTIVE, orQIRFULL.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.compilereturnsstd::optional<std::string>, not the compiled circuit directly. On success, it holds the compiled output; on failure it’sstd::nullopt, and the reason has already been reported viamlir::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);
|
Native-gate set |
|---|---|
|
|
|
|
|
|
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.