-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an example of loading a decoder .so (#42)
Add an example of auto loading decoder plugins Signed-off-by: Melody Ren <melodyr@nvidia.com>
- Loading branch information
1 parent
15cad65
commit 4f96465
Showing
9 changed files
with
418 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/******************************************************************************* | ||
* 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. * | ||
******************************************************************************/ | ||
|
||
#ifndef PLUGIN_LOADER_H | ||
#define PLUGIN_LOADER_H | ||
|
||
#include <dlfcn.h> | ||
#include <map> | ||
#include <memory> | ||
#include <string> | ||
|
||
/// @brief Enum to define different types of plugins | ||
enum class PluginType { | ||
DECODER, // Decoder plugins | ||
CODE // QEC codes plugins | ||
// Add other plugin types here as needed | ||
}; | ||
|
||
/// @brief A struct to store plugin handle with its type | ||
struct PluginHandle { | ||
std::shared_ptr<void> handle; // Pointer to the shared library handle. This is | ||
// the result of dlopen() function. | ||
PluginType type; // Type of the plugin (e.g., decoder, code, etc) | ||
}; | ||
|
||
/// @brief Function to load plugins from a directory based on type | ||
/// @param plugin_dir The directory where the plugins are located | ||
/// @param type The type of plugins to load. Only plugins of this type will be | ||
/// loaded. | ||
void load_plugins(const std::string &plugin_dir, PluginType type); | ||
|
||
/// @brief Function to clean up loaded plugins of a specific type | ||
/// @param type The type of plugins to clean up. Only plugins of this type will | ||
/// be cleaned up. | ||
void cleanup_plugins(PluginType type); | ||
|
||
#endif // PLUGIN_LOADER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# ============================================================================ # | ||
# 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. # | ||
# ============================================================================ # | ||
|
||
cmake_minimum_required(VERSION 3.28 FATAL_ERROR) | ||
|
||
set(MODULE_NAME "cudaq-qec-example") | ||
|
||
project(${MODULE_NAME}) | ||
|
||
# Specify the source file for the plugin | ||
set(PLUGIN_SRC | ||
single_error_lut_example.cpp | ||
# single_error_lut_example2.cpp // add other decoder source files here | ||
) | ||
|
||
# Create the shared library | ||
add_library(${MODULE_NAME} SHARED ${PLUGIN_SRC}) | ||
|
||
# Set the include directories for dependencies | ||
target_include_directories(${MODULE_NAME} | ||
PUBLIC | ||
${CMAKE_SOURCE_DIR}/libs/qec/include | ||
${CMAKE_SOURCE_DIR}/libs/core/include | ||
) | ||
|
||
# Link with required libraries | ||
target_link_libraries(${MODULE_NAME} | ||
PUBLIC | ||
cudaqx-core | ||
cudaq::cudaq | ||
cudaq::cudaq-spin | ||
PRIVATE | ||
cudaq::cudaq-common | ||
cudaq-qec | ||
) | ||
|
||
set_target_properties(${MODULE_NAME} PROPERTIES | ||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/decoder-plugins | ||
) | ||
|
||
# RPATH configuration | ||
# ============================================================================== | ||
|
||
if (NOT SKBUILD) | ||
set_target_properties(${LIBRARY_NAME} PROPERTIES | ||
BUILD_RPATH "$ORIGIN" | ||
INSTALL_RPATH "$ORIGIN:$ORIGIN/.." | ||
) | ||
|
||
# Let CMake automatically add paths of linked libraries to the RPATH: | ||
set_target_properties(${LIBRARY_NAME} PROPERTIES | ||
INSTALL_RPATH_USE_LINK_PATH TRUE) | ||
else() | ||
# CUDA-Q install its libraries in site-packages/lib (or dist-packages/lib) | ||
# Thus, we need the $ORIGIN/../lib | ||
set_target_properties(${LIBRARY_NAME} PROPERTIES | ||
INSTALL_RPATH "$ORIGIN/../../lib" | ||
) | ||
endif() | ||
|
||
# Install | ||
# ============================================================================== | ||
|
||
install(TARGETS ${MODULE_NAME} | ||
COMPONENT qec-lib-plugins | ||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/decoder-plugins | ||
) |
112 changes: 112 additions & 0 deletions
112
libs/qec/lib/decoders/plugins/example/decoder_plugins_demo.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/******************************************************************************* | ||
* 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. * | ||
******************************************************************************/ | ||
|
||
// This example shows how to use decoders from decoder plugins | ||
// | ||
// Compile and run with | ||
// nvq++ --enable-mlir -lcudaq-qec decoder_plugins_demo.cpp -o | ||
// decoder_plugins_demo | ||
// ./decoder_plugins_demo | ||
|
||
#include <dlfcn.h> | ||
#include <filesystem> | ||
#include <iostream> | ||
#include <memory> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include "cudaq.h" | ||
#include "cudaq/qec/decoder.h" | ||
#include "cudaq/qec/experiments.h" | ||
|
||
int main() { | ||
auto steane = cudaq::qec::get_code("steane"); | ||
auto Hz = steane->get_parity_z(); | ||
std::vector<size_t> t_shape = Hz.shape(); | ||
|
||
std::cout << "Hz.shape():\n"; | ||
for (size_t elem : t_shape) | ||
std::cout << elem << " "; | ||
std::cout << "\n"; | ||
|
||
std::cout << "Hz:\n"; | ||
Hz.dump(); | ||
|
||
auto Lz = steane->get_observables_x(); | ||
std::cout << "Lz:\n"; | ||
Lz.dump(); | ||
|
||
double p = 0.2; | ||
size_t nShots = 5; | ||
|
||
// Check for available decoders | ||
for (auto &name : cudaq::qec::decoder::get_registered()) | ||
printf("Decoder: %s\n", name.c_str()); | ||
// create a decoder from the plugins | ||
auto lut_decoder = cudaq::qec::get_decoder("single_error_lut_example", Hz); | ||
|
||
std::cout << "nShots: " << nShots << "\n"; | ||
|
||
// May want a order-2 tensor of syndromes | ||
// access tensor by stride to write in an entire syndrome | ||
cudaqx::tensor<uint8_t> syndrome({Hz.shape()[0]}); | ||
|
||
int nErrors = 0; | ||
for (size_t shot = 0; shot < nShots; ++shot) { | ||
std::cout << "shot: " << shot << "\n"; | ||
auto shot_data = cudaq::qec::generate_random_bit_flips(Hz.shape()[1], p); | ||
std::cout << "shot data\n"; | ||
shot_data.dump(); | ||
|
||
auto observable_z_data = Lz.dot(shot_data); | ||
observable_z_data = observable_z_data % 2; | ||
std::cout << "Data Lz state:\n"; | ||
observable_z_data.dump(); | ||
|
||
auto syndrome = Hz.dot(shot_data); | ||
syndrome = syndrome % 2; | ||
std::cout << "syndrome:\n"; | ||
syndrome.dump(); | ||
|
||
auto [converged, v_result] = lut_decoder->decode(syndrome); | ||
cudaqx::tensor<uint8_t> result_tensor; | ||
// v_result is a std::vector<float_t>, of soft information. We'll convert | ||
// this to hard information and store as a tensor<uint8_t>. | ||
cudaq::qec::convert_vec_soft_to_tensor_hard(v_result, result_tensor); | ||
std::cout << "decode result:\n"; | ||
result_tensor.dump(); | ||
|
||
// check observable result | ||
auto decoded_observable_z = Lz.dot(result_tensor); | ||
std::cout << "decoded observable:\n"; | ||
decoded_observable_z.dump(); | ||
|
||
// check how many observable operators were decoded correctly | ||
// observable_z_data == decoded_observable_z This maps onto element wise | ||
// addition (mod 2) | ||
auto observable_flips = decoded_observable_z + observable_z_data; | ||
observable_flips = observable_flips % 2; | ||
std::cout << "Logical errors:\n"; | ||
observable_flips.dump(); | ||
std::cout << "\n"; | ||
|
||
// shot counts as a observable error unless all observables are correct | ||
if (observable_flips.any()) { | ||
nErrors++; | ||
} | ||
} | ||
std::cout << "Total logical errors: " << nErrors << "\n"; | ||
|
||
// Full data gen in function call | ||
auto [syn, data] = cudaq::qec::sample_code_capacity(Hz, nShots, p); | ||
std::cout << "Numerical experiment:\n"; | ||
std::cout << "Data:\n"; | ||
data.dump(); | ||
std::cout << "Syn:\n"; | ||
syn.dump(); | ||
} |
91 changes: 91 additions & 0 deletions
91
libs/qec/lib/decoders/plugins/example/single_error_lut_example.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. * | ||
******************************************************************************/ | ||
|
||
#include "cudaq/qec/decoder.h" | ||
#include <cassert> | ||
#include <map> | ||
#include <vector> | ||
|
||
namespace cudaq::qec { | ||
|
||
/// @brief This is a simple LUT (LookUp Table) decoder that demonstrates how to | ||
/// build a simple decoder that can correctly decode errors during a single bit | ||
/// flip in the block. | ||
class single_error_lut_example : public decoder { | ||
private: | ||
std::map<std::string, std::size_t> single_qubit_err_signatures; | ||
|
||
public: | ||
single_error_lut_example(const cudaqx::tensor<uint8_t> &H, | ||
const cudaqx::heterogeneous_map ¶ms) | ||
: decoder(H) { | ||
// Decoder-specific constructor arguments can be placed in `params`. | ||
|
||
// Build a lookup table for an error on each possible qubit | ||
|
||
// For each qubit with a possible error, calculate an error signature. | ||
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; | ||
// Toggle syndrome on every "1" entry in the row. | ||
// Except if there is an error on this qubit (c == qErr). | ||
for (std::size_t c = 0; c < block_size; c++) | ||
syndrome ^= (c != qErr) && H.at({r, c}); | ||
err_sig[r] = syndrome ? '1' : '0'; | ||
} | ||
// printf("Adding err_sig=%s for qErr=%lu\n", err_sig.c_str(), qErr); | ||
single_qubit_err_signatures.insert({err_sig, qErr}); | ||
} | ||
} | ||
|
||
virtual decoder_result decode(const std::vector<float_t> &syndrome) { | ||
// This is a simple decoder that simply results | ||
decoder_result result{false, std::vector<float_t>(block_size, 0.0)}; | ||
|
||
// Convert syndrome to a string | ||
std::string syndrome_str(syndrome.size(), '0'); | ||
assert(syndrome_str.length() == syndrome_size); | ||
bool anyErrors = false; | ||
for (std::size_t i = 0; i < syndrome_size; i++) { | ||
if (syndrome[i] >= 0.5) { | ||
syndrome_str[i] = '1'; | ||
anyErrors = true; | ||
} | ||
} | ||
|
||
if (!anyErrors) { | ||
result.converged = true; | ||
return result; | ||
} | ||
|
||
auto it = single_qubit_err_signatures.find(syndrome_str); | ||
if (it != single_qubit_err_signatures.end()) { | ||
assert(it->second < block_size); | ||
result.converged = true; | ||
result.result[it->second] = 1.0; | ||
} else { | ||
// Leave result.converged set to false. | ||
} | ||
|
||
return result; | ||
} | ||
|
||
virtual ~single_error_lut_example() {} | ||
|
||
CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION( | ||
single_error_lut_example, static std::unique_ptr<decoder> create( | ||
const cudaqx::tensor<uint8_t> &H, | ||
const cudaqx::heterogeneous_map ¶ms) { | ||
return std::make_unique<single_error_lut_example>(H, params); | ||
}) | ||
}; | ||
|
||
CUDAQ_REGISTER_TYPE(single_error_lut_example) | ||
|
||
} // namespace cudaq::qec |
Oops, something went wrong.