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 Surface Code #7

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions docs/sphinx/api/core/cpp_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,36 @@ Namespaces
:desc-only:
.. doxygennamespace:: cudaq::qec::steane
:desc-only:
.. doxygennamespace:: cudaq::qec::surface_code
:desc-only:
.. doxygennamespace:: cudaq::qec::repetition
:desc-only:
.. doxygennamespace:: cudaq::solvers
:desc-only:
.. doxygennamespace:: cudaq::solvers::stateprep
.. doxygennamespace:: cudaq::solvers::stateprep
:desc-only:
.. doxygennamespace:: cudaq::solvers::adapt
:desc-only:
.. doxygennamespace:: cudaq::optim
:desc-only:

Core
Core
=============

.. doxygenclass:: cudaqx::extension_point
.. doxygenclass:: cudaqx::extension_point
:members:

.. doxygenclass:: cudaqx::heterogeneous_map
.. doxygenclass:: cudaqx::heterogeneous_map
:members:

.. doxygenclass:: cudaqx::tear_down
:members:

.. doxygenclass:: cudaqx::details::tensor_impl
:members:
.. doxygenclass:: cudaqx::details::tensor_impl
:members:

.. doxygenclass:: cudaqx::tensor
:members:
.. doxygenclass:: cudaqx::graph
:members:
.. doxygenclass:: cudaqx::tensor
:members:

.. doxygenclass:: cudaqx::graph
:members:
8 changes: 7 additions & 1 deletion docs/sphinx/api/qec/cpp_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ CUDA-Q QEC C++ API
.. doxygenclass:: cudaq::qec::steane::steane
:members:

.. doxygenclass:: cudaq::qec::surface_code::stabilizer_grid
:members:

.. doxygenclass:: cudaq::qec::surface_code::surface_code
:members:

.. doxygenclass:: cudaq::qec::repetition::repetition
:members:

.. doxygenclass:: cudaq::qec::code
:members:

.. doxygenenum:: cudaq::qec::operation

.. doxygenfunction:: cudaq::qec::sample_code_capacity(const cudaqx::tensor<uint8_t> &, std::size_t, double)
.. doxygenfunction:: cudaq::qec::sample_code_capacity(const cudaqx::tensor<uint8_t> &, std::size_t, double, unsigned)
.. doxygenfunction:: cudaq::qec::sample_code_capacity(const code &, std::size_t, double)
Expand Down
274 changes: 274 additions & 0 deletions libs/qec/include/cudaq/qec/codes/surface_code.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/****************************************************************-*- C++ -*-****
* Copyright (c) 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. *
******************************************************************************/
#pragma once

#include "cudaq/qec/code.h"
#include "cudaq/qec/patch.h"

using namespace cudaqx;

