Skip to content

Commit

Permalink
Use extension point instead of extern c
Browse files Browse the repository at this point in the history
Signed-off-by: Melody Ren <melodyr@nvidia.com>
  • Loading branch information
melody-ren committed Dec 18, 2024
1 parent 1ccf018 commit ad25870
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 258 deletions.
45 changes: 28 additions & 17 deletions libs/qec/lib/decoders/plugins/example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,53 @@
# ============================================================================ #

cmake_minimum_required(VERSION 3.28 FATAL_ERROR)
project(cudaq-qec-example)

set(MODULE_NAME "cudaq-qec-example")

project(${MODULE_NAME})

# Specify the source file for the plugin
set(PLUGIN_SRC
set(PLUGIN_SRC
single_error_lut_example.cpp
# single_error_lut_example2.cpp // add other decoder source files here
plugin_exports.cpp
)

# Create the shared library
add_library(cudaq-qec-example SHARED ${PLUGIN_SRC})
add_library(${MODULE_NAME} SHARED ${PLUGIN_SRC})

# Set the include directories for dependencies
target_include_directories(cudaq-qec-example
PUBLIC
${CMAKE_SOURCE_DIR}/libs/qec/include # Path to cudaq/qec/decoder.h
${CMAKE_SOURCE_DIR}/libs/core/include # Path to cuda-qx/core/ (for tensor.h, extension points, etc)
target_include_directories(${MODULE_NAME}
PUBLIC
${CMAKE_SOURCE_DIR}/libs/qec/include
${CMAKE_SOURCE_DIR}/libs/core/include
)

# Link with required libraries (based on earlier CMake you shared)
target_link_libraries(cudaq-qec-example
PUBLIC
cudaqx-core # This could be from another library target, or pre-built if needed
cudaq::cudaq
# Link with required libraries
target_link_libraries(${MODULE_NAME}
PUBLIC
cudaqx-core
cudaq::cudaq
cudaq::cudaq-spin
PRIVATE
PRIVATE
cudaq::cudaq-common
cudaq-qec
)

# Place the shared library in the specified output directory
set_target_properties(cudaq-qec-example PROPERTIES
set_target_properties(${MODULE_NAME} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/plugins
)

# Set RPATH so that the shared libraries are properly resolved
set_target_properties(cudaq-qec-example PROPERTIES
# Set RPATH
set_target_properties(${MODULE_NAME} PROPERTIES
BUILD_RPATH "$ORIGIN"
INSTALL_RPATH "$ORIGIN:$ORIGIN/../lib"
)

# Install
# ==============================================================================

install(TARGETS ${MODULE_NAME}
COMPONENT qec-lib-plugins
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
18 changes: 6 additions & 12 deletions libs/qec/lib/decoders/plugins/example/decoder_plugins_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
#include <vector>

#include "cudaq.h"
#include "decoder_plugins_loader.h" // required header to load the plugins
#include "cudaq/qec/decoder.h"
#include "cudaq/qec/experiments.h"
#include "decoder_plugins_loader.h" // required header to load the plugins

int main() {
auto steane = cudaq::qec::get_code("steane");
auto Hz = steane->get_parity_z();
cudaqx::heterogeneous_map params;
std::vector<size_t> t_shape = Hz.shape();

std::cout << "Hz.shape():\n";
Expand All @@ -45,17 +46,10 @@ int main() {
double p = 0.2;
size_t nShots = 5;

DecoderFactory factory;
factory.load_plugins(
"."); // provide the abs path to the directory containing the plugins
auto plugin_names = factory.get_all_plugin_names();
std::cout << "Decoder plugins contain the following decoders:\n";
for (auto &name : plugin_names) {
std::cout << "-> " << name << "\n";
}
cudaqx::heterogeneous_map params;
std::unique_ptr<cudaq::qec::decoder> lut_decoder =
factory.create_decoder("create_single_error_lut_example", Hz, params);
// load the decoder plugins
load_plugins(".");
// create a decoder from the plugins
auto lut_decoder = cudaq::qec::get_decoder("single_error_lut_example", Hz);

std::cout << "nShots: " << nShots << "\n";

Expand Down
145 changes: 19 additions & 126 deletions libs/qec/lib/decoders/plugins/example/decoder_plugins_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,137 +12,30 @@
#include <dlfcn.h>
#include <filesystem>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include "cudaq.h"
#include "cudaq/qec/decoder.h"

namespace fs = std::filesystem;

// Type definition for the creator function
using CreatorFunction = std::unique_ptr<cudaq::qec::decoder> (*)(
const cudaqx::tensor<uint8_t> &, const cudaqx::heterogeneous_map &);

/**
* @brief Struct to manage loaded decoders.
* This struct holds the name, handle, and creator function of a decoder plugin.
*/
struct DecoderPlugin {
std::string name;
void *handle;
CreatorFunction creator;
};

/**
* @brief Factory class to manage and create decoder plugins.
* This class handles the loading of decoder plugins from shared libraries,
* and provides functionality to create decoders and retrieve plugin names.
*/
class DecoderFactory {
private:
std::vector<DecoderPlugin> plugins;

public:
~DecoderFactory() {
for (auto &plugin : plugins) {
if (plugin.handle) {
dlclose(plugin.handle);
/// @brief Load all shared libraries (.so) from the specified directory.
/// @param directory The directory where the shared libraries are located.
bool load_plugins(const std::string &directory) {
bool success = true;
for (const auto &entry : fs::directory_iterator(directory)) {
if (entry.path().extension() == ".so") {
std::cout << "Loading plugin: " << entry.path() << std::endl;

// Open the shared library
void *handle = dlopen(entry.path().c_str(), RTLD_NOW);
if (!handle) {
std::cerr << "Failed to load plugin: " << entry.path()
<< "Error: " << dlerror() << std::endl;
success = false;
} else {
// Close the shared library
dlclose(handle);
}
}
}

/**
* @brief Load plugins from a specified directory.
* This function scans the given directory for shared library files (.so)
* and loads them as decoder plugins.
* @param directory The directory to scan for plugins.
*/
void load_plugins(const std::string &directory) {
for (const auto &entry : fs::directory_iterator(directory)) {
// scan the directory and load all .so files
if (entry.path().extension() == ".so") {
std::cout << "Loading plugin: " << entry.path() << "\n";

void *handle = dlopen(entry.path().c_str(), RTLD_LAZY);
if (!handle) {
std::cerr << "Failed to load " << entry.path() << ": " << dlerror()
<< "\n";
continue;
}

dlerror(); // Clear errors
using SymbolListFunction = const char *(*)();
SymbolListFunction get_symbols = reinterpret_cast<SymbolListFunction>(
dlsym(handle, "get_exported_symbols"));
const char *dlsym_error = dlerror();
if (dlsym_error) {
std::cerr << "dlsym failed to get 'get_exported_symbols': "
<< dlsym_error << "\n";
dlclose(handle);
continue;
}

// Extract the list of symbols and split by comma if there are multiple
std::string symbols = get_symbols();
std::vector<std::string> symbol_list;
std::stringstream ss(symbols);
std::string item;
while (std::getline(ss, item, ',')) {
symbol_list.push_back(item);
}

for (const auto &symbol : symbol_list) {
std::cout << "Looking for function: " << symbol << "\n";
CreatorFunction creator =
reinterpret_cast<CreatorFunction>(dlsym(handle, symbol.c_str()));
const char *dlsym_error = dlerror();
if (!dlsym_error) {
plugins.push_back({symbol, handle, creator});
std::cout << "Successfully loaded symbol: " << symbol << "\n";
} else {
std::cerr << "dlsym failed to get symbol' " << symbol
<< "': " << dlsym_error << "\n";
}
}
}
}
}

/**
* @brief Create a decoder using a specified plugin.
* This function creates a decoder by invoking the creator function of the
* specified plugin.
* @param plugin_name The name of the plugin to use.
* @param H The tensor to pass to the creator function.
* @param params The heterogeneous map to pass to the creator function.
* @return A unique pointer to the created decoder.
* @throws std::runtime_error if the plugin is not found.
*/
std::unique_ptr<cudaq::qec::decoder>
create_decoder(const std::string &plugin_name,
const cudaqx::tensor<uint8_t> &H,
const cudaqx::heterogeneous_map &params) {
for (auto &plugin : plugins) {
if (plugin.name == plugin_name) {
return plugin.creator(H, params);
}
}
throw std::runtime_error("Decoder " + plugin_name + " not found.");
}

/**
* @brief Returns a vector of string of available decoders
* @return A unique pointer to the created decoder.
*/
std::vector<std::string> get_all_plugin_names() {
std::vector<std::string> plugin_names;
for (auto &plugin : plugins) {
plugin_names.push_back(plugin.name);
}
return plugin_names;
}
};
return success;
}

#endif // DECODER_PLUGINS_LOADER_H
25 changes: 0 additions & 25 deletions libs/qec/lib/decoders/plugins/example/plugin_exports.cpp

This file was deleted.

60 changes: 0 additions & 60 deletions libs/qec/lib/decoders/plugins/example/plugin_exports.h

This file was deleted.

25 changes: 7 additions & 18 deletions libs/qec/lib/decoders/plugins/example/single_error_lut_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

#include "plugin_exports.h" // required for exporting the decoder as a shared library
#include "cudaq/qec/decoder.h"
#include <cassert>
#include <map>
Expand Down Expand Up @@ -79,24 +78,14 @@ class single_error_lut_example : public decoder {

virtual ~single_error_lut_example() {}

// The following is not needed when compiling the decoder code as a plugin
// 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 &params) {
// return std::make_unique<single_error_lut_example>(H, params);
// })
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 &params) {
return std::make_unique<single_error_lut_example>(H, params);
})
};

// CUDAQ_REGISTER_TYPE(single_error_lut_example)
CUDAQ_REGISTER_TYPE(single_error_lut_example)

} // namespace cudaq::qec

// The following is required when compiling the decoder code as a plugin
extern "C" std::unique_ptr<cudaq::qec::decoder>
create_single_error_lut_example(const cudaqx::tensor<uint8_t> &H,
const cudaqx::heterogeneous_map &params) {
return std::make_unique<cudaq::qec::single_error_lut_example>(H, params);
}

REGISTER_DECODER(create_single_error_lut_example);

0 comments on commit ad25870

Please sign in to comment.