Skip to content
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
33 changes: 33 additions & 0 deletions docs/sphinx/api/qec/cpp_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ Decoder Interfaces
.. doxygenstruct:: cudaq::qec::decoder_result
:members:

Sparse Parity-Check Matrices
============================

``sparse_binary_matrix`` stores a binary matrix in compressed sparse column
(CSC) or compressed sparse row (CSR) layout without storing values for its
nonzero entries. Input indices are preserved as supplied. Use
:cpp:func:`cudaq::qec::sparse_binary_matrix::canonicalize` when duplicate
indices should be combined over GF(2) and each compressed row or column should
be sorted. Canonicalization preserves the matrix layout.

The matrix uses ``std::uint32_t`` indices, so each dimension and the number of
stored entries must fit in that type. Dense ``cudaqx::tensor<std::uint8_t>``
PCMs remain accepted by decoder entry points through an implicit conversion.

.. doxygenenum:: cudaq::qec::sparse_binary_matrix_layout

.. doxygenclass:: cudaq::qec::sparse_binary_matrix
:members:

Built-in Decoders
=================

Expand Down Expand Up @@ -78,19 +97,33 @@ Real-Time Decoding
Parity Check Matrix Utilities
=============================

The overloads that accept ``sparse_binary_matrix`` operate without
materializing the full input as a dense tensor. Use
:cpp:func:`cudaq::qec::generate_random_pcm_sparse` when a generated PCM would
be impractical to allocate densely. The dense generator remains available and
rejects dimensions whose products overflow ``std::size_t``.

.. doxygenfunction:: cudaq::qec::dense_to_sparse(const cudaqx::tensor<uint8_t> &)
.. doxygenfunction:: cudaq::qec::generate_random_pcm(std::size_t, std::size_t, std::size_t, int, std::mt19937_64 &&);
.. doxygenfunction:: cudaq::qec::generate_random_pcm_sparse(std::size_t, std::size_t, std::size_t, int, std::mt19937_64 &&);
.. doxygenfunction:: cudaq::qec::generate_timelike_sparse_detector_matrix(std::uint32_t num_syndromes_per_round, std::uint32_t num_rounds, bool include_first_round = false)
.. doxygenfunction:: cudaq::qec::generate_timelike_sparse_detector_matrix(std::uint32_t num_syndromes_per_round, std::uint32_t num_rounds, std::vector<std::int64_t> first_round_matrix)
.. doxygenfunction:: cudaq::qec::get_pcm_for_rounds(const cudaqx::tensor<uint8_t> &, std::uint32_t, std::uint32_t, std::uint32_t, bool, bool);
.. doxygenfunction:: cudaq::qec::get_pcm_for_rounds(const sparse_binary_matrix &, std::uint32_t, std::uint32_t, std::uint32_t, bool, bool, bool);
.. doxygenfunction:: cudaq::qec::get_sorted_pcm_column_indices(const std::vector<std::vector<std::uint32_t>> &, std::uint32_t);
.. doxygenfunction:: cudaq::qec::get_sorted_pcm_column_indices(const cudaqx::tensor<uint8_t> &, std::uint32_t);
.. doxygenfunction:: cudaq::qec::pcm_extend_to_n_rounds(const cudaqx::tensor<uint8_t> &, std::size_t, std::uint32_t);
.. doxygenfunction:: cudaq::qec::pcm_from_sparse_string(const std::string &, std::size_t, std::size_t)
.. doxygenfunction:: cudaq::qec::pcm_from_sparse_vec(const std::vector<std::int64_t>& sparse_vec, std::size_t num_rows, std::size_t num_cols)
.. doxygenfunction:: cudaq::qec::pcm_is_sorted(const cudaqx::tensor<uint8_t> &, std::uint32_t);
.. doxygenfunction:: cudaq::qec::pcm_to_sparse_string(const cudaqx::tensor<uint8_t> &)
.. doxygenfunction:: cudaq::qec::pcm_to_sparse_string(const sparse_binary_matrix &)
.. doxygenfunction:: cudaq::qec::pcm_to_sparse_vec(const cudaqx::tensor<uint8_t>& pcm)
.. doxygenfunction:: cudaq::qec::pcm_to_sparse_vec(const sparse_binary_matrix &)
.. doxygenfunction:: cudaq::qec::reorder_pcm_columns(const cudaqx::tensor<uint8_t> &, const std::vector<std::uint32_t> &, uint32_t, uint32_t);
.. doxygenfunction:: cudaq::qec::reorder_pcm_columns(const sparse_binary_matrix &, const std::vector<std::uint32_t> &, uint32_t, uint32_t);
.. doxygenfunction:: cudaq::qec::shuffle_pcm_columns(const cudaqx::tensor<uint8_t> &, std::mt19937_64 &&);
.. doxygenfunction:: cudaq::qec::shuffle_pcm_columns(const sparse_binary_matrix &, std::mt19937_64 &&);
.. doxygenfunction:: cudaq::qec::simplify_pcm(const cudaqx::tensor<uint8_t> &, const std::vector<double> &, std::uint32_t);
.. doxygenfunction:: cudaq::qec::sort_pcm_columns(const cudaqx::tensor<uint8_t> &, std::uint32_t);