namespace cudaq::qec::surface_code {

/// @brief enumerates the role of a grid site in the surface codes stabilizer
/// grid
enum surface_role { amx, amz, empty };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: is this "a measure x" and "a measure z", or something else?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a fine interpretation, although in my head I was thinking "ancilla for mx/z".


/// @brief describes the 2d coordinate on the stabilizer grid
struct vec2d {
int row;
int col;

vec2d(int row_in, int col_in);
};

vec2d operator+(const vec2d &lhs, const vec2d &rhs);
vec2d operator-(const vec2d &lhs, const vec2d &rhs);
bool operator==(const vec2d &lhs, const vec2d &rhs);
bool operator<(const vec2d &lhs, const vec2d &rhs);

/// @brief Generates and keeps track of the 2d grid of stabilizers in the
/// rotated surface code
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps consider adding link to a reference paper who's convention you're following w.r.t. the grid.

// Grid layout is arranged from left to right, top to bottom (row major storage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to double check that the docs are rendering as desired for // vs ///.

// grid_length = 4 example:
// (0,0) (0,1) (0,2) (0,3)
// (1,0) (1,1) (1,2) (1,3)
// (2,0) (2,1) (2,2) (2,3)
// (3,0) (3,1) (3,2) (3,3)
//
// Each entry on the grid can be an X stabilizer, Z stabilizer,
// or empty, as is needed on the edges.
// The grid length of 4 corresponds to a distance 3 surface code, which results
// in: e(0,0) e(0,1) Z(0,2) e(0,3) X(1,0) Z(1,1) X(1,2) e(1,3) e(2,0)
// X(2,1) Z(2,2) X(2,3) e(3,0) Z(3,1) e(3,2) e(3,3)
Comment on lines +45 to +47
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double-check formatting.

//
// This is seen through the `print_stabilizer_grid()` member function.
// To get rid of the empty sites, the `print_stabilizer_coords()` function is
// used:
// Z(0,2)
// X(1,0) Z(1,1) X(1,2)
// X(2,1) Z(2,2) X(2,3)
// Z(3,1)
//
// and to get the familiar visualization of the distance three surface code, the
// `print_stabilizer_indices` results in:
// Z0
// X0 Z1 X1
// X2 Z2 X3
// Z3
//
// The data qubits are located at the four corners of each of the weight-4
// stabilizers. They are also organized with index increasing from left to
// right, top to bottom: d0 d1 d2 d3 d4 d5 d6 d7 d8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check formatting (two spaces vs single spac)

class stabilizer_grid {
private:
/// @brief Generates this->roles
void generate_grid_roles();
/// @brief Generates {x,z}_stab_coords and indices
void generate_grid_indices();
/// @brief Generates {x,z}_stabilizers
void generate_stabilizers();

public:
/// @brief The distance of the code,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
/// @brief The distance of the code,
/// @brief The distance of the code

// determines the number of data qubits per dimension
uint32_t distance;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to initialize this for cases where the default constructor is used.

Suggested change
uint32_t distance;
uint32_t distance = 0;


/// @brief length of the stabilizer grid
// for distance = d data qubits,
// the stabilizer grid has length d+1
uint32_t grid_length;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to initialize this for cases where the default constructor is used.

Suggested change
uint32_t grid_length;
uint32_t grid_length= 0;


/// @brief flattened vector of the stabilizer grid sites roles'
// grid idx -> role
// stored in row major order
std::vector<surface_role> roles;

/// @brief x stab index -> 2d coord
std::vector<vec2d> x_stab_coords;

/// @brief z stab index -> 2d coord
std::vector<vec2d> z_stab_coords;

/// @brief 2d coord -> z stab index
std::map<vec2d, size_t> x_stab_indices;

/// @brief 2d coord -> z stab index
std::map<vec2d, size_t> z_stab_indices;

/// @brief data index -> 2d coord
// data qubits are in an offset 2D coord system from stabilizers
std::vector<vec2d> data_coords;

/// @brief 2d coord -> data index
std::map<vec2d, size_t> data_indices;

/// @brief Each element is an X stabilizer specified by the data qubits it has
/// support on
// In surface code, can have weight 2 or weight 4 stabs
// So {x,z}_stabilizer[i].size() == 2 || 4
std::vector<std::vector<size_t>> x_stabilizers;

/// @brief Each element is an Z stabilizer specified by the data qubits it has
/// support on
std::vector<std::vector<size_t>> z_stabilizers;

/// @brief Construct the grid from the code's distance
stabilizer_grid(uint32_t distance);
/// @brief Empty constructor
stabilizer_grid();

/// @brief Print a 2d grid of stabilizer roles
void print_stabilizer_grid() const;

/// @brief Print a 2d grid of stabilizer coords
void print_stabilizer_coords() const;

/// @brief Print a 2d grid of stabilizer indices
void print_stabilizer_indices() const;

/// @brief Print a 2d grid of data qubit indices
void print_data_grid() const;

/// @brief Print the coord <--> indices maps
void print_stabilizer_maps() const;

/// @brief Print the stabilizers in sparse pauli format
void print_stabilizers() const;

/// @brief Get the stabilizers as a vector of cudaq::spin_ops
std::vector<cudaq::spin_op> get_spin_op_stabilizers() const;

/// @brief Get the observables as a vector of cudaq::spin_ops
std::vector<cudaq::spin_op> get_spin_op_observables() const;
};

/// \pure_device_kernel
///
/// @brief Apply X gate to a surface_code patch
/// @param p The patch to apply the X gate to
__qpu__ void x(patch p);

/// \pure_device_kernel
///
/// @brief Apply Y gate to a surface_code patch
/// @param p The patch to apply the Y gate to
__qpu__ void y(patch p);

/// \pure_device_kernel
///
/// @brief Apply Z gate to a surface_code patch
/// @param p The patch to apply the Z gate to
__qpu__ void z(patch p);

/// \pure_device_kernel
///
/// @brief Apply Hadamard gate to a surface_code patch
/// @param p The patch to apply the Hadamard gate to
__qpu__ void h(patch p);

/// \pure_device_kernel
///
/// @brief Apply S gate to a surface_code patch
/// @param p The patch to apply the S gate to
__qpu__ void s(patch p);

/// \pure_device_kernel
///
/// @brief Apply controlled-X gate between two surface_code patches
/// @param control The control patch
/// @param target The target patch
__qpu__ void cx(patch control, patch target);

/// \pure_device_kernel
///
/// @brief Apply controlled-Y gate between two surface_code patches
/// @param control The control patch
/// @param target The target patch
__qpu__ void cy(patch control, patch target);

/// \pure_device_kernel
///
/// @brief Apply controlled-Z gate between two surface_code patches
/// @param control The control patch
/// @param target The target patch
__qpu__ void cz(patch control, patch target);

/// \pure_device_kernel
///
/// @brief Prepare a surface_code patch in the |0⟩ state
/// @param p The patch to prepare
__qpu__ void prep0(patch p);

/// \pure_device_kernel
///
/// @brief Prepare a surface_code patch in the |1⟩ state
/// @param p The patch to prepare
__qpu__ void prep1(patch p);

/// \pure_device_kernel
///
/// @brief Prepare a surface_code patch in the |+⟩ state
/// @param p The patch to prepare
__qpu__ void prepp(patch p);

/// \pure_device_kernel
///
/// @brief Prepare a surface_code patch in the |-⟩ state
/// @param p The patch to prepare
__qpu__ void prepm(patch p);

/// \pure_device_kernel
///
/// @brief Perform stabilizer measurements on a surface_code patch
/// @param p The patch to measure
/// @param x_stabilizers Indices of X stabilizers to measure
/// @param z_stabilizers Indices of Z stabilizers to measure
/// @return Vector of measurement results
__qpu__ std::vector<cudaq::measure_result>
stabilizer(patch p, const std::vector<std::size_t> &x_stabilizers,
const std::vector<std::size_t> &z_stabilizers);
Comment on lines +233 to +234
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think we supported pass-by-reference going into a __qpu__ function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected behavior of passing a ref in? This seems to work here as well as in the Steane code.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, either pass by value or pass by ref get you the same thing - a subtlety of the ASTBridge MLIR generation. No matter what the signature is, if you pass a std::vector it is modeled as a cc.stdvec which lowers to a span-like type (a pointer and a size). You are always passing by reference no matter what. Eric can elaborate more if needed.


/// @brief surface_code implementation
class surface_code : public cudaq::qec::code {
protected:
/// @brief The code distance parameter
std::size_t distance;

/// @brief Get the number of data qubits in the surface_code
/// @return Number of data qubits (distance^2 for surface_code)
std::size_t get_num_data_qubits() const override;

/// @brief Get the number of total ancilla qubits in the surface_code
/// @return Number of data qubits (distance^2 - 1 for surface_code)
std::size_t get_num_ancilla_qubits() const override;

/// @brief Get the number of X ancilla qubits in the surface_code
/// @return Number of data qubits ((distance^2 - 1)/2 for surface_code)
std::size_t get_num_ancilla_x_qubits() const override;

/// @brief Get the number of Z ancilla qubits in the surface_code
/// @return Number of data qubits ((distance^2 - 1)/2 for surface_code)
std::size_t get_num_ancilla_z_qubits() const override;

public:
/// @brief Constructor for the surface_code
surface_code(const heterogeneous_map &);
// Grid constructor would be useful

/// @brief Extension creator function for the surface_code
CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION(
surface_code, static std::unique_ptr<cudaq::qec::code> create(
const cudaqx::heterogeneous_map &options) {
return std::make_unique<surface_code>(options);
})

/// @brief Grid to keep track of topological arrangement of qubits.
stabilizer_grid grid;
};

} // namespace cudaq::qec::surface_code
3 changes: 2 additions & 1 deletion libs/qec/lib/codes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
cudaqx_add_device_code(cudaq-qec
SOURCES
steane_device.cpp
surface_code_device.cpp
repetition_device.cpp
)

target_sources(cudaq-qec PRIVATE steane.cpp repetition.cpp)
target_sources(cudaq-qec PRIVATE steane.cpp repetition.cpp surface_code.cpp)
Loading