Creating custom pipelines in QAT

We can bring together all the granular components of a pipeline to create a custom pipeline. See below.

Let’s recreate the echo pipeline!

  • AutoFrontend: dispatches the correct frontend, decided by the input type.

  • DefaultMiddleend: Applies default optimization and lowering of pulse-level IR.

  • WaveformBackend: Transforms the IR into a binary for “Waveform” hardware.

  • SimpleRuntime: Applies a hybrid of execution on the hardware, followed by a results post-processing pipeline with an EchoEngine in this example.

  • EchoEngine: Executes the binary against an Echo simulator.

from qat.backend.waveform import WaveformBackend
from qat.core.pipeline import Pipeline
from qat.engines.waveform import EchoEngine
from qat.frontend import AutoFrontend
from qat.middleend.default import DefaultMiddleend
from qat.model.loaders.lucy import LucyModelLoader
from qat.model.target_data import DefaultTargetData
from qat.runtime import SimpleRuntime
from qat.runtime.results_pipeline import get_results_pipeline

model = LucyModelLoader(qubit_count=8).load()
target_data = DefaultTargetData()
results_pipeline = get_results_pipeline(model=model, target_data=target_data)
new_echo8 = Pipeline(
    name="new_echo8",
    frontend=AutoFrontend.default_for_pydantic(model),
    middleend=DefaultMiddleend(model, target_data),
    backend=WaveformBackend(model, target_data),
    runtime=SimpleRuntime(EchoEngine(), results_pipeline=results_pipeline),
    model=model,
    target_data=target_data,
)

If we want to make this an updateable pipeline, we can do so as shown below. The _build_pipeline acts a factory method with the recipe to build a pipeline provided the model and target data.

from qat.backend.waveform import WaveformBackend
from qat.core.pipeline import Pipeline
from qat.engines.waveform import EchoEngine
from qat.frontend import AutoFrontend
from qat.middleend.default import DefaultMiddleend
from qat.model.loaders.lucy import LucyModelLoader
from qat.model.target_data import DefaultTargetData, TargetData
from qat.pipelines.updateable import Model, PipelineConfig, UpdateablePipeline
from qat.runtime import SimpleRuntime
from qat.runtime.results_pipeline import get_results_pipeline


class MyCoolPipeline(UpdateablePipeline):
    @staticmethod
    def _build_pipeline(
        config: PipelineConfig,
        model: Model,
        target_data: TargetData | None,
        *args,
        **kwargs,
    ) -> Pipeline:
        results_pipeline = get_results_pipeline(model=model, target_data=target_data)
        return Pipeline(
            name="new_echo8",
            frontend=AutoFrontend.default_for_pydantic(model),
            middleend=DefaultMiddleend(model, target_data),
            backend=WaveformBackend(model, target_data),
            runtime=SimpleRuntime(EchoEngine(), results_pipeline=results_pipeline),
            model=model,
            target_data=target_data,
        )


model = LucyModelLoader(qubit_count=8).load()
target_data = DefaultTargetData()
pipeline_instance = MyCoolPipeline(
    config=dict(name="my_pipeline"), model=model, target_data=target_data
)