Skip to content

Latest commit

 

History

History
598 lines (449 loc) · 17.3 KB

developer_documentation.md

File metadata and controls

598 lines (449 loc) · 17.3 KB

Stim Developer Documentation

This is documentation for programmers working with stim, e.g. how to build it. These notes generally assume you are on a Linux system.

Index

Releasing a new version

  • Create an off-main-branch release commit
    • Update version to vX.Y.Z in all setup.py files
    • Commit changes
    • git tag vX.Y.Z
    • Push tag to github
    • Get stim wheels from cibuildwheels of this tag
    • Build stimcirq sdist on this tag using python setup.py sdist
    • Combine stim and stimcirq package files into one directory
  • Bump to next dev version on main branch
    • Update version to vX.Y.dev0 in all setup.py files
    • Update INTENTIONAL_VERSION_SEED_INCOMPATIBILITY in src/stim/circuit/circuit.h
    • Push to github as a branch and merge into main using a pull request
  • Write release notes on github
    • In title, use two-word themeing of most important changes
    • Flagship changes section
    • Notable changes section
    • Include wheels/sdists as attachments
  • Do irreversible steps last
    • Upload wheels/sdists to pypi using twine
    • Publish the github release notes
    • Add gates reference page to wiki for the new version
    • Add python api reference page to wiki for the new version
    • Update main wiki page to point to latest reference pages
    • Tweet about the release

Building stim command line tool

The stim command line tool is a binary program stim that accepts commands like stim sample -shots 100 -in circuit.stim (see the command line reference). It can be built with cmake, with bazel, or manually with gcc.

Building stim command line tool with cmake

# from the repository root:
cmake .
make stim

# output binary ends up at:
# ./out/stim

Vectorization can be controlled by passing the flag -DSIMD_WIDTH to cmake:

  • cmake . -DSIMD_WIDTH=256 means "use 256 bit avx operations" (forces -mavx2)
  • cmake . -DSIMD_WIDTH=128 means "use 128 bit sse operations" (forces -msse2)
  • cmake . -DSIMD_WIDTH=64 means "don't use simd operations" (no machine arch flags)
  • cmake . means "use the best thing possible on this machine" (forces -march=native)

Building stim command line tool with bazel

bazel build stim

or, to build and run:

bazel run stim

Building stim command line tool with gcc

# from the repository root:
find src \
    | grep "\\.cc" \
    | grep -v "\\.\(test\|perf\|pybind\)\\.cc" \
    | xargs g++ -I src -pthread -std=c++11 -O3 -march=native

# output binary ends up at:
# ./a.out

Linking to libstim shared library

!!!CAUTION!!! Stim's C++ API is not kept stable! Always pin to a specific version! I WILL break your downstream code when I update stim if you don't! The API is also not extensively documented; what you can find in the headers is what you get.

To use Stim functionality within your C++ program, you can build libstim and link to it to gain direct access to underlying Stim types and methods.

If you want a stim API that promises backwards compatibility, use the python API.

Linking to libstim shared library with cmake

!!!CAUTION!!! Stim's C++ API is not kept stable! Always pin to a specific version! I WILL break your downstream code when I update stim if you don't! The API is also not extensively documented; what you can find in the headers is what you get.

In your CMakeLists.txt file, use FetchContent to automatically fetch stim from github when running cmake .:

# in CMakeLists.txt file

include(FetchContent)
FetchContent_Declare(stim
        GIT_REPOSITORY https://github.com/quantumlib/stim.git
        GIT_TAG v1.4.0)  # [[[<<<<<<< customize the version you want!!]]]
FetchContent_GetProperties(stim)
if(NOT stim_POPULATED)
  FetchContent_Populate(stim)
  add_subdirectory(${stim_SOURCE_DIR})
endif()

(Replace v1.4.0 with another version tag as appropriate.)

For build targets that need to use stim functionality, add libstim to them using target_link_libraries:

# in CMakeLists.txt file

target_link_libraries(some_cmake_target PRIVATE libstim)

In your source code, use #include "stim.h" to access stim types and functions:

// in a source code file

#include "stim.h"

stim::Circuit make_bell_pair_circuit() {
    return stim::Circuit(R"CIRCUIT(
        H 0
        CNOT 0 1
        M 0 1
        DETECTOR rec[-1] rec[-2]
    )CIRCUIT");
}

Linking to libstim shared library with bazel

!!!CAUTION!!! Stim's C++ API is not kept stable! Always pin to a specific version! I WILL break your downstream code when I update stim if you don't! The API is also not extensively documented; what you can find in the headers is what you get.

