Development Guide

Ready to contribute to the project? Here is how to set up a local development environment.

Initial Setup

  1. Fork the cda-tum/qcec repository on GitHub (see

  2. Clone your fork locally

    $ git clone --recursive


    The --recursive flag is required to also clone all the required submodules. If you happen to forget passing the flag on your initial clone, you can initialize all the submodules by executing git submodule update --init --recursive in the main project directory.

  3. Change into the project directory

    $ cd mqt-qcec
  4. Create a branch for local development

    $ git checkout -b name-of-your-bugfix-or-feature

    Now you can make your changes locally.

  5. (Optional, highly recommended) Set up a virtual environment

    $ python3 -m venv venv
    $ source venv/bin/activate


    If you are using Windows, you can use the following command instead:

    $ python3 -m venv venv
    $ venv\Scripts\activate.bat

    Ensure that pip, setuptools, and wheel are up to date:

    (venv) $ pip install --upgrade pip setuptools wheel
  6. (Optional, highly recommended) Setup nox to conveniently run many development tasks.

    (venv) $ pipx install nox

    If you use macOS, then nox is in brew, use brew install nox.


    If you do not have pipx (pip for applications) installed, you can install it with:

    (venv) $ pip install pipx
    (venv) $ pipx ensurepath

    If you use macOS, then pipx is in brew, use brew install pipx.

  7. (Optional) Install pre-commit to automatically run a set of checks before each commit.

    (venv) $ pipx install pre-commit
    (venv) $ pre-commit install

    If you use macOS, then pre-commit is in brew, use brew install pre-commit.

Working on the core C++ library

Building the project requires a C++ compiler supporting C++17 and CMake with a minimum version of 3.19.


We noticed some issues when compiling with Microsoft’s MSCV compiler toolchain. If you want to start development on this project under Windows, consider using the clang compiler toolchain. A detailed description of how to set this up can be found here.

Configure and Build

Our projects use CMake as the main build configuration tool. Building a project using CMake is a two-stage process. First, CMake needs to be configured by calling


This tells CMake to

  • search the current directory . (passed via -S) for a CMakeLists.txt file.

  • process it into a directory build (passed via -B).

  • the flag -DCMAKE_BUILD_TYPE=Release tells CMake to configure a Release build (as opposed to, e.g., a Debug build).

  • the flag -DBUILD_QCEC_TESTS=ON tells CMake to also build the C++ tests.

  • the flag -DBINDINGS=ON tells CMake to also build the Python bindings.

After configuring with CMake, the project can be built by calling

$ cmake --build build --config Release

This tries to build the project in the build directory (passed via --build). Some operating systems and development environments explicitly require a configuration to be set, which is why the --config flag is also passed to the build command. The flag --parallel <NUMBER_OF_THREADS> may be added to trigger a parallel build.

Building the project this way generates

  • the main library libqcec.a (Unix) / qcec.lib (Windows) in the build/src directory

  • a test executable qcec_test containing unit tests in the build/test directory

  • the Python bindings library pyqcec.<...> in the build/mqt/qcec directory

Running C++ Tests

We use the GoogleTest framework for unit testing of the C++ library. All tests are contained in the test directory. After building the project (as described above), the C++ unit tests can be run by executing the test executable qcec_test in the build/test directory.

[.../build/test] $ ./qcec_test

C++ Code Formatting and Linting

This project mostly follows the LLVM Coding Standard, which is a set of guidelines for writing C++ code. To ensure the quality of the code and that it conforms to these guidelines, we use

  • clang-tidy – a static analysis tool that checks for common mistakes in C++ code, and

  • clang-format – a tool that automatically formats C++ code according to a given style guide.

Common IDEs like Visual Studio Code or CLion have plugins that can automatically run clang-tidy on the code and automatically format it with clang-format.

  • If you are using Visual Studio Code, you can install the clangd extension.

  • If you are using CLion, you can configure the project to use the .clang-tidy and .clang-format files in the project root directory.

They will automatically execute clang-tidy on your code and highlight any issues. In many cases, they also provide quick-fixes for these issues. Furthermore, they provide a command to automatically format your code according to the given style.


If you want to use clang-tidy from the command line, you first have to configure CMake with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON to generate a compilation database. It needs this information to correctly analyze the code. After configuring CMake, you can run clang-tidy on a file by calling


where <FILE> is the file you want to analyze and <PATH_TO_INCLUDE_DIRECTORY> is the path to the include directory of the project.

Working on the Python module

Pybind11 is used for providing bindings of the C++ core library to Python. This allows to keep the performance critical parts of the code in C++ while providing a convenient interface for Python users. All of the bindings code as well as the Python module itself is contained in the mqt/qcec directory.

Building the Python module

The recommended way of building the Python module is to perform an editable install using pip.

(venv) $ pip install --editable .[dev]

The --editable flag ensures that changes in the Python code are instantly available without re-running the command. The [dev] extra makes sure that all dependencies for running the Python tests and building the documentation are available.


When using the zsh shell it might be necessary to add double quotes around the .[dev] part of the command.


Do not forget to run the above command again after any changes to the C++ core library or bindings to make the changes available in the Python module.

Running Python Tests

The Python part of the code base is tested by unit tests using the pytest framework. The corresponding test files can be found in the test/python directory. A nox session is provided to conveniently run the Python tests.

(venv) $ nox -rs tests

This installs all dependencies for running the tests in an isolated environment, builds the Python package, and then runs the tests. The -r flag ensures that the environment is reused for subsequent runs. To speed up subsequent runs, the installation step can be skipped by adding the skip-install flag.

(venv) $ nox -rs tests -- skip-install


If you don’t want to use nox, you can also run the tests directly using pytest.

(venv) $ pytest test/python

Python Code Formatting and Linting

The Python code is formatted and linted using a collection of pre-commit hooks. This collection includes:

  • black – a code formatter that automatically formats Python code according to the PEP 8 style guide

  • flake8 – a linter that checks for common mistakes in Python code

  • isort – a tool that automatically sorts Python imports according to the PEP 8 style guide

  • mypy – a static type checker for Python code

  • pyupgrade – a tool that automatically upgrades Python syntax to a newer version

There are two ways of using these hooks:

  • You can install the hooks manually by running pre-commit install in the project root directory. This will install the hooks in the .git/hooks directory of the repository. The hooks will then be executed automatically when committing changes.

  • You can use the nox session lint to run the hooks manually.

    (venv) $ nox -rs lint


    If you don’t want to use nox, you can also run the hooks directly using pre-commit.

    (venv) $ pre-commit run --all-files

In addition to the pre-commit hooks, the Python code is also type checked by mypy. This is done by the nox session mypy.

(venv) $ nox -rs mypy

Working on the Documentation

The documentation is written in reStructuredText and built using Sphinx. The documentation source files can be found in the docs/source directory. You can build the documentation using the nox session docs.

(venv) $ nox -rs docs


In order to properly build the jupyter notebooks in the documentation, you need to have pandoc installed. See the pandoc documentation for installation instructions.

This will install all dependencies for building the documentation in an isolated environment, build the Python package, and then build the documentation. The session also provides a convenient option to automatically serve the docs on a local web server. Running

(venv) $ nox -rs docs -- serve

will start a local web server on port 8000 and provide a link to open the documentation in your browser.

To build the documentation without (re-)installing the Python package, you can use the skip-install flag.

(venv) $ nox -rs docs -- skip-install


If you don’t want to use nox, you can also build the documentation directly using sphinx-build.

(venv) $ sphinx-build -b html docs/source docs/build

The docs can then be found in the docs/build directory.