Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Clifford-only simulator (Stim) #2193

Merged
merged 12 commits into from
Sep 12, 2024
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@
path = tpls/Crow
url = https://github.com/CrowCpp/Crow.git
ignore = dirty
[submodule "tpls/Stim"]
path = tpls/Stim
url = https://github.com/quantumlib/Stim
8 changes: 8 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,11 @@ Tweedledum - MIT License
License at <https://github.com/boschmitt/tweedledum/blob/master/LICENSE>

----------------------------------------------------------------

Stim - Apache License 2.0
<https://github.com/quantumlib/Stim>

The incorporated source code and its license can be found as a submodule on the CUDA-Q repository.
License at <https://github.com/quantumlib/Stim/blob/main/LICENSE>

----------------------------------------------------------------
1 change: 1 addition & 0 deletions docs/sphinx/using/backends/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ CUDA-Q Backends
* :ref:`qpp-cpu <qpp-cpu-backend>`
* :ref:`quantinuum <quantinuum-backend>`
* :ref:`remote-mqpu <mqpu-platform>`
* :ref:`stim <stim-backend>`
* :ref:`tensornet <tensor-backends>`
* :ref:`tensornet-mps <tensor-backends>`

Expand Down
34 changes: 34 additions & 0 deletions docs/sphinx/using/backends/simulators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,40 @@ use the following commands:
./program.x


Clifford-Only Simulation (CPU)
++++++++++++++++++++++++++++++++++

.. _stim-backend:

This target provides a fast simulator for circuits containing *only* Clifford
gates. Any non-Clifford gates (such as T gates and Toffoli gates) are not
supported. This simulator is based on the `Stim <https://github.com/quantumlib/Stim>`_
library.

To execute a program on the :code:`stim` target, use the following commands:

.. tab:: Python

.. code:: bash

python3 program.py [...] --target stim

The target can also be defined in the application code by calling

.. code:: python

cudaq.set_target('stim')

If a target is set in the application code, this target will override the :code:`--target` command line flag given during program invocation.

.. tab:: C++

.. code:: bash

nvq++ --target stim program.cpp [...] -o program.x
./program.x


Tensor Network Simulators
==================================

Expand Down
91 changes: 91 additions & 0 deletions python/tests/backends/test_stim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ============================================================================ #
# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. #
# All rights reserved. #
# #
# This source code and the accompanying materials are made available under #
# the terms of the Apache License 2.0 which accompanies this distribution. #
# ============================================================================ #

import os
from typing import List
import pytest

import cudaq
import numpy as np


@pytest.fixture(scope="session", autouse=True)
def setTarget():
old_target = cudaq.get_target()
cudaq.set_target('stim')
yield
cudaq.set_target(old_target)


def test_stim_non_clifford():

@cudaq.kernel
def kernel():
qubits = cudaq.qvector(10)
rx(0.1, qubits[0])

with pytest.raises(RuntimeError) as e:
# Cannot perform non-Clifford gates in Stim simulator
cudaq.sample(kernel)


def test_stim_toffoli_gates():

@cudaq.kernel
def kernel():
qubits = cudaq.qvector(10)
cx(qubits[0:9], qubits[9])

with pytest.raises(RuntimeError) as e:
# Cannot perform Toffoli gates in Stim simulator
cudaq.sample(kernel)


def test_stim_sample():
# Create the kernel we'd like to execute on Stim
@cudaq.kernel
def kernel():
qubits = cudaq.qvector(250)
h(qubits[0])
# Stim is a Clifford-only simulator, so it can do many qubits.
for i in range(1, 250):
cx(qubits[i - 1], qubits[i])
mz(qubits)

counts = cudaq.sample(kernel)
assert (len(counts) == 2)
assert ('0' * 250 in counts)
assert ('1' * 250 in counts)


def test_stim_state_preparation():

@cudaq.kernel
def kernel(vec: List[complex]):
qubits = cudaq.qvector(vec)

with pytest.raises(RuntimeError) as e:
# Cannot initialize qubits from state data in this simulator
state = [1. / np.sqrt(2.), 1. / np.sqrt(2.), 0., 0.]
cudaq.sample(kernel, state)


def test_stim_state_preparation_builder():
kernel, state = cudaq.make_kernel(List[complex])
qubits = kernel.qalloc(state)

with pytest.raises(RuntimeError) as e:
# Cannot initialize qubits from state data in this simulator
state = [1. / np.sqrt(2.), 1. / np.sqrt(2.), 0., 0.]
cudaq.sample(kernel, state)


# leave for gdb debugging
if __name__ == "__main__":
loc = os.path.abspath(__file__)
pytest.main([loc, "-s"])
1 change: 1 addition & 0 deletions runtime/nvqir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ install(TARGETS ${LIBRARY_NAME}
INCLUDES DESTINATION include/nvqir)

add_subdirectory(qpp)
add_subdirectory(stim)

if (CUSTATEVEC_ROOT AND CUDA_FOUND)
add_subdirectory(custatevec)
Expand Down
12 changes: 11 additions & 1 deletion runtime/nvqir/CircuitSimulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,17 @@ class CircuitSimulatorBase : public CircuitSimulator {
summaryData.svGateUpdate(
next.controls.size(), next.targets.size(), stateDimension,
stateDimension * sizeof(std::complex<ScalarType>));
applyGate(next);
try {
applyGate(next);
} catch (std::exception &e) {
while (!gateQueue.empty())
gateQueue.pop();
throw e;
} catch (...) {
while (!gateQueue.empty())
gateQueue.pop();
throw std::runtime_error("Unknown exception in applyGate");
}
if (executionContext && executionContext->noiseModel) {
std::vector<std::size_t> noiseQubits{next.controls.begin(),
next.controls.end()};
Expand Down
42 changes: 42 additions & 0 deletions runtime/nvqir/stim/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ============================================================================ #
# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. #
# All rights reserved. #
# #
# This source code and the accompanying materials are made available under #
# the terms of the Apache License 2.0 which accompanies this distribution. #
# ============================================================================ #

set(LIBRARY_NAME nvqir-stim)
set(INTERFACE_POSITION_INDEPENDENT_CODE ON)

set(STIM_SOURCE_DIR ${CMAKE_SOURCE_DIR}/tpls/Stim)
set(STIM_BINARY_DIR ${CMAKE_BINARY_DIR}/tpls/Stim)

# The EXCLUDE_FROM_ALL makes it so that only libstim is built. If other targets
# are desired (like the command-line tool), remove EXCLUDE_FROM_ALL below.
add_subdirectory(${STIM_SOURCE_DIR} ${STIM_BINARY_DIR} EXCLUDE_FROM_ALL)

add_library(${LIBRARY_NAME} SHARED StimCircuitSimulator.cpp)
set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME})

set (STIM_DEPENDENCIES libstim fmt::fmt-header-only cudaq-common)

# If -Wall is enabled (as is done in parent directories), Stim will not compile.
# So override that here.
target_compile_options(libstim PRIVATE -Wno-all)

target_include_directories(${LIBRARY_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/runtime>
$<INSTALL_INTERFACE:include>)

target_link_libraries(${LIBRARY_NAME}
PRIVATE ${STIM_DEPENDENCIES})

set_target_properties(${LIBRARY_NAME}
PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:${LLVM_BINARY_DIR}/lib")

install(TARGETS ${LIBRARY_NAME} DESTINATION lib)

add_target_config(stim)
Loading
Loading