In your WORKSPACE file, include stim's git repo using git_repository:

# in WORKSPACE file

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

git_repository(
    name = "stim",
    commit = "v1.4.0",
    remote = "https://github.com/quantumlib/stim.git",
)

(Replace v1.4.0 with another version tag or commit SHA as appropriate.)

In your BUILD file, add @stim//:stim_lib to the relevant target's deps:

# in BUILD file

cc_binary(
    ...
    deps = [
        ...
        "@stim//:stim_lib",
        ...
    ],
)

In your source code, use #include "stim.h" to access stim types and functions:

// in a source code file

#include "stim.h"

stim::Circuit make_bell_pair_circuit() {
    return stim::Circuit(R"CIRCUIT(
        H 0
        CNOT 0 1
        M 0 1
        DETECTOR rec[-1] rec[-2]
    )CIRCUIT");
}

Running unit tests

Stim's code base includes a variety of types of tests, spanning over a few packages and languages.

Running C++ unit tests with cmake

Unit testing with cmake requires the GTest library to be installed on your system in a place that cmake can find it. Follow the "Standalone CMake Project" instructions from the GTest README to get GTest installed correctly.

Run tests with address and memory sanitization, without compile time optimization:

# from the repository root:
cmake .
make stim_test
./out/stim_test

Run tests without sanitization, with compile time optimization:

# from the repository root:
cmake .
make stim_test_o3
./out/stim_test_o3

Stim supports 256 bit (AVX), 128 bit (SSE), and 64 bit (native) vectorization. The type to use is chosen at compile time. To force this choice (so that each case can be tested on one machine), add -DSIMD_WIDTH=256 or -DSIMD_WIDTH=128 or -DSIMD_WIDTH=64 to the cmake . command.

Running C++ unit tests with bazel

# from the repository root:
bazel test stim_test

Running performance benchmarks

Running performance benchmarks with cmake

cmake .
make stim_benchmark
./out/stim_benchmark

Running performance benchmarks with bazel

bazel run stim_benchmark

Interpreting output from stim_benchmark

When you run stim_benchmark you will see output like:

[....................*....................] 460 ns (vs 450 ns) ( 21 GBits/s) simd_bits_randomize_10K
[...................*|....................]  24 ns (vs  20 ns) (400 GBits/s) simd_bits_xor_10K
[....................|>>>>*...............] 3.6 ns (vs 4.0 ns) (270 GBits/s) simd_bits_not_zero_100K
[....................*....................] 5.8 ms (vs 6.0 ms) ( 17 GBits/s) simd_bit_table_inplace_square_transpose_diam10K
[...............*<<<<|....................] 8.1 ms (vs 5.0 ms) ( 12 GOpQubits/s) FrameSimulator_depolarize1_100Kqubits_1Ksamples_per1000
[....................*....................] 5.3 ms (vs 5.0 ms) ( 18 GOpQubits/s) FrameSimulator_depolarize2_100Kqubits_1Ksamples_per1000

The bars on the left show how fast each task is running compared to baseline expectations (on my dev machine). Each tick away from the center | is 1 decibel slower or faster (i.e. each < or > represents a factor of 1.26).

Basically, if you see [......*<<<<<<<<<<<<<|....................] then something is seriously wrong, because the code is running 25x slower than expected.

The benchmark binary supports a --only=BENCHMARK_NAME filter flag. Multiple filters can be specified by separating them with commas --only=A,B. Ending a filter with a * turns it into a prefix filter --only=sim_*.

Profiling with gcc and perf

find src \
    | grep "\\.cc" \
    | grep -v "\\.\(test\|perf\|pybind\)\\.cc" \
    | xargs g++ -I src -pthread -std=c++11 -O3 -march=native -g -fno-omit-frame-pointer
sudo perf record -g ./a.out  # [ADD STIM FLAGS FOR THE CASE YOU WANT TO PROFILE]
sudo perf report

Creating a python dev environment

First, create a fresh python 3.6+ virtual environment using your favorite method: python -m venv, virtualenvwrapper, conda, or etc.

Second, build and install a stim wheel. Follow the python packaging stim instructions to create a wheel. I recommend packaging with bazel because it is BY FAR the fastest. Once you have the wheel, run pip install [that_wheel]. For example:

# from the repository root in a python virtual environment
bazel build :stim_dev_wheel
pip uninstall stim --yes
pip install bazel-bin/stim-dev-py3-none-any.whl

Note that you need to repeat the above steps each time you make a change to stim.

