From 81047e05d994b6b3d998cb4921847c55cde74704 Mon Sep 17 00:00:00 2001 From: Hiroshi Horii Date: Mon, 30 Oct 2023 13:43:54 +0900 Subject: [PATCH] add command-line parameter binding --- include/API/api.h | 9 ++ lib/API/CMakeLists.txt | 2 +- lib/API/api.cpp | 86 ----------- lib/API/bind.cpp | 264 ++++++++++++++++++++++++++++++++++ tools/CMakeLists.txt | 1 + tools/qss-bind/CMakeLists.txt | 16 +++ tools/qss-bind/qss-bind.cpp | 5 + 7 files changed, 296 insertions(+), 87 deletions(-) create mode 100644 lib/API/bind.cpp create mode 100644 tools/qss-bind/CMakeLists.txt create mode 100644 tools/qss-bind/qss-bind.cpp diff --git a/include/API/api.h b/include/API/api.h index c306e1162..8cf21d893 100644 --- a/include/API/api.h +++ b/include/API/api.h @@ -35,6 +35,15 @@ namespace qssc { int compile(int argc, char const **argv, std::string *outputString, std::optional diagnosticCb); +/// @brief Call the parameter binder +/// @param argc the number of argument strings +/// @param argv array of argument strings +/// @param outputString an optional buffer for the compilation result +/// @param diagnosticCb an optional callback that will receive emitted +/// diagnostics +int bind(int argc, char const **argv, std::string *outputString, + std::optional diagnosticCb); + /// @brief Call the parameter binder /// @param target name of the target to employ /// @param moduleInputPath path of the module to use as input diff --git a/lib/API/CMakeLists.txt b/lib/API/CMakeLists.txt index b4f3aeb24..b0e685034 100644 --- a/lib/API/CMakeLists.txt +++ b/lib/API/CMakeLists.txt @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -add_library(QSSCAPI api.cpp) +add_library(QSSCAPI api.cpp bind.cpp) add_library(QSSCError errors.cpp) diff --git a/lib/API/api.cpp b/lib/API/api.cpp index ea9672969..055afab32 100644 --- a/lib/API/api.cpp +++ b/lib/API/api.cpp @@ -799,89 +799,3 @@ int qssc::compile(int argc, char const **argv, std::string *outputString, return 0; } - -class MapAngleArgumentSource : public qssc::arguments::ArgumentSource { - -public: - MapAngleArgumentSource( - const std::unordered_map ¶meterMap) - : parameterMap(parameterMap) {} - - qssc::arguments::ArgumentType - getArgumentValue(llvm::StringRef name) const override { - std::string name_{name}; - auto pos = parameterMap.find(name_); - - if (pos == parameterMap.end()) - return llvm::None; - return pos->second; - } - -private: - const std::unordered_map ¶meterMap; -}; - -llvm::Error -_bindArguments(std::string_view target, std::string_view configPath, - std::string_view moduleInput, std::string_view payloadOutputPath, - std::unordered_map const &arguments, - bool treatWarningsAsErrors, bool enableInMemoryInput, - std::string *inMemoryOutput, - const std::optional &onDiagnostic) { - - MLIRContext context{}; - - qssc::hal::registry::TargetSystemInfo &targetInfo = - *qssc::hal::registry::TargetSystemRegistry::lookupPluginInfo(target) - .getValueOr(qssc::hal::registry::TargetSystemRegistry:: - nullTargetSystemInfo()); - - auto created = targetInfo.createTarget(&context, llvm::StringRef(configPath)); - if (auto err = created.takeError()) { - return llvm::joinErrors( - llvm::createStringError(llvm::inconvertibleErrorCode(), - "Unable to create target!"), - std::move(err)); - } - - auto targetInst = targetInfo.getTarget(&context); - if (auto err = targetInst.takeError()) { - return llvm::joinErrors( - llvm::createStringError(llvm::inconvertibleErrorCode(), - "Unable to load target!"), - std::move(err)); - } - - MapAngleArgumentSource source(arguments); - - auto factory = targetInst.get()->getBindArgumentsImplementationFactory(); - if ((!factory.hasValue()) || (factory.getValue() == nullptr)) { - return qssc::emitDiagnostic( - onDiagnostic, qssc::Severity::Error, - qssc::ErrorCategory::QSSLinkerNotImplemented, - "Unable to load bind arguments implementation for target."); - } - qssc::arguments::BindArgumentsImplementationFactory &factoryRef = - *factory.getValue(); - return qssc::arguments::bindArguments( - moduleInput, payloadOutputPath, source, treatWarningsAsErrors, - enableInMemoryInput, inMemoryOutput, factoryRef, onDiagnostic); -} - -int qssc::bindArguments( - std::string_view target, std::string_view configPath, - std::string_view moduleInput, std::string_view payloadOutputPath, - std::unordered_map const &arguments, - bool treatWarningsAsErrors, bool enableInMemoryInput, - std::string *inMemoryOutput, - const std::optional &onDiagnostic) { - - if (auto err = - _bindArguments(target, configPath, moduleInput, payloadOutputPath, - arguments, treatWarningsAsErrors, enableInMemoryInput, - inMemoryOutput, onDiagnostic)) { - llvm::logAllUnhandledErrors(std::move(err), llvm::errs()); - return 1; - } - return 0; -} diff --git a/lib/API/bind.cpp b/lib/API/bind.cpp new file mode 100644 index 000000000..16202e04a --- /dev/null +++ b/lib/API/bind.cpp @@ -0,0 +1,264 @@ +//===- bind.cpp -----------------------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +#include +#include + +#include + +#include "API/api.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" + +#include "Arguments/Arguments.h" + +#include "HAL/PassRegistration.h" +#include "HAL/TargetSystem.h" +#include "HAL/TargetSystemRegistry.h" + +using namespace mlir; + +using json = nlohmann::json; + +static llvm::cl::OptionCategory qsscBindCat_(" Options for parameter binding", + "Options that binds parameters"); + +namespace { +enum GenAction { None, GenQEM, GenQEQEM }; +} // anonymous namespace + +static llvm::cl::opt emitGenAction( + "emit", llvm::cl::init(GenAction::None), + llvm::cl::desc("Select method used to generate input"), + // llvm::cl::values(clEnumValN(DumpAST, "ast", "output the AST dump")), + // llvm::cl::values(clEnumValN(DumpASTPretty, "ast-pretty", + // "pretty print the AST")), + // llvm::cl::values(clEnumValN(DumpMLIR, "mlir", "output the MLIR dump")), + // llvm::cl::values(clEnumValN(DumpWaveMem, "wavemem", + // "output the waveform memory")), + llvm::cl::values(clEnumValN(GenAction::GenQEM, "qem", + "a quantum executable module (qem) " + "for execution on hardware")), + llvm::cl::values( + clEnumValN(GenAction::GenQEQEM, "qe-qem", + "a target-specific quantum executable module (qeqem) " + "for execution on hardware"))); + +static llvm::cl::opt + inputQemFile(llvm::cl::Positional, llvm::cl::desc("[Input qem filename]"), + llvm::cl::init("-"), llvm::cl::cat(qsscBindCat_)); + +static llvm::cl::opt + outputQemFile("o", llvm::cl::desc("Output qem filename"), + llvm::cl::value_desc("filename"), llvm::cl::init("-"), + llvm::cl::cat(qsscBindCat_)); + +static llvm::cl::opt + paramFile("p", llvm::cl::desc("parameter json filename"), + llvm::cl::value_desc("filename"), llvm::cl::init("-"), + llvm::cl::cat(qsscBindCat_)); + +static llvm::cl::opt + targetStr("target", + llvm::cl::desc("Target architecture used to generate inputfile."), + llvm::cl::value_desc("targetName"), llvm::cl::init("-"), + llvm::cl::cat(qsscBindCat_)); + +static llvm::cl::opt + configPathStr("config", + llvm::cl::desc("configuration path for target."), + llvm::cl::value_desc("configPath"), llvm::cl::init("-"), + llvm::cl::cat(qsscBindCat_)); + +llvm::Error loadJsonFile(json &d, const std::string &filename) { + std::ifstream f(filename); + if (!f.is_open()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to load json file: " + filename); + } else { + f >> d; + return llvm::Error::success(); + } +} + +class MapAngleArgumentSource : public qssc::arguments::ArgumentSource { + +public: + MapAngleArgumentSource( + const std::unordered_map ¶meterMap) + : parameterMap(parameterMap) {} + + qssc::arguments::ArgumentType + getArgumentValue(llvm::StringRef name) const override { + std::string name_{name}; + auto pos = parameterMap.find(name_); + + if (pos == parameterMap.end()) + return llvm::None; + return pos->second; + } + +private: + const std::unordered_map ¶meterMap; +}; + +llvm::Error +_bindArguments(std::string_view target, std::string_view configPath, + std::string_view moduleInput, std::string_view payloadOutputPath, + std::unordered_map const &arguments, + bool treatWarningsAsErrors, bool enableInMemoryInput, + std::string *inMemoryOutput, + const std::optional &onDiagnostic) { + + MLIRContext context{}; + + qssc::hal::registry::TargetSystemInfo &targetInfo = + *qssc::hal::registry::TargetSystemRegistry::lookupPluginInfo(target) + .getValueOr(qssc::hal::registry::TargetSystemRegistry:: + nullTargetSystemInfo()); + + auto created = targetInfo.createTarget(&context, llvm::StringRef(configPath)); + if (auto err = created.takeError()) { + return llvm::joinErrors( + llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to create target!"), + std::move(err)); + } + + auto targetInst = targetInfo.getTarget(&context); + if (auto err = targetInst.takeError()) { + return llvm::joinErrors( + llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to load target!"), + std::move(err)); + } + + MapAngleArgumentSource source(arguments); + + auto factory = targetInst.get()->getBindArgumentsImplementationFactory(); + if ((!factory.hasValue()) || (factory.getValue() == nullptr)) { + return qssc::emitDiagnostic( + onDiagnostic, qssc::Severity::Error, + qssc::ErrorCategory::QSSLinkerNotImplemented, + "Unable to load bind arguments implementation for target."); + } + qssc::arguments::BindArgumentsImplementationFactory &factoryRef = + *factory.getValue(); + return qssc::arguments::bindArguments( + moduleInput, payloadOutputPath, source, treatWarningsAsErrors, + enableInMemoryInput, inMemoryOutput, factoryRef, onDiagnostic); +} + +int qssc::bindArguments( + std::string_view target, std::string_view configPath, + std::string_view moduleInput, std::string_view payloadOutputPath, + std::unordered_map const &arguments, + bool treatWarningsAsErrors, bool enableInMemoryInput, + std::string *inMemoryOutput, + const std::optional &onDiagnostic) { + + if (auto err = + _bindArguments(target, configPath, moduleInput, payloadOutputPath, + arguments, treatWarningsAsErrors, enableInMemoryInput, + inMemoryOutput, onDiagnostic)) { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs()); + return 1; + } + return 0; +} + +llvm::Error bind_(int argc, char const **argv, std::string *outputString, + std::optional diagnosticCb) { + llvm::cl::ParseCommandLineOptions( + argc, argv, "Quantum System Software (QSS) Parameter Binding\n"); + + if (paramFile == "-") { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to detect a parameter file! Please specify the " + "a parameter file with -p"); + } + + json inputArgs; + if (loadJsonFile(inputArgs, paramFile)) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to load a json file for arguments: file=" + paramFile); + } + + auto argsMap = std::unordered_map(); + for (auto &[paramName, paramValue] : inputArgs.items()) + argsMap[paramName] = (double)paramValue; + + if (emitGenAction == GenAction::None) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to detect an emit option! Please specify the " + "an emmit option with --emit=qem or --emit=qe-qem"); + } + + if (outputQemFile == "-") { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to detect an output file! Please specify the " + "a parameter file with -o"); + } + + if (inputQemFile == "-") { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to detect an input file! Please specify the input qem file"); + } + + if (targetStr == "-") { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to detect target used to generate " + "input file! Please specify the " + "target name with --target"); + } + + if (configPathStr == "-") { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Unable to detect configuration for the target! Please specify the " + "configuration file path with --config"); + } + + bool treatWarningsAsErrors = false; + bool enableInMemoryInput = false; + + return _bindArguments(targetStr, configPathStr, inputQemFile, outputQemFile, + argsMap, treatWarningsAsErrors, enableInMemoryInput, + nullptr, std::nullopt); +} + +int qssc::bind(int argc, char const **argv, std::string *outputString, + std::optional diagnosticCb) { + + if (auto err = bind_(argc, argv, outputString, std::move(diagnosticCb))) { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "Error: "); + return 1; + } + + return 0; +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 5e5e86fc5..029617f49 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(qss-compiler) add_subdirectory(qss-opt) +add_subdirectory(qss-bind) diff --git a/tools/qss-bind/CMakeLists.txt b/tools/qss-bind/CMakeLists.txt new file mode 100644 index 000000000..2a52b650a --- /dev/null +++ b/tools/qss-bind/CMakeLists.txt @@ -0,0 +1,16 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +add_llvm_executable(qss-bind qss-bind.cpp) +llvm_update_compile_flags(qss-bind) +target_link_libraries(qss-bind PRIVATE QSSCLib) +mlir_check_all_link_libraries(qss-bind) diff --git a/tools/qss-bind/qss-bind.cpp b/tools/qss-bind/qss-bind.cpp new file mode 100644 index 000000000..788a8be92 --- /dev/null +++ b/tools/qss-bind/qss-bind.cpp @@ -0,0 +1,5 @@ +#include "API/api.h" + +int main(int argc, const char **argv) { + return qssc::bind(argc, argv, nullptr, {}); +} \ No newline at end of file