This is documentation for programmers working with stim, e.g. how to build it. These notes generally assume you are on a Linux system.
- releasing a new version
- building
stim
command line tool - linking to
libstim
shared library - running C++ tests
- running performance benchmarks
- creating a python dev environment
- running python unit tests
- python packaging
stim
- python packaging
stimcirq
- python packaging
sinter
- javascript packaging
stimjs
- autoformating code
- 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
andstimcirq
package files into one directory
- Update version to
- Bump to next dev version on main branch
- Update version to
vX.Y.dev0
in all setup.py files - Update
INTENTIONAL_VERSION_SEED_INCOMPATIBILITY
insrc/stim/circuit/circuit.h
- Push to github as a branch and merge into main using a pull request
- Update version to
- 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
- Upload wheels/sdists to pypi using
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.
# 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
)
bazel build stim
or, to build and run:
bazel run stim
# 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
!!!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.
!!!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");
}
!!!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");
}
Stim's code base includes a variety of types of tests, spanning over a few packages and languages.
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.
# from the repository root:
bazel test stim_test
cmake .
make stim_benchmark
./out/stim_benchmark
bazel run 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_*
.
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
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
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"
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.
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/*
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 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/*
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/*
# from repo root
cd glue/cirq
python setup.py sdist
cd -
# output in glue/cirq/dist/*
# from repo root
pip install -e glue/cirq
# stimcirq is now installed in current virtualenv as dev reference
# from repo root
cd glue/sample
python setup.py sdist
cd -
# output in glue/sample/dist/*
# from repo root
pip install -e glue/sample
# sinter is now installed in current virtualenv as dev reference
# from repo root
cd glue/zx
python setup.py sdist
cd -
# output in glue/zx/dist/*
# from repo root
pip install -e glue/zx
# stimzx is now installed in current virtualenv as dev reference
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
Run the following command from the repo root to auto-format all C++ code:
find src | grep "\.\(cc\|h\)$" | xargs clang-format -i