Third, use pip install -e to install development references to the pure-python glue packages:

# install stimcirq dev reference:
pip install -e glue/cirq

# install sinter dev reference:
pip install -e glue/sample

# install stimzx dev reference:
pip install -e glue/zx

Running python unit tests

See creating a python dev environment for instructions on creating a python virtual environment with your changes to stim installed.

Unit tests are run using pytest. Examples in docstrings are tested using doctest.

To test everything:

# from the repository root in a virtualenv with development wheels installed:
pytest src glue
python -c "import stim; import doctest; assert doctest.testmod(stim).failed == 0"
python -c "import stimcirq; import doctest; assert doctest.testmod(stimcirq).failed == 0"
python -c "import sinter; import doctest; assert doctest.testmod(sinter).failed == 0"
python -c "import stimzx; import doctest; assert doctest.testmod(stimzx).failed == 0"

Test only stim:

# from the repository root in a virtualenv with development wheels installed:
pytest src
python -c "import stim; import doctest; assert doctest.testmod(stim).failed == 0"

Test only stimcirq:

# from the repository root in a virtualenv with development wheels installed:
pytest glue/cirq
python -c "import stimcirq; import doctest; assert doctest.testmod(sitmcirq).failed == 0"

Test only sinter:

# from the repository root in a virtualenv with development wheels installed:
pytest glue/sample
python -c "import sinter; import doctest; assert doctest.testmod(sinter).failed == 0"

Test only stimzx:

# from the repository root in a virtualenv with development wheels installed:
pytest glue/zx
python -c "import stimzx; import doctest; assert doctest.testmod(stimzx).failed == 0"

python packaging stim

Because stim is a C++ extension, it is non-trivial to create working python packages for it. To make cross-platform release wheels, we rely heavily on cibuildwheels. To make development wheels, we various other options are possible.

python packaging stim with cibuildwheels

When a commit is merged into the main branch of stim's github repository, there are github actions that use cibuildwheels to build wheels for all supported platforms.

When these wheels are finished building, they are automatically uploaded to pypi as a dev version of stim. For release versions, the artifacts created by the github action must be manually downloaded and uploaded using twine:

twine upload --username="${PROD_TWINE_USERNAME}" --password="${PROD_TWINE_PASSWORD}" artifacts_directory/*

python packaging stim with bazel

Bazel can be used to create dev versions of the stim python wheel:

# from the repository root:
bazel build stim_dev_wheel
# output is at bazel-bin/stim-dev-py3-none-any.whl

python packaging stim with python setup.py

Python can be used to create dev versions of the stim python wheel (very slow):

Binary distribution:

# from the repository root in a python venv with pybind11 installed:
python setup.py bdist
# output is at dist/*

Source distribution:

# from the repository root in a python venv with pybind11 installed:
python setup.py sdist
# output is at dist/*

python packaging stim with pip install -e

You can directly install stim as a development python wheel by using pip (very slow):

# from the repository root
pip install -e .
# output is at dist/*

Python packaging stimcirq

Python packaging stimcirq with python setup.py

# from repo root
cd glue/cirq
python setup.py sdist
cd -
# output in glue/cirq/dist/*

Python packaging stimcirq with pip install -e

# from repo root
pip install -e glue/cirq
# stimcirq is now installed in current virtualenv as dev reference

Python packaging sinter

Python packaging sinter with python setup.py

# from repo root
cd glue/sample
python setup.py sdist
cd -
# output in glue/sample/dist/*

Python packaging sinter with pip install -e

# from repo root
pip install -e glue/sample
# sinter is now installed in current virtualenv as dev reference

Python packaging stimzx

Python packaging stimzx with python setup.py

# from repo root
cd glue/zx
python setup.py sdist
cd -
# output in glue/zx/dist/*

Python packaging stimzx with pip install -e

# from repo root
pip install -e glue/zx
# stimzx is now installed in current virtualenv as dev reference

Javascript packaging stimjs

Javascript packaging stimjs with emscripten

Install and activate enscriptem (emcc must be in your PATH). Example:

# [outside of repo]
git clone [email protected]:emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source emsdk_env.sh

Run the bash build script:

# [from repo root]
glue/javascript/build_wasm.sh

Outputs are the binary out/stim.js and the test runner out/all_stim_tests.html. Run tests by opening in a browser and checking for an All tests passed. message in the browser console:

firefox out/all_stim_tests.html

Autoformating code

Autoformating code with clang-format

Run the following command from the repo root to auto-format all C++ code:

find src | grep "\.\(cc\|h\)$" | xargs clang-format -i