Expand Down
30 changes: 17 additions & 13 deletions docs/sphinx/components/qec/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ The decoder base class defines the core interface for syndrome decoding:
protected:
std::size_t block_size; // For [n,k] code, this is n
std::size_t syndrome_size; // For [n,k] code, this is n-k
tensor<uint8_t> H; // Parity check matrix
sparse_binary_matrix H; // Parity check matrix

public:
struct decoder_result {
Expand All @@ -541,7 +541,7 @@ The decoder base class defines the core interface for syndrome decoding:

Key Components:

* **Parity Check Matrix**: Defines the code structure via :code:`H`
* **Parity Check Matrix**: Defines the code structure via the sparse :code:`H` member
* **Block Size**: Number of physical qubits in the code
* **Syndrome Size**: Number of stabilizer measurements
* **Decoder Result**: Contains convergence status and error probabilities
Expand All @@ -561,7 +561,7 @@ To implement a new decoder:
// Decoder-specific members

public:
my_decoder(const tensor<uint8_t>& H,
my_decoder(const qec::sparse_binary_matrix& H,
const heterogeneous_map& params)
: decoder(H) {
// Initialize decoder
Expand All @@ -580,14 +580,19 @@ To implement a new decoder:
CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION(
my_decoder,
static std::unique_ptr<decoder> create(
const tensor<uint8_t>& H,
const qec::decoder_init& init,
const heterogeneous_map& params) {
return std::make_unique<my_decoder>(H, params);
return qec::make_pcm_decoder<my_decoder>(init, params);
}
)

CUDAQ_EXT_PT_REGISTER_TYPE(my_decoder)

The :code:`make_pcm_decoder` helper dispatches :code:`decoder_init`. It
passes a stored sparse PCM directly to the decoder constructor; when the
variant contains Stim DEM text, it parses the DEM and constructs the sparse
detector matrix before invoking the same constructor.

Example: Lookup Table Decoder
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand All @@ -600,18 +605,17 @@ Here's a simple lookup table decoder for the Steane code:
std::map<std::string, std::size_t> single_qubit_err_signatures;

public:
single_error_lut(const tensor<uint8_t>& H,
single_error_lut(const qec::sparse_binary_matrix& H,
const heterogeneous_map& params)
: decoder(H) {
// Build lookup table for single-qubit errors
// Canonicalize before using each sparse column as an error
// signature so duplicate row indices cancel over GF(2).
auto H_e2d = H.canonicalize().to_nested_csc();

for (std::size_t qErr = 0; qErr < block_size; qErr++) {
std::string err_sig(syndrome_size, '0');
for (std::size_t r = 0; r < syndrome_size; r++) {
bool syndrome = 0;
for (std::size_t c = 0; c < block_size; c++)
syndrome ^= (c != qErr) && H.at({r, c});
err_sig[r] = syndrome ? '1' : '0';
}
for (std::uint32_t row : H_e2d[qErr])
err_sig[row] = '1';
single_qubit_err_signatures.insert({err_sig, qErr});
}
}
Expand Down
7 changes: 7 additions & 0 deletions docs/sphinx/examples_rst/qec/decoders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ that would result from those errors.
``H``. This is the preferred form for large PCMs because no dense
``rows x cols`` allocation is made — the matrix is normalised to CSR
internally. Dense NumPy ``uint8`` arrays remain supported.
The PCM utilities :func:`cudaq_qec.reorder_pcm_columns`,
:func:`cudaq_qec.shuffle_pcm_columns`, and
:func:`cudaq_qec.pcm_to_sparse_vec` also accept SciPy sparse matrices
without creating a dense ``cudaqx::tensor``. Reordering and shuffling a
sparse input returns a ``scipy.sparse.csc_matrix``; a dense input continues
to return a NumPy array.

``scipy`` is an optional dependency; if it is not installed, pass a dense
NumPy array instead.

Expand Down
Loading